diff --git a/.circleci/Dockerfile.sentinel b/.circleci/Dockerfile.sentinel deleted file mode 100644 index 8c40d8c4..00000000 --- a/.circleci/Dockerfile.sentinel +++ /dev/null @@ -1,31 +0,0 @@ -# Docker image to build and run the sentinel tests - -# This is required because CircleCI will run the sentinel nodes on a remote docker instance, -# and these containers cannot be accessed directly over the host container's network. Instead -# we build an image on the fly and run it in the same network as the sentinel containers. - -# note: the top level target directory must be removed prior to running this - -FROM cimg/rust:1.72.1 -USER circleci - -ARG REDIS_VERSION -ARG REDIS_USERNAME -ARG REDIS_PASSWORD -ARG REDIS_SENTINEL_PASSWORD -ARG FRED_CI_NEXTEST - -# try to make the new container look like the host -WORKDIR /home/circleci/project -COPY --chown=1001:1001 . /home/circleci/project - -RUN sudo apt-get update && sudo apt-get install -y libssl-dev dnsutils -RUN rm -rf ~/.cargo/git/* && rm -rf ~/.cargo/registry/cache/* -RUN echo "REDIS_VERSION=$REDIS_VERSION" -# RUN .circleci/install_redis_cli.sh - -# For debugging -RUN cargo --version && rustc --version -# RUN cargo install cargo-nextest - -CMD tests/runners/sentinel-features.sh \ No newline at end of file diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 28d31d8c..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,299 +0,0 @@ -version: 2.1 - -# CI cred rotation counter = 3 -# have to use the machine executor to mount volumes -commands: - restore-cargo-deps-cache: - steps: - - restore_cache: - name: Restore cargo dependencies cache - key: cargo-cache-stable-{{ .Environment.CACHE_VERSION }}-{{ arch }} - save-cargo-deps-cache: - steps: - - save_cache: - name: Save cargo dependencies cache - key: cargo-cache-stable-{{ .Environment.CACHE_VERSION }}-{{ arch }}-{{ epoch }} - paths: - - "/home/circleci/.cargo/registry" - - "/home/circleci/project/target" - setup-empty-registry: - steps: - - run: - name: Create empty cargo registry - command: mkdir -p /home/circleci/.cargo/registry - clear_git_cache: - steps: - - run: - name: Clear the cargo git cache - command: rm -rf ~/.cargo/git/* && rm -rf ~/.cargo/registry/cache/* - build_docs: - steps: - - run: - name: Install nightly - command: rustup install nightly - - run: - name: Build documentation - command: tests/doc.sh - test_mocks: - steps: - - run: - name: Run mock tests - command: cargo test --lib --features "mocks i-keys" --no-default-features - test_default_features: - steps: - - checkout - - restore-cargo-deps-cache - - run: - name: Run tests with default features - command: source tests/environ && tests/runners/default-features.sh - - save-cargo-deps-cache - test_all_features: - steps: - - checkout - - restore-cargo-deps-cache - - run: - name: Run tests with all features - command: source tests/environ && tests/runners/all-features.sh - - save-cargo-deps-cache - test_redis_stack: - steps: - - checkout - - restore-cargo-deps-cache - - run: - name: Run tests with redis-stack features - command: source tests/environ && tests/runners/redis-stack.sh - - save-cargo-deps-cache - test_no_features: - steps: - - checkout - - restore-cargo-deps-cache - - run: - name: Run tests with no features - command: source tests/environ && tests/runners/no-features.sh - - save-cargo-deps-cache - test_sentinel: - steps: - - checkout - - restore-cargo-deps-cache - - run: - name: Install and run sentinel tests - command: source tests/environ && tests/runners/sentinel-features.sh - - save-cargo-deps-cache - test_tls_cluster: - steps: - - checkout - - restore-cargo-deps-cache - - run: - name: Run cluster tests with native-tls features - command: source tests/environ && tests/scripts/tls-creds.sh && tests/runners/cluster-native-tls.sh - - save-cargo-deps-cache - test_rustls_cluster: - steps: - - checkout - - restore-cargo-deps-cache - - run: - name: Run cluster tests with rustls features - command: source tests/environ && tests/scripts/tls-creds.sh && tests/runners/cluster-rustls.sh - - save-cargo-deps-cache - test_rustls_cluster_ring: - steps: - - checkout - - restore-cargo-deps-cache - - run: - name: Run cluster tests with rustls/ring features - command: source tests/environ && tests/scripts/tls-creds.sh && tests/runners/cluster-rustls-ring.sh - - save-cargo-deps-cache - test_default_nil_types_features: - steps: - - checkout - - restore-cargo-deps-cache - - run: - name: Run tests with default-nil-types features - command: source tests/environ && tests/runners/default-nil-types.sh - - save-cargo-deps-cache - test_unix_socket: - steps: - - checkout - - restore-cargo-deps-cache - - run: - name: Run tests with unix socket features - command: source tests/environ && tests/runners/unix-socket.sh - - save-cargo-deps-cache - test_valkey: - steps: - - checkout - - restore-cargo-deps-cache - - run: - name: Run tests against local valkey deployments - command: source tests/environ && tests/runners/valkey-all-features.sh - - save-cargo-deps-cache - -jobs: - test-default-nil-types-7_2: - machine: - image: ubuntu-2204:2022.10.2 - docker_layer_caching: true - resource_class: medium - environment: - REDIS_VERSION: 7.2.4 - steps: - - test_default_nil_types_features - test-default-7_2: - machine: - image: ubuntu-2204:2022.10.2 - docker_layer_caching: true - resource_class: medium - environment: - REDIS_VERSION: 7.2.4 - steps: - - test_default_features - test-unix-socket-7_2: - machine: - image: ubuntu-2204:2022.10.2 - docker_layer_caching: true - resource_class: medium - environment: - REDIS_VERSION: 7.2.4 - steps: - - test_unix_socket - test-redis-stack-7_2: - machine: - image: ubuntu-2204:2022.10.2 - docker_layer_caching: true - resource_class: medium - environment: - REDIS_VERSION: 7.2.4 - steps: - - test_redis_stack - test-no-features-7_2: - machine: - image: ubuntu-2204:2022.10.2 - docker_layer_caching: true - resource_class: medium - environment: - REDIS_VERSION: 7.2.4 - steps: - - test_no_features - test-all-features-7_2: - machine: - image: ubuntu-2204:2022.10.2 - docker_layer_caching: true - resource_class: medium - environment: - REDIS_VERSION: 7.2.4 - steps: - - test_all_features - test-cluster-tls-features-7_2: - machine: - image: ubuntu-2204:2022.10.2 - docker_layer_caching: true - resource_class: medium - environment: - REDIS_VERSION: 7.2.4 - FRED_CI_TLS: true - steps: - - test_tls_cluster - test-cluster-rustls-features-7_2: - machine: - image: ubuntu-2204:2022.10.2 - docker_layer_caching: true - resource_class: medium - environment: - REDIS_VERSION: 7.2.4 - FRED_CI_TLS: true - steps: - - test_rustls_cluster - test-cluster-rustls-ring-features-7_2: - machine: - image: ubuntu-2204:2022.10.2 - docker_layer_caching: true - resource_class: medium - environment: - REDIS_VERSION: 7.2.4 - FRED_CI_TLS: true - steps: - - test_rustls_cluster_ring - test-sentinel-7_2: - machine: - image: ubuntu-2204:2022.10.2 - docker_layer_caching: true - resource_class: medium - environment: - REDIS_VERSION: 7.2.4 - steps: - - test_sentinel - test-valkey-7_2: - machine: - image: ubuntu-2204:2022.10.2 - docker_layer_caching: true - resource_class: medium - environment: - REDIS_VERSION: 7.2.4 - VALKEY_VERSION: 7.2.5 - steps: - - test_valkey - test-misc: - docker: - - image: cimg/rust:1.78 - environment: - CARGO_NET_GIT_FETCH_WITH_CLI: true - steps: - - checkout - - build_docs - - test_mocks - - run: - name: Check partial tracing - command: cargo check --features partial-tracing - check-all-interface-features: - docker: - - image: cimg/rust:1.78 - environment: - CARGO_NET_GIT_FETCH_WITH_CLI: true - steps: - - checkout - - run: - name: Check all features - command: tests/scripts/check_features.sh - clippy-lint: - docker: - - image: cimg/rust:1.78 - environment: - CARGO_NET_GIT_FETCH_WITH_CLI: true - steps: - - checkout - - run: - name: Clippy - command: cargo clippy --all-features --lib -p fred -- -Dwarnings - cargo-fmt: - docker: - - image: cimg/rust:1.78 - environment: - CARGO_NET_GIT_FETCH_WITH_CLI: true - steps: - - checkout - - run: - name: Install nightly toolchain - command: rustup toolchain install nightly - - run: - name: Cargo fmt - command: cargo +nightly fmt --all --check - -workflows: - version: 2 - build: - jobs: - - test-default-7_2 - - test-all-features-7_2 - - test-no-features-7_2 - - test-default-nil-types-7_2 - - test-redis-stack-7_2 - - test-sentinel-7_2 - - test-unix-socket-7_2 - - test-valkey-7_2 - - test-misc - - test-cluster-tls-features-7_2 - - test-cluster-rustls-features-7_2 - - test-cluster-rustls-ring-features-7_2 - - clippy-lint - - cargo-fmt - - check-all-interface-features diff --git a/.circleci/install_redis_cli.sh b/.circleci/install_redis_cli.sh deleted file mode 100755 index c4dd6fb1..00000000 --- a/.circleci/install_redis_cli.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -function check_root_dir { - if [ ! -d "./tests/tmp" ]; then - echo "Must be in application root for redis installation scripts to work." - exit 1 - fi -} - -declare -a arr=("REDIS_VERSION") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -ROOT=$PWD -[[ -z "${JOBS}" ]] && PARALLEL_JOBS='2' || PARALLEL_JOBS="${JOBS}" - -# Returns 0 if not installed, 1 otherwise. -function check_redis { - if [ -d "$ROOT/tests/tmp/redis_$REDIS_VERSION/redis-$REDIS_VERSION" ]; then - echo "Skipping redis install." - return 1 - else - echo "Redis install not found." - return 0 - fi -} - -function install_redis { - echo "Installing redis..." - pushd $ROOT > /dev/null - rm -rf tests/tmp/redis_cluster_$REDIS_VERSION - cd tests/tmp - curl -O http://download.redis.io/releases/redis-$REDIS_VERSION.tar.gz - mkdir redis_$REDIS_VERSION - tar xf redis-$REDIS_VERSION.tar.gz -C redis_$REDIS_VERSION - rm redis-$REDIS_VERSION.tar.gz - cd redis_$REDIS_VERSION/redis-$REDIS_VERSION - make -j"${PARALLEL_JOBS}" - popd > /dev/null -} - -check_root_dir -check_redis -if [[ "$?" -eq 0 ]]; then - install_redis -fi - -echo "Finished installing centralized redis server." \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 7900d421..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -name: Bug report -about: File a bug report -title: "[Bug]" -labels: bug -assignees: aembke - ---- - -Fred version - X -Redis version - X -Platform - linux|mac|windows -Deployment type - cluster|sentinel|centralized - -**Describe the bug** - -**To Reproduce** -Steps to reproduce the behavior: - -1. X -2. X - -**Logs** -(If possible set `RUST_LOG=fred=trace` and run with `--features debug-ids`) - diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 79630d7c..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: Feature Request -about: Discuss a feature request -title: "[Feature]" -labels: enhancement -assignees: aembke - ---- - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 99a18274..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "rust-analyzer.cargo.features": "all", - "rust-analyzer.check.command": "clippy", - "rust-analyzer.rustfmt.overrideCommand": [ - "rustup", - "run", - "nightly", - "rustfmt" - ] -} diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 9c47e0f8..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,375 +0,0 @@ -## 9.1.2 - -* Fix `FT.AGGREGATE` command with `SORTBY` operation - -## 9.1.1 - -* Fix tracing span names and missing fields - -## 9.1.0 - -* Add [RediSearch](https://github.com/RediSearch/RediSearch) interface. -* Adapt testing and CI processes to test Redis and Valkey -* Add `FromIterator` impl to `RedisMap` -* Add `ExclusivePool` client -* Support `redis+unix` config URLs for Unix domain sockets -* Add `PEXPIRE` and `PEXPIREAT` -* Replace `trust-dns-resolver` with `hickory-resolver` - -## 9.0.3 - -* Fix `bytes_utils` min version -* Fix rustls reexports with `enable-rustls-ring` - -## 9.0.2 - -* Add `enable-rustls-ring` feature flag - -## 9.0.1 - -* Fix `partial-tracing` imports - -## 9.0.0 - -This version should reduce compilation times for most use cases. - -* **RPITIT / AFIT** -* Set MSRV to 1.75 -* Upgrade `rustls` to 0.23 -* Upgrade `redis-protocol` to 5.0.0 -* Split public interfaces with new feature flags. -* Add `ClusterDiscoveryPolicy` configuration options. -* Add `SORT` and `SORT_RO` -* Add `cluster_hash` policy to `Options` -* Change tracing span names to - follow [OpenTelemetry naming conventions](https://opentelemetry.io/docs/specs/semconv/general/attribute-naming/). - -### Upgrading from 8.x - -* Callers that use certain managed services or Kubernetes-based deployment models should review the - new `ClusterDiscoveryPolicy`. -* Double-check the new feature flags. The `codec` feature was also moved - to [redis-protocol](https://github.com/aembke/redis-protocol.rs). -* Rustls - Check the new [aws-lc-rs](https://aws.github.io/aws-lc-rs/requirements/index.html) requirements or switch - back to `rustls/ring`. -* Note the new [tracing span names](src/trace/README.md). - -## 8.0.6 - -* Add `TransactionInterface` to `RedisPool` - -## 8.0.5 - -* Add conversion from `HashMap` to `MultipleOrderedPairs`. -* Remove `lazy_static` -* Update examples - -## 8.0.4 - -* Fix tracing span annotations. - -## 8.0.3 - -* Box large futures to reduce stack usage. - -## 8.0.2 - -* Fix cluster replica failover at high concurrency. -* Fix potential race condition initializing the mocking layer. - -## 8.0.1 - -* Add a shorthand `init` interface. -* Fix cluster replica failover with unresponsive connections. -* Fix RESP3 connection init when used without a password. - -## 8.0.0 - -* Remove the `globals` interface. -* Support unix domain sockets. -* Add a Redis TimeSeries interface. -* Improve unresponsive connection checks. -* Move several feature flags to configuration options. -* Add a [benchmarking](bin/benchmark) tool. -* Update to Rustls 0.22. -* Add several new connection configuration options. -* Add a `fail_fast` flag to commands. -* Switch to [crossbeam types](https://crates.io/crates/crossbeam-queue) internally. - -### Upgrading from 7.x - -Using `..Default::default()` with the various configuration structs can avoid most of the breaking changes here. - -Notable changes: - -* Several configuration options were moved from `globals` to `ConnectionConfig` and `PerformanceConfig`. -* Several feature flags were moved to configuration fields, including: - * `ignore-auth-error` - * `pool-prefer-active` - * `reconnect-on-auth-error` - * `auto-client-setname` -* The `on_message` and `on_keyspace_event` functions were renamed and moved to the `EventInterface`. They now use the - same naming conventions as the other event streams. - -## 7.1.2 - -* Fix intermittent cluster routing errors - -## 7.1.1 - -* Fix cluster failover in transactions - -## 7.1.0 - -* Fix panic when reconnect delay jitter is 0 -* Support percent encoding in URLs -* Support tuples for `RedisValue` and `MultipleKeys` -* Make `CLIENT ID` checks optional -* Update dependencies - -## 7.0.0 - -* Added a new client [builder](src/types/builder.rs) and configuration interface. -* Reworked or removed the majority of the `globals` interface. -* -* Support multiple IP addresses in the `Resolve` interface. -* Add `with_options` command configuration interface. -* Replaced the `no-client-setname` feature flag with `auto-client-setname`. -* Add an interface to configure TCP socket options. -* Removed the automatic `serde_json::Value` -> `RedisValue` type conversion logic. - * This unintentionally introduced some ambiguity on certain interfaces. - * The `RedisValue` -> `serde_json::Value` type conversion logic was not changed. -* Reworked the majority of the `RedisPool` interface. -* Moved and refactored the `on_*` functions into a new `EventInterface`. -* Fixed bugs with the `Replica` routing implementation. -* Fixed bugs related to parsing single-element arrays. -* Changed several `FromRedis` type conversion rules. See below or the `FromRedis` docs for more information. -* Add a [RedisJSON](https://github.com/RedisJSON/RedisJSON/) interface. -* Add a RESP2 and RESP3 codec interface. - -### Upgrading from 6.x - -Notable interface changes: - -* `ArcStr` has been replaced with `bytes_utils::Str`. -* Timeout arguments or fields now all use `std::time::Duration`. -* Many of the old global or performance config values can now be set on individual commands via the `with_options` - interface. -* The `RedisPool` interface now directly implements `ClientLike` rather than relying on `Deref` shenanigans. -* The `on_*` event functions were moved and renamed. Reconnection events now include the associated `Server`. -* The `tls_server_name` field on `Server` is now properly gated by the TLS feature flags. -* Mocks are now optional even when the feature flag is enabled. - -Notable implementation Changes: - -* `Pipeline` and `Transaction` structs can now be reused. Calling `exec`, `all`, `last`, or `try_all` no longer drains - the inner command buffer. -* Many of the default timeout values have been lowered significantly, often from 60 sec to 10 sec. -* In earlier versions the `FromRedis` trait implemented a few inconsistent or ambiguous type conversions policies. - * Most of these were consolidated under the `default-nil-types` feature flag. - * It is recommended that callers review the updated `FromRedis` docs or see the unit tests - in [responses](src/modules/response.rs). -* The `connect` function can now be called more than once to force reset all client state. - -## 6.3.2 - -* Fix a bug with connection errors unexpectedly ending the connection task. - -## 6.3.1 - -* Update various dependencies -* Move `pretty-env-logger` to `dev-dependencies` -* Update rustfmt.toml - -## 6.3.0 - -* Fix cluster replica discovery with Elasticache -* Fix cluster replica `READONLY` usage -* Fix compilation error with `full-tracing` -* Support `Vec<(T1, T2, ...)>` with `FromRedis` - -## 6.2.1 - -* Fix cluster failover with paused nodes - -## 6.2.0 - -* Add `Pipeline::try_all` -* Add missing pubsub commands -* Improve docs, examples - -## 6.1.0 - -* Add a [client tracking](https://redis.io/docs/manual/client-side-caching/) interface. -* Add a global config value for broadcast channel capacity. -* Add an interface to interact with individual cluster nodes. -* Fix missing `impl StreamInterface for Transaction` -* Add all `RedisClient` command traits to `SubscriberClient` - -## 6.0.0 - -* Refactored the connection and protocol layer. -* Add a manual `Pipeline` interface for pipelining commands within a task. -* Add a `Replica` client for interacting with replica nodes. -* Rework the `Transaction` interface to buffer commands in memory before EXEC/DISCARD. -* Rework the cluster discovery and failover implementation. -* Rework the MOVED/ASK implementation to more quickly and reliably follow cluster redirects. -* Rework the sentinel interface to more reliably handle failover scenarios. -* Fix several bugs related to detecting closed connections. -* Support the `functions` interface. -* Add `Script`, `Library`, and `Function` structs. -* Add `Message` and `MessageKind` pubsub structs. -* Add a DNS configuration interface. -* Rework the `native-tls` interface to support fully customizable TLS configurations. -* Add `rustls` support. - * Note: both TLS feature flags can be used at the same time. -* Add a mocking layer interface. - -### Updating from 5.x - -Potentially breaking changes in 6.x: - -* Switched from `(String, u16)` tuples to the `Server` struct for all server identifiers. -* New TLS feature flags: `enable-rustls` and `enable-native-tls`. - * `vendored-tls` is now `vendored-openssl` -* New TLS configuration process: see the [example](examples/tls.rs). -* New [transaction](examples/transactions.rs) interface. - * `Transaction` commands are now buffered in memory before calling `exec()` or `discard()`. -* New backpressure configuration options, most notably the `Drain` policy. This is now the default. -* Changed the type and fields on `BackpressurePolicy::Sleep`. -* New [custom command interface](examples/custom.rs) for managing cluster hash slots. -* Removed or renamed some fields on `RedisConfig`. -* Changed the pubsub receiver interface to use `Message` instead of `(String, RedisValue)` tuples. -* Changed the `on_*` family of functions to return - a [BroadcastReceiver](https://docs.rs/tokio/latest/tokio/sync/broadcast/struct.Receiver.html). -* The `FromRedis` trait converts `RedisValue::Null` to `"nil"` with `String` and `Str`. - -## 5.2.0 - -* Reduce number of `tokio` features -* Use 6379 as default cluster port in `from_url` -* Fix RESP3 auth error (https://github.com/aembke/fred.rs/issues/54) - -## 5.2.0 - -* Reduce number of `tokio` features -* Use 6379 as default cluster port in `from_url` -* Fix RESP3 auth error (https://github.com/aembke/fred.rs/issues/54) - -## 5.2.0 - -* Reduce number of `tokio` features -* Use 6379 as default cluster port in `from_url` -* Fix RESP3 auth error (https://github.com/aembke/fred.rs/issues/54) - -## 5.1.0 - -* Update `redis-protocol` and `nom` -* Add `no-client-setname` feature flag - -## 5.0.0 - -* Bug fixes -* Support URL parsing into a `RedisConfig` -* Update `bzpopmin` and `bzpopmax` return type -* Remove unimplemented `mocks` feature - -## 5.0.0-beta.1 - -* Rewrite the [protocol parser](https://github.com/aembke/redis-protocol.rs), so it can decode frames without moving or - copying the underlying bytes -* Change most command implementations to avoid unnecessary allocations when using static str slices -* Rewrite the public interface to use different traits for different parts of the redis interface -* Relax some restrictions on certain commands being used in a transaction -* Implement the Streams interface (XADD, XREAD, etc.) -* RESP3 support -* Move most perf configuration options from `globals` to client-specific config structs -* Add backpressure configuration options to the client config struct -* Fix bugs that can occur when using non-UTF8 byte arrays as keys -* Add the `serde-json` feature -* Handle more complicated failure modes with Redis clusters -* Add a more robust and specialized pubsub subscriber client -* Ergonomics improvements on the public interfaces -* Improve docs -* More tests - -## 4.3.2 - -* Fix https://github.com/aembke/fred.rs/issues/27 -* Fix https://github.com/aembke/fred.rs/issues/26 - -## 4.3.1 - -* Fix authentication bug with `sentinel-auth` tests -* Update tests and CI config for `sentinel-auth` feature -* Add more testing scripts, update docs -* Switch to CircleCI - -## 4.3.0 - -* Add `sentinel-auth` feature - -## 4.2.3 - -* Add `NotFound` error kind variant -* Use `NotFound` errors when casting `nil` server responses to non-nullable types - -## 4.2.2 - -* Remove some unnecessary async locks -* Fix client pool `wait_for_connect` implementation - -## 4.2.1 - -* Fix https://github.com/aembke/fred.rs/issues/11 - -## 4.2.0 - -* Support Sentinel clients -* Fix broken doc links - -## 4.1.0 - -* Support Redis Sentinel -* Sentinel tests -* Move metrics behind compiler flag - -## 4.0.0 - -* Add generic response interface. -* Add tests - -## 3.0.0 - -See below. - -## 3.0.0-beta.4 - -* Add support for the `MONITOR` command. - -## 3.0.0-beta.3 - -* Redo cluster state change implementation to diff `CLUSTER NODES` changes -* MOVED/ASK errors no longer initiate reconnection logic -* Fix chaos monkey tests - -## 3.0.0-beta.2 - -* Extend and refactor RedisConfig options -* Change RedisKey to work with bytes, not str -* Support unblocking clients with a control connection -* First draft of chaos monkey tests -* Custom reconnect errors feature - -## 3.0.0-beta.1 - -* Rewrite to use async/await -* Add Lua support -* Add transaction support -* Add hyperloglog, geo, acl, memory, slowlog, and cluster command support -* Add tests -* Add [pipeline_test](bin/pipeline_test) application - -## < 3.0.0 - -See the old repository at [azuqua/fred.rs](https://github.com/azuqua/fred.rs). \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 91961a19..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,193 +0,0 @@ -# Contributing - -This document gives some background on how the library is structured and how to contribute. - -## General - -* Run rustfmt and clippy before submitting any changes. - * Formatting should adhere to [rustfmt.toml](./rustfmt.toml), i.e. by running `cargo +nightly fmt --all` - * VS Code users should use the checked-in settings in the [.vscode](./.vscode) directory -* Please use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary). - -## File Structure - -The code has the following structure: - -* The [commands](src/commands) folder contains the public interface and private implementation for each of the Redis - commands, organized by category. This is roughly the same categorization used by - the [public docs](https://redis.io/commands/). Each of these public command category interfaces are exposed as a trait - with default implementations for each command. -* The [clients](src/clients) folder contains public client structs that implement and/or override the traits - from [the command category traits folder](src/commands/impls). The [interfaces](src/interfaces.rs) file contains the - shared traits required by most of the command category traits, such as `ClientLike`. -* The [monitor](src/monitor) folder contains the implementation of the `MONITOR` command and the parser for the response - stream. -* The [protocol](src/protocol) folder contains the implementation of the base `Connection` struct and the logic for - splitting a connection to interact with reader and writer halves in separate tasks. - The [TLS interface](src/protocol/tls.rs) is also implemented here. -* The [router](src/router) folder contains the logic that implements the sentinel and cluster interfaces. Clients - interact with this struct via a message passing interface. The interface exposed by the `Router` attempts to hide all - the complexity associated with sentinel or clustered deployments. -* The [trace](src/trace) folder contains the tracing implementation. Span IDs are manually tracked on each command as - they move across tasks. -* The [types](src/types) folder contains the type definitions used by the public interface. The Redis interface is - relatively loosely typed but Rust can support more strongly typed interfaces. The types in this module aim to support - an optional but more strongly typed interface for callers. -* The [modules](src/modules) folder contains smaller helper interfaces such as a - lazy [Backchannel](src/modules/backchannel.rs) connection interface and - the [response type conversion logic](src/modules/response.rs). - -## Examples - -### Add A New Command - -This example shows how to add `MGET` to the commands. - -1. Add the new variant to the `RedisCommandKind` enum, if needed. - - ```rust - pub enum RedisCommandKind { - // ... - Mget, - // ... - } - - impl RedisCommandKind { - // .. - - pub fn to_str_debug(&self) -> &str { - match *self { - // .. - RedisCommandKind::Mget => "MGET", - // .. - } - } - - // .. - - pub fn cmd_str(&self) -> Str { - match *self { - // .. - RedisCommandKind::Mget => "MGET" - // .. - } - } - - // .. - } - ``` - -2. Create the private function implementing the command in [src/commands/impls/keys.rs](src/commands/impls/keys.rs). - - ```rust - pub async fn mget(client: &C, keys: MultipleKeys) -> Result { - // maybe do some kind of validation - utils::check_empty_keys(&keys)?; - - let frame = utils::request_response(client, move || { - // time spent here will show up in traces in the `prepare_command` span - Ok((RedisCommandKind::Mget, keys.into_values())) - }) - .await?; - - protocol_utils::frame_to_results(frame) - } - ``` - - Or use one of the shorthand helper functions or macros. - - ```rust - pub async fn mget(client: &C, keys: MultipleKeys) -> Result { - utils::check_empty_keys(&keys)?; - args_values_cmd(client, keys.into_values()).await - } - ``` - -3. Create the public function in the [src/commands/interfaces/keys.rs](src/commands/interfaces/keys.rs) file. - - ```rust - - // ... - - pub trait KeysInterface: ClientLike { - - // ... - - /// Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the special value nil is returned. - /// - /// - fn mget(&self, keys: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::keys::mget(self, keys).await?.convert() - } - } - - // ... - } - ``` - -4. Implement the interface on the client structs, if needed. - - In the [RedisClient](src/clients/redis.rs) file. - - ```rust - impl KeysInterface for RedisClient {} - ``` - - In the [transaction](src/clients/transaction.rs) file. - - ```rust - impl KeysInterface for Transaction {} - ``` - -## Adding Tests - -Integration tests are in the [tests/integration](tests/integration) folder organized by category. See the -tests [README](tests/README.md) for more information. - -Using `MGET` as an example again: - -1. Write tests in the [keys](tests/integration/keys/mod.rs) file. - - ```rust - pub async fn should_mget_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected: Vec<(&str, RedisValue)> = vec![("a{1}", 1.into()), ("b{1}", 2.into()), ("c{1}", 3.into())]; - for (key, value) in expected.iter() { - let _: () = client.set(key, value.clone(), None, None, false).await?; - } - let values: Vec = client.mget(vec!["a{1}", "b{1}", "c{1}"]).await?; - assert_eq!(values, vec![1, 2, 3]); - - Ok(()) - } - ``` - -2. Call the tests from the [centralized server tests](tests/integration/centralized.rs). - - ```rust - mod keys { - // .. - centralized_test!(keys, should_mget_values); - } - - ``` - -3. Call the tests from the [cluster server tests](tests/integration/clustered.rs). - - ```rust - mod keys { - // .. - cluster_test!(keys, should_mget_values); - } - ``` - -These macros will generate test wrapper functions to call your test 8 times based on the following options: - -* Clustered vs centralized deployments -* Pipelined vs non-pipelined clients -* RESP2 vs RESP3 protocol modes diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index ec806794..00000000 --- a/Cargo.toml +++ /dev/null @@ -1,261 +0,0 @@ -[package] -authors = ["Alec Embke "] -categories = ["asynchronous", "database", "web-programming"] -description = "An async client for Redis and Valkey." -edition = "2021" -exclude = ["tests", ".circleci", "bin", ".github", "docs"] -homepage = "https://github.com/aembke/fred.rs" -keywords = ["redis", "valkey", "cluster", "sentinel"] -license = "MIT" -name = "fred" -readme = "README.md" -repository = "https://github.com/aembke/fred.rs" -rust-version = "1.75" -version = "9.2.0" - -[package.metadata.docs.rs] -# do not show the glommio version of the docs -features = [ - "i-all", - "i-redis-stack", - "transactions", - "blocking-encoding", - "dns", - "metrics", - "mocks", - "monnitor", - "replicas", - "sentinel-auth", - "sentinel-client", - "serde-json", - "subscriber-client", - "unix-sockets", - "enable-rustls", - "enable-native-tls", - "full-tracing" -] -rustdoc-args = ["--cfg", "docsrs"] - -[lib] -doc = true -name = "fred" -test = true - -[features] -default = ["transactions", "i-std"] - -blocking-encoding = ["tokio/rt-multi-thread"] -custom-reconnect-errors = [] -default-nil-types = [] -dns = ["hickory-resolver"] -metrics = [] -mocks = [] -monitor = ["nom"] -replicas = [] -sentinel-auth = [] -sentinel-client = [] -serde-json = ["serde_json"] -subscriber-client = ["i-pubsub"] -transactions = [] -trust-dns-resolver = ["dep:trust-dns-resolver"] -unix-sockets = [] - -# Enable experimental support for the Glommio runtime. -glommio = ["dep:glommio", "futures-io", "pin-project", "rm-send-macros/enabled", "oneshot", "futures-lite"] - -# Enables rustls with the rustls/aws_lc_rs crypto backend -enable-rustls = [ - "rustls", - "tokio-rustls", - "rustls-native-certs", - "rustls/std", - "tokio-rustls/logging", - "tokio-rustls/tls12", - "tokio-rustls/aws_lc_rs", -] - -# Enables rustls with the rustls/ring backend -enable-rustls-ring = [ - "rustls", - "tokio-rustls", - "rustls-native-certs", - "rustls/std", - "tokio-rustls/logging", - "tokio-rustls/tls12", - "tokio-rustls/ring", -] - -# Enables native tls -enable-native-tls = ["native-tls", "tokio-native-tls"] - -# Enables native tls with vendored openssl -vendored-openssl = ["enable-native-tls", "native-tls/vendored"] - -# Standard Redis Interfaces -i-acl = [] -i-all = [ - "i-acl", - "i-client", - "i-cluster", - "i-config", - "i-geo", - "i-hashes", - "i-hyperloglog", - "i-keys", - "i-lists", - "i-scripts", - "i-memory", - "i-pubsub", - "i-server", - "i-streams", - "i-tracking", - "i-sorted-sets", - "i-slowlog", - "i-sets", -] -i-client = [] -i-cluster = [] -i-config = [] -i-geo = ["i-sorted-sets"] -i-hashes = [] -i-hyperloglog = [] -i-keys = [] -i-lists = [] -i-memory = [] -i-pubsub = [] -i-scripts = [] -i-server = [] -i-sets = [] -i-slowlog = [] -i-sorted-sets = [] -i-std = [ - "i-hashes", - "i-keys", - "i-lists", - "i-sets", - "i-streams", - "i-pubsub", - "i-sorted-sets", - "i-server", -] -i-streams = [] -i-tracking = ["i-client", "i-pubsub"] - -# Redis Stack Interfaces -i-redis-json = ["serde-json"] -i-redis-stack = ["i-redis-json", "i-time-series", "i-redisearch"] -i-redisearch = ["i-sorted-sets", "i-geo", "i-hashes"] -i-time-series = [] - -# Full and partial tracing -full-tracing = ["partial-tracing"] -partial-tracing = ["tracing", "tracing-futures"] - -# Debugging Features -debug-ids = [] -network-logs = [] - -[dependencies] -arc-swap = "1.7" -async-trait = { version = "0.1" } -bytes = "1.6" -bytes-utils = "0.1.3" -crossbeam-queue = "0.3" -float-cmp = "0.9" -futures = { version = "0.3", features = ["std"] } -log = "0.4" -native-tls = { version = "0.2", optional = true } -nom = { version = "7.1", optional = true } -parking_lot = "0.12" -rand = "0.8" -redis-protocol = { version = "5.0.1", features = ["resp2", "resp3", "bytes"] } -rustls = { version = "0.23", optional = true, default-features = false } -rustls-native-certs = { version = "0.7", optional = true } -semver = "1.0" -serde_json = { version = "1", optional = true } -sha-1 = { version = "0.10", optional = true } -socket2 = "0.5" -tokio = { version = "1.34", features = [ - "net", - "sync", - "rt", - "rt-multi-thread", - "macros", -] } -tokio-native-tls = { version = "0.3", optional = true } -tokio-rustls = { version = "0.26", optional = true, default-features = false } -tokio-stream = "0.1" -tokio-util = { version = "0.7", features = ["codec"] } -tracing = { version = "0.1", optional = true } -tracing-futures = { version = "0.2", optional = true } -trust-dns-resolver = { version = "0.23", optional = true, features = ["tokio"] } -hickory-resolver = { version = "0.24.1", optional = true, features = ["tokio"] } -url = "2.4" -urlencoding = "2.1" -glommio = { version = "0.9.0", optional = true } -futures-io = { version = "0.3", optional = true } -pin-project = { version = "1.1.5", optional = true } -oneshot = { version = "0.1.8", optional = true, features = ["async"] } -futures-lite = { version = "2.3", optional = true } -rm-send-macros = { git = "https://github.com/aembke/rm-send-macros" } - -[dev-dependencies] -axum = { version = "0.7", features = ["macros"] } -base64 = "0.22.0" -maplit = "1.0" -pretty_env_logger = "0.5" -serde = { version = "1.0", features = ["derive"] } -subprocess = "0.2" -tokio-stream = { version = "0.1", features = ["sync"] } - -[[example]] -name = "misc" -required-features = ["i-all"] - -[[example]] -name = "scan" -required-features = ["i-all"] - -[[example]] -name = "monitor" -required-features = ["monitor"] - -[[example]] -name = "pubsub" -required-features = ["subscriber-client"] - -[[example]] -name = "axum" -required-features = ["subscriber-client"] - -[[example]] -name = "serde_json" -required-features = ["serde-json"] - -[[example]] -name = "redis_json" -required-features = ["i-redis-json"] - -[[example]] -name = "replicas" -required-features = ["i-std", "i-cluster", "replicas"] - -[[example]] -name = "dns" -required-features = ["dns"] - -[[example]] -name = "client_tracking" -required-features = ["i-tracking", "i-std"] - -[[example]] -name = "lua" -required-features = ["sha-1", "i-scripts"] - -[[example]] -name = "events" -required-features = ["tokio-stream/sync", "i-std"] - -[[example]] -name = "transactions" -required-features = ["transactions", "i-std"] diff --git a/LICENSE-APACHE b/LICENSE-APACHE deleted file mode 100644 index 408f6d20..00000000 --- a/LICENSE-APACHE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2024 Alec Embke - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - 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. \ No newline at end of file diff --git a/LICENSE-MIT b/LICENSE-MIT deleted file mode 100644 index f3ac9bbe..00000000 --- a/LICENSE-MIT +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2024 Alec Embke - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index f41d8897..00000000 --- a/README.md +++ /dev/null @@ -1,123 +0,0 @@ -Fred -==== - -[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) -[![CircleCI](https://circleci.com/gh/aembke/fred.rs/tree/main.svg?style=svg)](https://circleci.com/gh/aembke/fred.rs/tree/main) -[![Crates.io](https://img.shields.io/crates/v/fred.svg)](https://crates.io/crates/fred) -[![API docs](https://docs.rs/fred/badge.svg)](https://docs.rs/fred) - -An async client for Redis and Valkey - -## Example - -```rust -use fred::prelude::*; - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let client = RedisClient::default(); - client.init().await?; - - // convert responses to many common Rust types - let foo: Option = client.get("foo").await?; - assert!(foo.is_none()); - - client.set("foo", "bar", None, None, false).await?; - // or use turbofish to declare response types - println!("Foo: {:?}", client.get::("foo").await?); - - client.quit().await?; - Ok(()) -} -``` - -See the [examples](https://github.com/aembke/fred.rs/tree/main/examples) for more. - -## Features - -* RESP2 and RESP3 protocol modes. -* Clustered, centralized, and sentinel server deployments. -* TLS via `native-tls` or `rustls`. -* Unix sockets. -* Automatic reconnection interfaces. -* Publish-Subscribe and keyspace events interfaces. -* A round-robin client pooling interface. -* A round-robin replica routing interface. -* Built-in mocking interfaces. -* Lua [scripts](https://redis.io/docs/interact/programmability/eval-intro/) - or [functions](https://redis.io/docs/interact/programmability/functions-intro/). -* [Transactions](https://redis.io/docs/interact/transactions/) -* [Pipelining](https://redis.io/topics/pipelining) -* [Client Tracking](https://redis.io/docs/manual/client-side-caching/) -* [Automatic pipelining](bin/benchmark/README.md) -* [Zero-copy frame parsing](https://github.com/aembke/redis-protocol.rs) -* [Tracing](https://github.com/tokio-rs/tracing) - -See the build features for more information. - -## Client Features - -| Name | Default | Description | -|---------------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `transactions` | x | Enable a [Transaction](https://redis.io/docs/interact/transactions/) interface. | -| `enable-native-tls` | | Enable TLS support via [native-tls](https://crates.io/crates/native-tls). | -| `enable-rustls` | | Enable TLS support via [rustls](https://crates.io/crates/rustls) with the default crypto backend features. | -| `enable-rustls-ring` | | Enable TLS support via [rustls](https://crates.io/crates/rustls) and the ring crypto backend. | -| `vendored-openssl` | | Enable the `native-tls/vendored` feature. | -| `metrics` | | Enable the metrics interface to track overall latency, network latency, and request/response sizes. | -| `full-tracing` | | Enable full [tracing](./src/trace/README.md) support. This can emit a lot of data. | -| `partial-tracing` | | Enable partial [tracing](./src/trace/README.md) support, only emitting traces for top level commands and network latency. | -| `blocking-encoding` | | Use a blocking task for encoding or decoding frames. This can be useful for clients that send or receive large payloads, but requires a multi-thread Tokio runtime. | -| `custom-reconnect-errors` | | Enable an interface for callers to customize the types of errors that should automatically trigger reconnection logic. | -| `monitor` | | Enable an interface for running the `MONITOR` command. | -| `sentinel-client` | | Enable an interface for communicating directly with Sentinel nodes. This is not necessary to use normal Redis clients behind a sentinel layer. | -| `sentinel-auth` | | Enable an interface for using different authentication credentials to sentinel nodes. | -| `subscriber-client` | | Enable a subscriber client interface that manages channel subscription state for callers. | -| `serde-json` | | Enable an interface to automatically convert Redis types to JSON via `serde-json`. | -| `mocks` | | Enable a mocking layer interface that can be used to intercept and process commands in tests. | -| `dns` | | Enable an interface that allows callers to override the DNS lookup logic. | -| `replicas` | | Enable an interface that routes commands to replica nodes. | -| `default-nil-types` | | Enable a looser parsing interface for `nil` values. | -| `sha-1` | | Enable an interface for hashing Lua scripts. | -| `unix-sockets` | | Enable Unix socket support. | -| `glommio` | | Enable experimental [Glommio](https://github.com/DataDog/glommio) support. See [the Runtime docs](./src/glommio/README.md) for more information. | - -## Interface Features - -The command interfaces have many functions and compile times can add up quickly. Interface features -begin with `i-` and control which public interfaces are built. - -| Name | Default | Description | -|-----------------|---------|---------------------------------------------------------------------------------------------------------------------------| -| `i-all` | | Enable the interfaces included with a basic Redis or Valkey installation. This does not include `i-redis-stack` features. | -| `i-std` | x | Enable the common data structure interfaces (lists, sets, streams, keys, etc). | -| `i-acl` | | Enable the ACL command interface. | -| `i-client` | | Enable the CLIENT command interface. | -| `i-cluster` | | Enable the CLUSTER command interface. | -| `i-config` | | Enable the CONFIG command interface. | -| `i-geo` | | Enable the GEO command interface. | -| `i-hashes` | | Enable the hashes (HGET, etc) command interface. | -| `i-hyperloglog` | | Enable the hyperloglog command interface. | -| `i-keys` | | Enable the main keys (GET, SET, etc) command interface. | -| `i-lists` | | Enable the lists (LPUSH, etc) command interface. | -| `i-scripts` | | Enable the scripting command interfaces. | -| `i-memory` | | Enable the MEMORY command interfaces. | -| `i-pubsub` | | Enable the publish-subscribe command interfaces. | -| `i-server` | | Enable the server control (SHUTDOWN, BGSAVE, etc) interfaces. | -| `i-sets` | | Enable the sets (SADD, etc) interface. | -| `i-sorted-sets` | | Enable the sorted sets (ZADD, etc) interface. | -| `i-slowlog` | | Enable the SLOWLOG interface. | -| `i-streams` | | Enable the streams (XADD, etc) interface. | -| `i-tracking` | | Enable a [client tracking](https://redis.io/docs/manual/client-side-caching/) interface. | -| `i-time-series` | | Enable a [Redis Timeseries](https://redis.io/docs/data-types/timeseries/) interface. | -| `i-redis-json` | | Enable a [RedisJSON](https://github.com/RedisJSON/RedisJSON) interface. | -| `i-redisearch` | | Enable a [RediSearch](https://github.com/RediSearch/RediSearch) interface. | -| `i-redis-stack` | | Enable the [Redis Stack](https://github.com/redis-stack) interfaces (`i-redis-json`, `i-time-series`, etc). | - -## Debugging Features - -| Name | Default | Description | -|----------------|---------|-----------------------------------------------------------------| -| `debug-ids` | | Enable a global counter used to differentiate commands in logs. | -| `network-logs` | | Enable additional TRACE logs for all frames on all sockets. | diff --git a/bin/README.md b/bin/README.md deleted file mode 100644 index 1e0d88cd..00000000 --- a/bin/README.md +++ /dev/null @@ -1,8 +0,0 @@ -Utils -===== - -* [benchmark](./benchmark) contains a module for benchmarking various client configurations. -* [benchmark_metrics](./benchmark_metrics) runs the benchmark script repeatedly with different argv combinations and - aggregates the results. -* [inf_loop](./inf_loop) runs a delayed infinite loop. This can be useful when doing ad-hoc cluster failover testing. -* [replica_consistency](./replica_consistency) Tests replica consistency and replication lag. \ No newline at end of file diff --git a/bin/benchmark/.gitignore b/bin/benchmark/.gitignore deleted file mode 100644 index 01916099..00000000 --- a/bin/benchmark/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.idea -target -Cargo.lock -perf* -server.log \ No newline at end of file diff --git a/bin/benchmark/Cargo.toml b/bin/benchmark/Cargo.toml deleted file mode 100644 index 60a2bf9e..00000000 --- a/bin/benchmark/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "fred_benchmark" -version = "0.1.0" -authors = ["Alec Embke "] -edition = "2018" -description = "A benchmarking script based on the `redis-benchmark` tool included with Redis." - -[profile.release] -debug = true - -[dependencies] -clap = { version = "2.33", features = ["yaml"] } -opentelemetry = { version = "0.18.0", features = ["rt-tokio", "trace"] } -opentelemetry-jaeger = { version = "0.17.0", features = ["tokio", "isahc_collector_client", "isahc", "collector_client", "rt-tokio"] } -tracing-attributes = "0.1.23" -tracing-opentelemetry = "0.18.0" -tracing-core = "0.1.30" -tracing-subscriber = "0.3.16" -tracing = "0.1.37" -log = "0.4" -pretty_env_logger = "0.5" -tokio = { version = "1", features = ["full"] } -futures = "0.3" -rand = "0.8" -indicatif = "=0.17.1" -bb8-redis = { version = "0.14", optional = true } - -[dependencies.fred] -#path = "../.." -path = "/fred" -features = ["replicas", "unix-sockets"] -default-features = false - -[features] -default = [] -assert-expected = [] -redis-rs = ["bb8-redis"] -enable-rustls = ["fred/enable-rustls"] -enable-native-tls = ["fred/enable-native-tls"] -debug-ids = ["fred/debug-ids"] -stdout-tracing = ["fred/partial-tracing"] -partial-tracing = ["fred/partial-tracing"] -full-tracing = ["fred/full-tracing"] -blocking-encoding = ["fred/blocking-encoding"] \ No newline at end of file diff --git a/bin/benchmark/README.md b/bin/benchmark/README.md deleted file mode 100644 index b4e83984..00000000 --- a/bin/benchmark/README.md +++ /dev/null @@ -1,163 +0,0 @@ -Fred Benchmark -============== - -Redis includes a [benchmarking tool](https://redis.io/docs/management/optimization/benchmarks/) that can be used to -measure the throughput of a client/connection pool. This module attempts to reproduce the same process with Tokio and -Fred. - -The general strategy involves using an atomic global counter and spawning `-c` Tokio tasks that share`-P` clients -in order to send `-n` total `INCR` commands to the server as quickly as possible. - -Each of the `-c` Tokio tasks use a different random key so commands are uniformly distributed across a cluster or -replica set. - -This strategy also has the benefit of being somewhat representative of an Axum or Actix web server use case where -requests run in separate Tokio tasks but share a common client pool. - -The [benchmark metrics](../benchmark_metrics) folder contains a tool that can test different combinations of -concurrency (`-c`) and pool size (`-P`) argv. - -## Tuning - -There are several additional features or performance tuning options that can affect these results. For example: - -* Tracing. Enabling the FF cut throughput by ~20% in my tests. -* Pipelining. The `auto_pipeline` feature can dramatically improve throughput in scenarios like this where a client or - pool is shared among many Tokio tasks. The original purpose of this tool was to test this particular optimization. -* Clustering -* Backpressure settings -* Network latency -* Log levels, often indirectly for the same reason as `tracing` (contention on a pipe, file handle, or socket). -* The size of the client connection pool. - -Callers should take care to consider each of these when deciding on argv values. - -This module also includes an optional `assert-expected` feature flag that adds an `assert!` call after each `INCR` -command to ensure the response is actually correct. - -## Tracing - -This also shows how to configure the client with tracing enabled against a local Jaeger instance. -A [docker compose](../../tests/docker/compose/jaeger.yml) file is included that will run a local Jaeger instance. - -``` -docker-compose -f /path/to/fred/tests/docker/compose/jaeger.yml up -``` - -Then navigate to . - -By default, this module does not compile any tracing features, but there are 3 flags that can toggle how tracing is -configured. - -* `partial-tracing` - Enables `fred/partial-tracing` and emits traces to the local jaeger instance. -* `full-tracing` - Enables `fred/full-tracing` and emits traces to the local jaeger instance. -* `stdout-tracing` - Enables `fred/partial-tracing` and emits traces to stdout. - -## Docker - -Linux+Docker is the best supported option via the `./run.sh` script. The `Cargo.toml` provided here has a comment/toggle -around the lines that need to change if callers want to use a remote server. - -Callers may have to also change `run.sh` to enable additional features in docker. - -## Usage - -``` -USAGE: - fred_benchmark [FLAGS] [OPTIONS] [SUBCOMMAND] - -FLAGS: - --cluster Whether to assume a clustered deployment. - --help Prints help information - -q, --quiet Only print the final req/sec measurement. - --replicas Whether to use `GET` with replica nodes instead of `INCR` with primary nodes. - -t, --tls Enable TLS via whichever build flag is provided. - -t, --tracing Whether to enable tracing via a local Jeager instance. See tests/docker-compose.yml to - start up a local Jaeger instance. - -V, --version Prints version information - -OPTIONS: - -a, --auth The password/key to use. `REDIS_USERNAME` and `REDIS_PASSWORD` can also be used. - -c, --concurrency The number of Tokio tasks used to run commands. [default: 100] - -n, --commands The number of commands to run. [default: 100000] - -h, --host The hostname of the redis server. [default: 127.0.0.1] - -P, --pool The number of clients in the redis connection pool. [default: 1] - -p, --port The port for the redis server. [default: 6379] - -u, --unix-sock The path to a unix socket. - -SUBCOMMANDS: - help Prints this message or the help of the given subcommand(s) - no-pipeline Run the test without pipelining [Default]. - pipeline Run the test with pipelining. -``` - -## Examples - -All the examples below use the following parameters: - -* Clustered deployment via local docker (3 primary nodes with one replica each) -* No tracing features enabled -* No TLS -* 10_000_000 INCR commands with `assert-expected` enabled -* 10_000 Tokio tasks -* 15 clients in the connection pool - -With `auto_pipeline` **disabled**: - -``` -$ ./run.sh --cluster -c 10000 -n 10000000 -P 15 -h redis-cluster-1 -p 30001 -a bar no-pipeline -Performed 10000000 operations in: 31.496934107s. Throughput: 317500 req/sec -``` - -With `auto_pipeline` **enabled**: - -``` -$ ./run.sh --cluster -c 10000 -n 10000000 -P 15 -h redis-cluster-1 -p 30001 -a bar pipeline -Performed 10000000 operations in: 4.125544401s. Throughput: 2424242 req/sec -``` - -With `auto_pipeline` **enabled** and using `GET` with replica nodes instead of `INCR` with primary nodes: - -``` -$ ./run.sh --cluster -c 10000 -n 10000000 -P 15 -h redis-cluster-1 -p 30001 -a bar --replicas pipeline -Performed 10000000 operations in: 3.356416674s. Throughput: 2979737 req/sec -``` - -Maybe Relevant Specs: - -* 32 CPUs -* 64 GB memory - -## `redis-rs` Comparison - -The `USE_REDIS_RS` environment variable can be toggled to [switch the benchmark logic](./src/_redis.rs) to -use `redis-rs` instead of `fred`. There's also an `info` level log line that can confirm this at runtime. - -The `redis-rs` variant uses the same general strategy, but with [bb8-redis](https://crates.io/crates/bb8-redis) ( -specifically `Pool`) instead of `fred::clients::RedisPool`. All the other components -in the benchmark logic are the same. - -### Examples - -These examples use the following parameters: - -* Centralized deployment via local docker -* No tracing features enabled -* No TLS -* 10_000_000 INCR commands with `assert-expected` enabled -* 10_000 Tokio tasks -* 15 clients in the connection pool - -``` -# fred without `auto_pipeline` -$ ./run.sh -h redis-main -p 6379 -a bar -n 10000000 -P 15 -c 10000 no-pipeline -Performed 10000000 operations in: 62.79547495s. Throughput: 159248 req/sec - -# redis-rs via bb8-redis -$ USE_REDIS_RS=1 ./run.sh -h redis-main -p 6379 -a bar -n 10000000 -P 15 -c 10000 -Performed 10000000 operations in: 62.583055519s. Throughput: 159787 req/sec - -# fred with `auto_pipeline` -$ ./run.sh -h redis-main -p 6379 -a bar -n 10000000 -P 15 -c 10000 pipeline -Performed 10000000 operations in: 5.882182708s. Throughput: 1700102 req/sec -``` \ No newline at end of file diff --git a/bin/benchmark/cli.yml b/bin/benchmark/cli.yml deleted file mode 100644 index 61bbfb57..00000000 --- a/bin/benchmark/cli.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: fred_benchmark -version: "1.0" -author: Alec Embke -about: A benchmarking module based on the `redis-benchmark` tool included with Redis. -args: - - tracing: - short: t - long: tracing - help: Whether to enable tracing via a local Jeager instance. See tests/docker-compose.yml to start up a local Jaeger instance. - takes_value: false - - cluster: - long: cluster - help: Whether to assume a clustered deployment. - takes_value: false - - replicas: - long: replicas - help: Whether to use `GET` with replica nodes instead of `INCR` with primary nodes. - takes_value: false - - quiet: - short: q - long: quiet - help: Only print the final req/sec measurement. - takes_value: false - - tls: - short: t - long: tls - help: "Enable TLS via whichever build flag is provided." - takes_value: false - - count: - short: n - long: commands - value_name: "NUMBER" - help: The number of commands to run. - takes_value: true - default_value: "100000" - - concurrency: - short: c - long: concurrency - value_name: "NUMBER" - help: The number of Tokio tasks used to run commands. - takes_value: true - default_value: "100" - - unix: - short: u - long: unix-sock - value_name: "PATH" - help: The path to a unix socket. - takes_value: true - - host: - short: h - long: host - value_name: "STRING" - help: The hostname of the redis server. - takes_value: true - default_value: "127.0.0.1" - - port: - short: p - long: port - value_name: "NUMBER" - help: The port for the redis server. - takes_value: true - default_value: "6379" - - pool: - short: P - long: pool - value_name: "NUMBER" - help: The number of clients in the redis connection pool. - takes_value: true - default_value: "1" - - auth: - short: a - long: auth - value_name: "STRING" - help: The password/key to use. `REDIS_USERNAME` and `REDIS_PASSWORD` can also be used. - takes_value: true -subcommands: - - pipeline: - about: Run the test with pipelining. - - no-pipeline: - about: Run the test without pipelining [Default]. \ No newline at end of file diff --git a/bin/benchmark/docker-compose.yml b/bin/benchmark/docker-compose.yml deleted file mode 100644 index fb5e8287..00000000 --- a/bin/benchmark/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: '2' - -services: - unix-socket-tmp: - image: busybox - command: "chmod -R 777 ${REDIS_UNIX_SOCK_CONTAINER_DIR}" - volumes: - - '${REDIS_UNIX_SOCK_HOST_DIR}:${REDIS_UNIX_SOCK_CONTAINER_DIR}' - fred-benchmark: - depends_on: - - redis-main - - redis-cluster-6 - - redis-main-unix-socket - container_name: "fred-benchmark" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - environment: - RUST_LOG: "${RUST_LOG}" - REDIS_VERSION: "${REDIS_VERSION}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - REDIS_USERNAME: "${REDIS_USERNAME}" - volumes: - - "../../../bin/benchmark:/project" - - "../../..:/fred" - - "~/.cargo/registry:/usr/local/cargo/registry" - volumes_from: - - unix-socket-tmp \ No newline at end of file diff --git a/bin/benchmark/inf_loop.sh b/bin/benchmark/inf_loop.sh deleted file mode 100755 index 92b02aa2..00000000 --- a/bin/benchmark/inf_loop.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -idx=1 -while : -do - echo "Running ($idx)..." - $@ > server.log 2>&1 - idx=$(( $idx + 1 )) -done \ No newline at end of file diff --git a/bin/benchmark/run.sh b/bin/benchmark/run.sh deleted file mode 100755 index 9a26e3cf..00000000 --- a/bin/benchmark/run.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -[[ -z "${USE_REDIS_RS}" ]] && FEATURES="assert-expected" || FEATURES="assert-expected redis-rs" - -echo $FEATURES - -docker-compose -f ../../tests/docker/compose/cluster.yml \ - -f ../../tests/docker/compose/centralized.yml \ - -f ../../tests/docker/compose/unix-socket.yml -f ./docker-compose.yml \ - run -u $(id -u ${USER}):$(id -g ${USER}) --rm fred-benchmark cargo run --release --features "$FEATURES" -- "${@:1}" \ No newline at end of file diff --git a/bin/benchmark/rustfmt.toml b/bin/benchmark/rustfmt.toml deleted file mode 100644 index 08e76d74..00000000 --- a/bin/benchmark/rustfmt.toml +++ /dev/null @@ -1,52 +0,0 @@ -binop_separator = "Front" -blank_lines_upper_bound = 1 -brace_style = "SameLineWhere" -combine_control_expr = true -comment_width = 125 -condense_wildcard_suffixes = false -control_brace_style = "AlwaysSameLine" -edition = "2021" -empty_item_single_line = true -enum_discrim_align_threshold = 0 -error_on_line_overflow = false -error_on_unformatted = false -fn_params_layout = "Tall" -fn_single_line = false -force_explicit_abi = true -force_multiline_blocks = false -format_code_in_doc_comments = true -format_macro_bodies = true -format_macro_matchers = true -format_strings = true -hard_tabs = false -imports_granularity = "Crate" -imports_indent = "Block" -imports_layout = "HorizontalVertical" -indent_style = "Block" -inline_attribute_width = 0 -match_arm_blocks = true -match_block_trailing_comma = true -max_width = 118 -merge_derives = true -newline_style = "Auto" -normalize_comments = true -normalize_doc_attributes = true -overflow_delimited_expr = true -remove_nested_parens = true -reorder_impl_items = true -reorder_imports = true -reorder_modules = true -show_parse_errors = true -space_after_colon = true -space_before_colon = false -spaces_around_ranges = true -struct_field_align_threshold = 50 -struct_lit_single_line = true -tab_spaces = 2 -trailing_semicolon = true -type_punctuation_density = "Wide" -use_field_init_shorthand = true -use_small_heuristics = "Default" -use_try_shorthand = true -where_single_line = false -wrap_comments = true diff --git a/bin/benchmark/src/_fred.rs b/bin/benchmark/src/_fred.rs deleted file mode 100644 index ffc607ed..00000000 --- a/bin/benchmark/src/_fred.rs +++ /dev/null @@ -1,136 +0,0 @@ -use crate::{utils, Argv}; -use fred::{clients::RedisPool, error::RedisError, prelude::*, types::Builder as RedisBuilder}; -use indicatif::ProgressBar; -use std::{ - error::Error, - sync::{atomic::AtomicUsize, Arc}, - time::{Duration, SystemTime}, -}; -use tokio::task::JoinHandle; - -#[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enabled-rustls-ring" -))] -use fred::types::{TlsConfig, TlsConnector, TlsHostMapping}; -use futures::TryStreamExt; - -#[cfg(feature = "enable-rustls")] -fn default_tls_config() -> TlsConfig { - TlsConfig { - connector: TlsConnector::default_rustls().unwrap(), - hostnames: TlsHostMapping::None, - } -} - -#[cfg(feature = "enable-native-tls")] -fn default_tls_config() -> TlsConfig { - TlsConfig { - connector: TlsConnector::default_native_tls().unwrap(), - hostnames: TlsHostMapping::None, - } -} - -pub async fn init(argv: &Arc) -> Result { - let (username, password) = utils::read_auth_env(); - let config = RedisConfig { - fail_fast: true, - server: if argv.unix.is_some() { - ServerConfig::Unix { - path: argv.unix.clone().unwrap().into(), - } - } else if argv.cluster { - ServerConfig::Clustered { - hosts: vec![Server::new(&argv.host, argv.port)], - } - } else { - ServerConfig::new_centralized(&argv.host, argv.port) - }, - username, - password: argv.auth.clone().or(password), - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls: default_tls_config(), - #[cfg(any(feature = "stdout-tracing", feature = "partial-tracing", feature = "full-tracing"))] - tracing: TracingConfig::new(argv.tracing), - ..Default::default() - }; - - let pool = RedisBuilder::from_config(config) - .with_performance_config(|config| { - config.auto_pipeline = argv.pipeline; - config.backpressure.max_in_flight_commands = 100_000_000; - }) - .with_connection_config(|config| { - config.internal_command_timeout = Duration::from_secs(5); - }) - .set_policy(ReconnectPolicy::new_constant(0, 500)) - .build_pool(argv.pool)?; - - info!("Connecting to {}:{}...", argv.host, argv.port); - pool.connect(); - pool.wait_for_connect().await?; - info!("Connected to {}:{}.", argv.host, argv.port); - pool.flushall_cluster().await?; - - Ok(pool) -} - -fn spawn_client_task( - bar: &Option, - client: &RedisClient, - counter: &Arc, - argv: &Arc, -) -> JoinHandle<()> { - let (bar, client, counter, argv) = (bar.clone(), client.clone(), counter.clone(), argv.clone()); - - tokio::spawn(async move { - let key = utils::random_string(15); - let mut expected = 0; - - while utils::incr_atomic(&counter) < argv.count { - if argv.replicas { - let _: () = client.replicas().get(&key).await.map_err(utils::crash).unwrap(); - } else { - expected += 1; - let actual: i64 = client.incr(&key).await.map_err(utils::crash).unwrap(); - - #[cfg(feature = "assert-expected")] - { - if actual != expected { - println!("Unexpected result: {} == {}", actual, expected); - std::process::exit(1); - } - } - } - if let Some(ref bar) = bar { - bar.inc(1); - } - } - }) -} - -pub async fn run(argv: Arc, counter: Arc, bar: Option) -> Duration { - info!("Running with fred"); - - let pool = init(&argv).await.expect("Failed to init"); - let mut tasks = Vec::with_capacity(argv.tasks); - - info!("Starting commands..."); - let started = SystemTime::now(); - for _ in 0..argv.tasks { - tasks.push(spawn_client_task(&bar, pool.next(), &counter, &argv)); - } - if let Err(e) = futures::future::try_join_all(tasks).await { - println!("Finished with error: {:?}", e); - std::process::exit(1); - } - - SystemTime::now() - .duration_since(started) - .expect("Failed to calculate duration") -} diff --git a/bin/benchmark/src/_redis.rs b/bin/benchmark/src/_redis.rs deleted file mode 100644 index da9dc797..00000000 --- a/bin/benchmark/src/_redis.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::{utils, Argv}; -use bb8_redis::{ - bb8::{self, Pool, PooledConnection}, - redis::{cmd, AsyncCommands, ErrorKind as RedisErrorKind, RedisError}, - RedisConnectionManager, RedisMultiplexedConnectionManager, -}; -use futures::TryStreamExt; -use indicatif::ProgressBar; -use opentelemetry::trace::FutureExt; -use std::{ - error::Error, - sync::{atomic::AtomicUsize, Arc}, - time::{Duration, SystemTime}, -}; -use tokio::task::JoinHandle; - -async fn incr_key(pool: &Pool, key: &str) -> i64 { - let mut conn = pool.get().await.map_err(utils::crash).unwrap(); - cmd("INCR") - .arg(key) - .query_async(&mut *conn) - .await - .map_err(utils::crash) - .unwrap() -} - -async fn del_key(pool: &Pool, key: &str) -> i64 { - let mut conn = pool.get().await.map_err(utils::crash).unwrap(); - cmd("DEL") - .arg(key) - .query_async(&mut *conn) - .await - .map_err(utils::crash) - .unwrap() -} - -fn spawn_client_task( - bar: &Option, - pool: &Pool, - counter: &Arc, - argv: &Arc, -) -> JoinHandle<()> { - let (bar, pool, counter, argv) = (bar.clone(), pool.clone(), counter.clone(), argv.clone()); - - tokio::spawn(async move { - let key = utils::random_string(15); - let mut expected = 0; - - while utils::incr_atomic(&counter) < argv.count { - expected += 1; - let actual = incr_key(&pool, &key).await; - - #[cfg(feature = "assert-expected")] - { - if actual != expected { - println!("Unexpected result: {} == {}", actual, expected); - std::process::exit(1); - } - } - - if let Some(ref bar) = bar { - bar.inc(1); - } - } - }) -} - -// TODO support clustered deployments -async fn init(argv: &Arc) -> Pool { - let (username, password) = utils::read_auth_env(); - let url = if let Some(password) = password { - let username = username.map(|s| format!("{s}:")).unwrap_or("".into()); - format!("redis://{}{}@{}:{}", username, password, argv.host, argv.port) - } else { - format!("redis://{}:{}", argv.host, argv.port) - }; - debug!("Redis conn: {}", url); - - let manager = RedisMultiplexedConnectionManager::new(url).expect("Failed to create redis connection manager"); - let pool = bb8::Pool::builder() - .max_size(argv.pool as u32) - .build(manager) - .await - .expect("Failed to create client pool"); - - // try to warm up the pool first - let mut warmup_ft = Vec::with_capacity(argv.pool + 1); - for _ in 0..argv.pool + 1 { - warmup_ft.push(async { incr_key(&pool, "foo").await }); - } - futures::future::join_all(warmup_ft).await; - del_key(&pool, "foo").await; - - pool -} - -pub async fn run(argv: Arc, counter: Arc, bar: Option) -> Duration { - info!("Running with redis-rs"); - - if argv.cluster || argv.replicas { - panic!("Cluster or replica features are not supported yet with redis-rs benchmarks."); - } - let pool = init(&argv).await; - let mut tasks = Vec::with_capacity(argv.tasks); - - info!("Starting commands..."); - let started = SystemTime::now(); - for _ in 0..argv.tasks { - tasks.push(spawn_client_task(&bar, &pool, &counter, &argv)); - } - futures::future::join_all(tasks).await; - - SystemTime::now() - .duration_since(started) - .expect("Failed to calculate duration") -} diff --git a/bin/benchmark/src/main.rs b/bin/benchmark/src/main.rs deleted file mode 100644 index 530a16d9..00000000 --- a/bin/benchmark/src/main.rs +++ /dev/null @@ -1,232 +0,0 @@ -// shh -#![allow(warnings)] - -#[macro_use] -extern crate clap; -extern crate fred; -extern crate futures; -extern crate opentelemetry; -extern crate opentelemetry_jaeger; -extern crate tokio; -extern crate tracing; -extern crate tracing_opentelemetry; -extern crate tracing_subscriber; - -#[macro_use] -extern crate log; -extern crate pretty_env_logger; - -#[cfg(any(feature = "partial-tracing", feature = "full-tracing", feature = "stdout-tracing"))] -use fred::types::TracingConfig; - -use clap::App; -use indicatif::ProgressBar; -use opentelemetry::{ - global, - sdk::{ - export::trace::stdout, - runtime::{Runtime, Tokio}, - trace::{self, RandomIdGenerator, Sampler, TraceRuntime}, - }, -}; -use opentelemetry_jaeger::JaegerTraceRuntime; -use std::{ - default::Default, - env, - sync::{atomic::AtomicUsize, Arc}, - thread::{self}, - time::{Duration, SystemTime}, -}; -use tokio::{runtime::Builder, task::JoinHandle, time::Instant}; -use tracing_subscriber::{layer::SubscriberExt, Layer, Registry}; - -static DEFAULT_COMMAND_COUNT: usize = 10_000; -static DEFAULT_CONCURRENCY: usize = 10; -static DEFAULT_HOST: &'static str = "127.0.0.1"; -static DEFAULT_PORT: u16 = 6379; - -mod utils; - -#[cfg(all( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" -))] -compile_error!("Cannot use both TLS feature flags."); - -#[cfg(not(feature = "redis-rs"))] -mod _fred; -#[cfg(feature = "redis-rs")] -mod _redis; -#[cfg(not(feature = "redis-rs"))] -use _fred::run as run_benchmark; -#[cfg(feature = "redis-rs")] -use _redis::run as run_benchmark; - -// TODO update clap -#[derive(Debug)] -struct Argv { - pub cluster: bool, - pub replicas: bool, - pub tracing: bool, - pub count: usize, - pub tasks: usize, - pub unix: Option, - pub host: String, - pub port: u16, - pub pipeline: bool, - pub pool: usize, - pub quiet: bool, - pub auth: Option, -} - -fn parse_argv() -> Arc { - let yaml = load_yaml!("../cli.yml"); - let matches = App::from_yaml(yaml).get_matches(); - let tracing = matches.is_present("tracing"); - let mut cluster = matches.is_present("cluster"); - let replicas = matches.is_present("replicas"); - let quiet = matches.is_present("quiet"); - - if replicas { - cluster = true; - } - - let count = matches - .value_of("count") - .map(|v| { - v.parse::().unwrap_or_else(|_| { - panic!("Invalid command count: {}.", v); - }) - }) - .unwrap_or(DEFAULT_COMMAND_COUNT); - let tasks = matches - .value_of("concurrency") - .map(|v| { - v.parse::().unwrap_or_else(|_| { - panic!("Invalid concurrency: {}.", v); - }) - }) - .unwrap_or(DEFAULT_CONCURRENCY); - let host = matches - .value_of("host") - .map(|v| v.to_owned()) - .unwrap_or("127.0.0.1".into()); - let port = matches - .value_of("port") - .map(|v| v.parse::().expect("Invalid port")) - .unwrap_or(DEFAULT_PORT); - let unix = matches.value_of("unix").map(|v| v.to_owned()); - let pool = matches - .value_of("pool") - .map(|v| v.parse::().expect("Invalid pool")) - .unwrap_or(1); - let auth = matches.value_of("auth").map(|v| v.to_owned()); - let pipeline = matches.subcommand_matches("pipeline").is_some(); - - Arc::new(Argv { - cluster, - quiet, - unix, - tracing, - count, - tasks, - host, - port, - pipeline, - pool, - replicas, - auth, - }) -} - -#[cfg(all( - not(feature = "partial-tracing"), - not(feature = "stdout-tracing"), - not(feature = "full-tracing") -))] -pub fn setup_tracing(enable: bool) {} - -#[cfg(feature = "stdout-tracing")] -pub fn setup_tracing(enable: bool) { - if enable { - info!("Starting stdout tracing..."); - let layer = tracing_subscriber::fmt::layer() - .with_writer(std::io::stdout) - .with_ansi(false) - .event_format(tracing_subscriber::fmt::format().pretty()) - .with_thread_names(true) - .with_level(true) - .with_line_number(true) - .with_filter(tracing_subscriber::filter::LevelFilter::TRACE); - let subscriber = Registry::default().with(layer); - tracing::subscriber::set_global_default(subscriber).expect("Failed to set global tracing subscriber"); - } -} - -#[cfg(any(feature = "partial-tracing", feature = "full-tracing"))] -pub fn setup_tracing(enable: bool) { - let sampler = if enable { - info!("Starting tracing..."); - Sampler::AlwaysOn - } else { - Sampler::AlwaysOff - }; - - global::set_text_map_propagator(opentelemetry_jaeger::Propagator::new()); - let jaeger_install = opentelemetry_jaeger::new_agent_pipeline() - .with_service_name("fred-benchmark") - .with_trace_config( - trace::config() - .with_sampler(sampler) - .with_id_generator(RandomIdGenerator::default()) - .with_max_attributes_per_span(32), - ) - .install_simple(); - - let tracer = match jaeger_install { - Ok(t) => t, - Err(e) => panic!("Fatal error initializing tracing: {:?}", e), - }; - - let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); - let subscriber = Registry::default().with(telemetry); - tracing::subscriber::set_global_default(subscriber).expect("Failed to set global tracing subscriber"); - - info!("Initialized opentelemetry-jaeger pipeline."); -} - -fn main() { - pretty_env_logger::init(); - let argv = parse_argv(); - info!("Running with configuration: {:?}", argv); - - let sch = Builder::new_multi_thread().enable_all().build().unwrap(); - sch.block_on(async move { - setup_tracing(argv.tracing); - let counter = Arc::new(AtomicUsize::new(0)); - let bar = if argv.quiet { - None - } else { - Some(ProgressBar::new(argv.count as u64)) - }; - - let duration = run_benchmark(argv.clone(), counter, bar.clone()).await; - let duration_sec = duration.as_secs() as f64 + (duration.subsec_millis() as f64 / 1000.0); - if let Some(bar) = bar { - bar.finish(); - } - - if argv.quiet { - println!("{}", (argv.count as f64 / duration_sec) as u64); - } else { - println!( - "Performed {} operations in: {:?}. Throughput: {} req/sec", - argv.count, - duration, - (argv.count as f64 / duration_sec) as u64 - ); - } - global::shutdown_tracer_provider(); - }); -} diff --git a/bin/benchmark/src/utils.rs b/bin/benchmark/src/utils.rs deleted file mode 100644 index 25a749db..00000000 --- a/bin/benchmark/src/utils.rs +++ /dev/null @@ -1,45 +0,0 @@ -use rand::{self, distributions::Alphanumeric, Rng}; -use std::{ - env, - error::Error, - sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, - }, -}; - -pub fn incr_atomic(size: &Arc) -> usize { - size.fetch_add(1, Ordering::AcqRel).saturating_add(1) -} - -pub fn incr_by_atomic(size: &Arc, n: usize) -> usize { - size.fetch_add(n, Ordering::AcqRel).saturating_add(n) -} - -pub fn read_atomic(size: &Arc) -> usize { - size.load(Ordering::Acquire) -} - -pub fn set_atomic(size: &Arc, val: usize) -> usize { - size.swap(val, Ordering::SeqCst) -} - -pub fn read_auth_env() -> (Option, Option) { - let username = env::var_os("REDIS_USERNAME").and_then(|s| s.into_string().ok()); - let password = env::var_os("REDIS_PASSWORD").and_then(|s| s.into_string().ok()); - - (username, password) -} - -pub fn random_string(len: usize) -> String { - rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(len) - .map(char::from) - .collect() -} - -pub fn crash(error: impl Error) { - println!("{:?}", error); - std::process::exit(1); -} diff --git a/bin/benchmark_metrics/.gitignore b/bin/benchmark_metrics/.gitignore deleted file mode 100644 index a49f1a58..00000000 --- a/bin/benchmark_metrics/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -.DS_Store -target -Cargo.lock -.idea -tests/tmp/* -!tests/tmp/.gitkeep -dump.rdb -appendonly.aof -test_users.acl -centralized_server.log -redis_centralized.conf -redis_server.pid -tests/users.acl -tests/docker/overrides/* -!tests/docker/overrides/.gitkeep \ No newline at end of file diff --git a/bin/benchmark_metrics/Cargo.toml b/bin/benchmark_metrics/Cargo.toml deleted file mode 100644 index 4788791e..00000000 --- a/bin/benchmark_metrics/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "benchmark_metrics" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -subprocess = "0.2" -indicatif = "=0.17.1" -log = "0.4" -pretty_env_logger = "0.5" -clap = { version = "2.33", features = ["yaml"] } -csv = "1.1" diff --git a/bin/benchmark_metrics/README.md b/bin/benchmark_metrics/README.md deleted file mode 100644 index 0e739ece..00000000 --- a/bin/benchmark_metrics/README.md +++ /dev/null @@ -1,72 +0,0 @@ -Benchmark Metrics -================= - -Two of the more interesting variables for performance tuning purposes are the number of Tokio tasks (`--concurrency` -or `-c`) and the size of the client pool (`pool` or `-P`). This module will repeatedly run the [benchmark](../benchmark) -tool with different combinations of these two inputs. - -``` -./run.sh -h redis-cluster-1 -p 30001 --cluster -n 1000000 -P 1-16 --pool-step 2 -c 50-10000 --concurrency-step 50 pipeline -``` - -This will run the benchmark module with sensible combinations of argv in the ranges provided. The output CSV file maps -these argv combinations to their benchmarked throughput value. - -## Usage - -``` -USAGE: - benchmark_metrics [FLAGS] [OPTIONS] [SUBCOMMAND] - -FLAGS: - --cluster Whether to assume a clustered deployment. - --help Prints help information - -V, --version Prints version information - -OPTIONS: - -c, --concurrency The number of concurrent tasks used to run commands. [default: 1-1000] - --concurrency-step The amount to increment the `concurrency` value on each test run. [default: 30] - -n, --commands The number of commands to run. [default: 10000] - -h, --host The hostname of the redis server. [default: 127.0.0.1] - -P, --pool The number of clients in the redis connection pool. [default: 1-50] - --pool-step The amount to increment the `pool` value on each test run. [default: 2] - -p, --port The port for the redis server. [default: 6379] - -SUBCOMMANDS: - help Prints this message or the help of the given subcommand(s) - no-pipeline Run the test without pipelining. - pipeline Run the test with pipelining. -``` - -## Docker - -See the [benchmark](../benchmark/README.md#docker) section for more information. Many of the same restrictions apply. - -## Examples - -The [metrics](./metrics) folder has the results of running this on my desktop with the following parameters: - -* 32 CPUs, 64 GB, Ubuntu -* Clustered deployment running via local docker (3 primary nodes with one replica each) -* Centralized deployment running via local docker (no replicas) -* No tracing features enabled -* No TLS -* 1_000_000 INCR commands with `assert-expected` enabled -* Between 100 and 15_000 Tokio tasks, with increments of 100 -* 1 - 20 clients in the connection pool, with increments of 2 - -To regenerate the `metrics` folder (assuming Docker on Linux): - -``` -# don't forget the `source /path/to/fred/tests/environ` step - -# warning: it can be difficult to debug issues through multiple layers of docker. i'd recommend testing the underlying -# commands individually before running this since this script takes hours and does not show output while running. - -./run.sh -h redis-cluster-1 -p 30001 --cluster -n 1000000 -P 1-20 --pool-step 2 -c 100-15000 --concurrency-step 100 pipeline > ./metrics/clustered/pipeline.csv \ - && ./run.sh -h redis-cluster-1 -p 30001 --cluster -n 1000000 -P 1-20 --pool-step 2 -c 100-15000 --concurrency-step 100 no-pipeline > ./metrics/clustered/no-pipeline.csv \ - && ./run.sh -h redis-main -p 6379 -n 1000000 -P 1-20 --pool-step 2 -c 100-15000 --concurrency-step 100 pipeline > ./metrics/centralized/pipeline.csv \ - && ./run.sh -h redis-main -p 6379 -n 1000000 -P 1-20 --pool-step 2 -c 100-15000 --concurrency-step 100 no-pipeline > ./metrics/centralized/no-pipeline.csv -``` - - diff --git a/bin/benchmark_metrics/cli.yml b/bin/benchmark_metrics/cli.yml deleted file mode 100644 index 0b4fca7b..00000000 --- a/bin/benchmark_metrics/cli.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: benchmark_metrics -version: "1.0" -author: Alec Embke -about: Run the `benchmark` tool with different argv combinations, returning results as a CSV. -args: - - cluster: - long: cluster - help: Whether to assume a clustered deployment. - takes_value: false - - count: - short: n - long: commands - value_name: "NUMBER" - help: The number of commands to run. - takes_value: true - default_value: "10000" - - host: - short: h - long: host - value_name: "STRING" - help: The hostname of the redis server. - takes_value: true - default_value: "127.0.0.1" - - port: - short: p - long: port - value_name: "NUMBER" - help: The port for the redis server. - takes_value: true - default_value: "6379" - - pool: - short: P - long: pool - value_name: "RANGE (X-Y)" - help: The number of clients in the redis connection pool. - takes_value: true - default_value: "1-50" - - concurrency: - short: c - long: concurrency - value_name: "RANGE (X-Y)" - help: The number of concurrent tasks used to run commands. - takes_value: true - default_value: "1-1000" - - pool-step: - long: pool-step - value_name: "NUMBER" - help: The amount to increment the `pool` value on each test run. - takes_value: true - default_value: "2" - - concurrency-step: - long: concurrency-step - value_name: "NUMBER" - help: The amount to increment the `concurrency` value on each test run. - takes_value: true - default_value: "30" -subcommands: - - pipeline: - about: Run the test with pipelining. - - no-pipeline: - about: Run the test without pipelining. \ No newline at end of file diff --git a/bin/benchmark_metrics/docker-compose.yml b/bin/benchmark_metrics/docker-compose.yml deleted file mode 100644 index 5c1de133..00000000 --- a/bin/benchmark_metrics/docker-compose.yml +++ /dev/null @@ -1,24 +0,0 @@ -version: '2' - -services: - fred-benchmark-metrics: - depends_on: - - redis-main - - redis-cluster-6 - container_name: "fred-benchmark-metrics" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - environment: - RUST_LOG: "${RUST_LOG}" - REDIS_VERSION: "${REDIS_VERSION}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - volumes: - - "../../../bin/benchmark_metrics:/project" - - "../../../bin/benchmark:/benchmark" - - "../../..:/fred" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/bin/benchmark_metrics/metrics/centralized/README.md b/bin/benchmark_metrics/metrics/centralized/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/bin/benchmark_metrics/metrics/centralized/no-pipeline.csv b/bin/benchmark_metrics/metrics/centralized/no-pipeline.csv deleted file mode 100644 index 3a179f2c..00000000 --- a/bin/benchmark_metrics/metrics/centralized/no-pipeline.csv +++ /dev/null @@ -1,1502 +0,0 @@ -concurrency,pool,throughput -100,1,44974 -200,1,39770 -300,1,36904 -400,1,34354 -500,1,44050 -600,1,33160 -700,1,42511 -800,1,39085 -900,1,39151 -1000,1,33684 -1100,1,33322 -1200,1,33730 -1300,1,33209 -1400,1,47492 -1500,1,32642 -1600,1,34079 -1700,1,34068 -1800,1,35824 -1900,1,34438 -2000,1,41536 -2100,1,34064 -2200,1,38728 -2300,1,35563 -2400,1,36127 -2500,1,37195 -2600,1,32138 -2700,1,35143 -2800,1,35012 -2900,1,37838 -3000,1,44748 -3100,1,40824 -3200,1,37787 -3300,1,37969 -3400,1,36809 -3500,1,38189 -3600,1,36695 -3700,1,36983 -3800,1,31942 -3900,1,42515 -4000,1,36806 -4100,1,40033 -4200,1,34002 -4300,1,43404 -4400,1,35206 -4500,1,36745 -4600,1,35422 -4700,1,37268 -4800,1,38537 -4900,1,36465 -5000,1,40024 -5100,1,34320 -5200,1,40466 -5300,1,39281 -5400,1,34614 -5500,1,35991 -5600,1,33950 -5700,1,41235 -5800,1,34492 -5900,1,34216 -6000,1,38868 -6100,1,41939 -6200,1,36106 -6300,1,35909 -6400,1,36615 -6500,1,35859 -6600,1,33772 -6700,1,46768 -6800,1,37072 -6900,1,36196 -7000,1,41863 -7100,1,33555 -7200,1,33424 -7300,1,36822 -7400,1,32356 -7500,1,35560 -7600,1,37079 -7700,1,35135 -7800,1,39303 -7900,1,38714 -8000,1,36233 -8100,1,34724 -8200,1,36288 -8300,1,39053 -8400,1,33745 -8500,1,40014 -8600,1,41755 -8700,1,37032 -8800,1,38211 -8900,1,33815 -9000,1,36362 -9100,1,44440 -9200,1,36420 -9300,1,34768 -9400,1,41272 -9500,1,36046 -9600,1,34908 -9700,1,40390 -9800,1,34564 -9900,1,43576 -10000,1,35518 -10100,1,38443 -10200,1,44974 -10300,1,37864 -10400,1,37234 -10500,1,38720 -10600,1,35404 -10700,1,36655 -10800,1,31872 -10900,1,39506 -11000,1,36075 -11100,1,43438 -11200,1,40275 -11300,1,36090 -11400,1,38880 -11500,1,34139 -11600,1,32997 -11700,1,35967 -11800,1,39053 -11900,1,38792 -12000,1,36110 -12100,1,36756 -12200,1,36291 -12300,1,35330 -12400,1,38063 -12500,1,35582 -12600,1,33333 -12700,1,34302 -12800,1,38592 -12900,1,40202 -13000,1,35644 -13100,1,33545 -13200,1,35112 -13300,1,32367 -13400,1,33818 -13500,1,40658 -13600,1,38240 -13700,1,36456 -13800,1,37160 -13900,1,32444 -14000,1,33136 -14100,1,34491 -14200,1,41540 -14300,1,44583 -14400,1,37807 -14500,1,38735 -14600,1,33990 -14700,1,46479 -14800,1,37484 -14900,1,34963 -15000,1,33860 -100,3,118694 -200,3,116279 -300,3,113314 -400,3,111669 -500,3,116157 -600,3,118553 -700,3,117688 -800,3,116672 -900,3,115247 -1000,3,108742 -1100,3,112170 -1200,3,121847 -1300,3,113391 -1400,3,122264 -1500,3,120685 -1600,3,113623 -1700,3,115995 -1800,3,109890 -1900,3,113224 -2000,3,119232 -2100,3,117315 -2200,3,116171 -2300,3,114077 -2400,3,120875 -2500,3,118976 -2600,3,121787 -2700,3,111557 -2800,3,121124 -2900,3,121285 -3000,3,118736 -3100,3,114038 -3200,3,113817 -3300,3,114442 -3400,3,115460 -3500,3,116767 -3600,3,119189 -3700,3,123365 -3800,3,116063 -3900,3,112309 -4000,3,122819 -4100,3,115713 -4200,3,115740 -4300,3,115180 -4400,3,110975 -4500,3,120802 -4600,3,109878 -4700,3,114311 -4800,3,121021 -4900,3,114181 -5000,3,111507 -5100,3,119717 -5200,3,119717 -5300,3,121832 -5400,3,112157 -5500,3,119345 -5600,3,118315 -5700,3,116918 -5800,3,119431 -5900,3,118511 -6000,3,120365 -6100,3,117868 -6200,3,113817 -6300,3,115713 -6400,3,118497 -6500,3,109337 -6600,3,110132 -6700,3,115101 -6800,3,118525 -6900,3,119990 -7000,3,117219 -7100,3,109661 -7200,3,114311 -7300,3,109890 -7400,3,115021 -7500,3,120206 -7600,3,114626 -7700,3,117730 -7800,3,119460 -7900,3,115194 -8000,3,113417 -8100,3,110754 -8200,3,112726 -8300,3,113109 -8400,3,110411 -8500,3,117868 -8600,3,114810 -8700,3,117481 -8800,3,114810 -8900,3,116836 -9000,3,116252 -9100,3,114810 -9200,3,116672 -9300,3,112057 -9400,3,118021 -9500,3,111982 -9600,3,113908 -9700,3,116522 -9800,3,114481 -9900,3,111457 -10000,3,118948 -10100,3,109122 -10200,3,111831 -10300,3,111894 -10400,3,112422 -10500,3,123304 -10600,3,113314 -10700,3,118161 -10800,3,113507 -10900,3,111420 -11000,3,110963 -11100,3,117343 -11200,3,111894 -11300,3,123670 -11400,3,114836 -11500,3,116672 -11600,3,112714 -11700,3,117205 -11800,3,117536 -11900,3,110241 -12000,3,110399 -12100,3,120583 -12200,3,115035 -12300,3,113314 -12400,3,113752 -12500,3,116373 -12600,3,115194 -12700,3,112321 -12800,3,115260 -12900,3,114784 -13000,3,119846 -13100,3,109841 -13200,3,116836 -13300,3,109075 -13400,3,113352 -13500,3,115088 -13600,3,113391 -13700,3,114377 -13800,3,110877 -13900,3,117702 -14000,3,113713 -14100,3,118315 -14200,3,111234 -14300,3,117757 -14400,3,113494 -14500,3,116157 -14600,3,111507 -14700,3,117495 -14800,3,113520 -14900,3,115928 -15000,3,122025 -100,5,137950 -200,5,141542 -300,5,146305 -400,5,143636 -500,5,140449 -600,5,140864 -700,5,141683 -800,5,145158 -900,5,144425 -1000,5,146348 -1100,5,152322 -1200,5,141302 -1300,5,138102 -1400,5,131423 -1500,5,145878 -1600,5,144738 -1700,5,135317 -1800,5,143926 -1900,5,140984 -2000,5,148743 -2100,5,137931 -2200,5,149009 -2300,5,140036 -2400,5,142065 -2500,5,140548 -2600,5,149387 -2700,5,146842 -2800,5,142247 -2900,5,151745 -3000,5,145011 -3100,5,143122 -3200,5,145708 -3300,5,139938 -3400,5,147275 -3500,5,141282 -3600,5,143967 -3700,5,138888 -3800,5,149186 -3900,5,142714 -4000,5,150875 -4100,5,141502 -4200,5,151768 -4300,5,137362 -4400,5,149454 -4500,5,143740 -4600,5,148743 -4700,5,142328 -4800,5,147405 -4900,5,148411 -5000,5,145053 -5100,5,142592 -5200,5,156298 -5300,5,151860 -5400,5,148743 -5500,5,145878 -5600,5,144446 -5700,5,143616 -5800,5,141302 -5900,5,152951 -6000,5,141602 -6100,5,140370 -6200,5,144927 -6300,5,146907 -6400,5,148345 -6500,5,146692 -6600,5,145264 -6700,5,149365 -6800,5,147688 -6900,5,145391 -7000,5,142308 -7100,5,141203 -7200,5,141382 -7300,5,136705 -7400,5,143225 -7500,5,146735 -7600,5,146520 -7700,5,143595 -7800,5,147275 -7900,5,161082 -8000,5,140291 -8100,5,150511 -8200,5,142531 -8300,5,145412 -8400,5,144341 -8500,5,139353 -8600,5,147710 -8700,5,143884 -8800,5,142612 -8900,5,150579 -9000,5,143225 -9100,5,144508 -9200,5,146262 -9300,5,148743 -9400,5,140331 -9500,5,144404 -9600,5,146370 -9700,5,137174 -9800,5,145074 -9900,5,141904 -10000,5,135593 -10100,5,140548 -10200,5,145158 -10300,5,141743 -10400,5,145666 -10500,5,143513 -10600,5,139703 -10700,5,147536 -10800,5,140548 -10900,5,141864 -11000,5,144634 -11100,5,143472 -11200,5,135832 -11300,5,139489 -11400,5,139528 -11500,5,143081 -11600,5,141203 -11700,5,143369 -11800,5,139450 -11900,5,148942 -12000,5,149209 -12100,5,143389 -12200,5,142694 -12300,5,139742 -12400,5,138927 -12500,5,149678 -12600,5,141643 -12700,5,148279 -12800,5,135354 -12900,5,137475 -13000,5,136128 -13100,5,141342 -13200,5,142918 -13300,5,144675 -13400,5,146950 -13500,5,143266 -13600,5,138619 -13700,5,154130 -13800,5,145985 -13900,5,145772 -14000,5,141763 -14100,5,150625 -14200,5,148235 -14300,5,148809 -14400,5,138484 -14500,5,137061 -14600,5,139723 -14700,5,144759 -14800,5,139489 -14900,5,148676 -15000,5,139528 -100,7,158453 -200,7,150082 -300,7,162813 -400,7,158102 -500,7,156176 -600,7,149186 -700,7,147797 -800,7,152276 -900,7,151331 -1000,7,151538 -1100,7,151148 -1200,7,153940 -1300,7,151607 -1400,7,149365 -1500,7,157678 -1600,7,150398 -1700,7,150150 -1800,7,155811 -1900,7,141743 -2000,7,140173 -2100,7,158982 -2200,7,159362 -2300,7,150015 -2400,7,146950 -2500,7,151722 -2600,7,154273 -2700,7,161864 -2800,7,152137 -2900,7,147666 -3000,7,145836 -3100,7,143204 -3200,7,147188 -3300,7,142877 -3400,7,152299 -3500,7,150398 -3600,7,156030 -3700,7,158528 -3800,7,162999 -3900,7,147427 -4000,7,153022 -4100,7,152485 -4200,7,146735 -4300,7,156006 -4400,7,147601 -4500,7,151791 -4600,7,154966 -4700,7,158604 -4800,7,159337 -4900,7,148853 -5000,7,145751 -5100,7,146156 -5200,7,158679 -5300,7,157629 -5400,7,145011 -5500,7,154297 -5600,7,162972 -5700,7,152276 -5800,7,165535 -5900,7,156788 -6000,7,155908 -6100,7,148478 -6200,7,158881 -6300,7,155303 -6400,7,163934 -6500,7,157878 -6600,7,160359 -6700,7,154440 -6800,7,163585 -6900,7,164554 -7000,7,146584 -7100,7,145074 -7200,7,157331 -7300,7,153421 -7400,7,154894 -7500,7,147797 -7600,7,153280 -7700,7,157207 -7800,7,159872 -7900,7,152532 -8000,7,168321 -8100,7,154655 -8200,7,150579 -8300,7,165234 -8400,7,153964 -8500,7,155231 -8600,7,151400 -8700,7,159464 -8800,7,160153 -8900,7,148389 -9000,7,164176 -9100,7,158982 -9200,7,156030 -9300,7,159667 -9400,7,151080 -9500,7,153893 -9600,7,161655 -9700,7,143225 -9800,7,155496 -9900,7,153964 -10000,7,158202 -10100,7,157678 -10200,7,151034 -10300,7,152045 -10400,7,151446 -10500,7,160487 -10600,7,165371 -10700,7,159058 -10800,7,153822 -10900,7,153893 -11000,7,154464 -11100,7,154344 -11200,7,145412 -11300,7,153964 -11400,7,151952 -11500,7,143245 -11600,7,146886 -11700,7,154154 -11800,7,155110 -11900,7,163666 -12000,7,150670 -12100,7,148698 -12200,7,143988 -12300,7,140825 -12400,7,162548 -12500,7,146028 -12600,7,144948 -12700,7,139899 -12800,7,154990 -12900,7,150715 -13000,7,148016 -13100,7,142633 -13200,7,146670 -13300,7,158177 -13400,7,149857 -13500,7,153374 -13600,7,143451 -13700,7,144990 -13800,7,146821 -13900,7,146348 -14000,7,145053 -14100,7,149745 -14200,7,151906 -14300,7,151377 -14400,7,153917 -14500,7,154559 -14600,7,157678 -14700,7,151263 -14800,7,160307 -14900,7,152021 -15000,7,151171 -100,9,155811 -200,9,144071 -300,9,154249 -400,9,153609 -500,9,153917 -600,9,157529 -700,9,170852 -800,9,161160 -900,9,148765 -1000,9,161238 -1100,9,154894 -1200,9,152695 -1300,9,167700 -1400,9,171969 -1500,9,159948 -1600,9,167954 -1700,9,155738 -1800,9,158679 -1900,9,166972 -2000,9,155207 -2100,9,172651 -2200,9,159184 -2300,9,154966 -2400,9,155908 -2500,9,158679 -2600,9,176616 -2700,9,169004 -2800,9,154942 -2900,9,147492 -3000,9,157977 -3100,9,163025 -3200,9,149009 -3300,9,158227 -3400,9,168321 -3500,9,158453 -3600,9,156813 -3700,9,148148 -3800,9,169836 -3900,9,161264 -4000,9,166722 -4100,9,150784 -4200,9,153069 -4300,9,159134 -4400,9,157430 -4500,9,158002 -4600,9,163478 -4700,9,146455 -4800,9,158428 -4900,9,153162 -5000,9,151906 -5100,9,166112 -5200,9,161368 -5300,9,155207 -5400,9,154870 -5500,9,149678 -5600,9,159846 -5700,9,152322 -5800,9,161108 -5900,9,166251 -6000,9,139392 -6100,9,168861 -6200,9,148765 -6300,9,152835 -6400,9,155231 -6500,9,146134 -6600,9,149120 -6700,9,152625 -6800,9,159846 -6900,9,165343 -7000,9,160076 -7100,9,163934 -7200,9,171585 -7300,9,159033 -7400,9,164176 -7500,9,150511 -7600,9,156225 -7700,9,146778 -7800,9,157331 -7900,9,162999 -8000,9,161759 -8100,9,158856 -8200,9,159184 -8300,9,157629 -8400,9,148170 -8500,9,166168 -8600,9,160823 -8700,9,148765 -8800,9,142085 -8900,9,152998 -9000,9,150443 -9100,9,155520 -9200,9,152648 -9300,9,159515 -9400,9,157828 -9500,9,147885 -9600,9,154798 -9700,9,165425 -9800,9,155957 -9900,9,148544 -10000,9,142531 -10100,9,165207 -10200,9,171057 -10300,9,159795 -10400,9,152788 -10500,9,161186 -10600,9,151745 -10700,9,159667 -10800,9,156079 -10900,9,146972 -11000,9,163559 -11100,9,167392 -11200,9,156666 -11300,9,165070 -11400,9,161004 -11500,9,156421 -11600,9,164122 -11700,9,157629 -11800,9,161134 -11900,9,149009 -12000,9,166583 -12100,9,161368 -12200,9,156862 -12300,9,168804 -12400,9,151354 -12500,9,164095 -12600,9,150625 -12700,9,151906 -12800,9,169405 -12900,9,163025 -13000,9,153704 -13100,9,160539 -13200,9,158127 -13300,9,154249 -13400,9,153633 -13500,9,165152 -13600,9,162153 -13700,9,161056 -13800,9,162311 -13900,9,158102 -14000,9,162074 -14100,9,160978 -14200,9,173130 -14300,9,167504 -14400,9,157977 -14500,9,146241 -14600,9,159033 -14700,9,152160 -14800,9,153444 -14900,9,160256 -15000,9,145412 -100,11,153491 -200,11,161786 -300,11,171232 -400,11,162390 -500,11,171939 -600,11,148743 -700,11,166583 -800,11,162919 -900,11,158127 -1000,11,155424 -1100,11,166030 -1200,11,168890 -1300,11,159846 -1400,11,165562 -1500,11,166472 -1600,11,164257 -1700,11,159083 -1800,11,156788 -1900,11,165590 -2000,11,171086 -2100,11,160205 -2200,11,160333 -2300,11,161186 -2400,11,152625 -2500,11,162416 -2600,11,154655 -2700,11,142673 -2800,11,154631 -2900,11,167112 -3000,11,149097 -3100,11,154535 -3200,11,156666 -3300,11,156079 -3400,11,165371 -3500,11,157505 -3600,11,158027 -3700,11,157331 -3800,11,160436 -3900,11,167700 -4000,11,165727 -4100,11,155159 -4200,11,155038 -4300,11,168038 -4400,11,169836 -4500,11,163585 -4600,11,166251 -4700,11,163105 -4800,11,160797 -4900,11,160102 -5000,11,161160 -5100,11,163800 -5200,11,164962 -5300,11,154130 -5400,11,161655 -5500,11,165316 -5600,11,169118 -5700,11,166223 -5800,11,166638 -5900,11,151653 -6000,11,158478 -6100,11,170357 -6200,11,162707 -6300,11,153115 -6400,11,164798 -6500,11,150647 -6600,11,163666 -6700,11,169836 -6800,11,161681 -6900,11,161759 -7000,11,161238 -7100,11,169405 -7200,11,168491 -7300,11,160952 -7400,11,163961 -7500,11,160797 -7600,11,156421 -7700,11,175469 -7800,11,165645 -7900,11,157529 -8000,11,167420 -8100,11,164500 -8200,11,154368 -8300,11,159897 -8400,11,156079 -8500,11,162206 -8600,11,160978 -8700,11,160205 -8800,11,154774 -8900,11,144196 -9000,11,163425 -9100,11,159235 -9200,11,159033 -9300,11,164365 -9400,11,166389 -9500,11,160230 -9600,11,153988 -9700,11,160513 -9800,11,151814 -9900,11,156347 -10000,11,164500 -10100,11,161368 -10200,11,158453 -10300,11,169176 -10400,11,164473 -10500,11,165125 -10600,11,166805 -10700,11,165700 -10800,11,167336 -10900,11,156006 -11000,11,171174 -11100,11,159744 -11200,11,171614 -11300,11,167785 -11400,11,166444 -11500,11,168406 -11600,11,167897 -11700,11,165098 -11800,11,155787 -11900,11,163345 -12000,11,171791 -12100,11,160205 -12200,11,162839 -12300,11,164392 -12400,11,159540 -12500,11,160926 -12600,11,155666 -12700,11,162892 -12800,11,154106 -12900,11,154154 -13000,11,164690 -13100,11,161446 -13200,11,158503 -13300,11,161786 -13400,11,154750 -13500,11,168180 -13600,11,155424 -13700,11,154392 -13800,11,166917 -13900,11,166085 -14000,11,166195 -14100,11,153917 -14200,11,158957 -14300,11,158755 -14400,11,155086 -14500,11,165864 -14600,11,156323 -14700,11,160616 -14800,11,164284 -14900,11,164311 -15000,11,175870 -100,13,158252 -200,13,165152 -300,13,157927 -400,13,170823 -500,13,158528 -600,13,168890 -700,13,171350 -800,13,169004 -900,13,169033 -1000,13,159872 -1100,13,166833 -1200,13,166694 -1300,13,170910 -1400,13,161969 -1500,13,182982 -1600,13,162522 -1700,13,174978 -1800,13,168577 -1900,13,163265 -2000,13,165727 -2100,13,165289 -2200,13,153374 -2300,13,152508 -2400,13,165975 -2500,13,161498 -2600,13,160333 -2700,13,159438 -2800,13,154894 -2900,13,157505 -3000,13,159616 -3100,13,160102 -3200,13,151125 -3300,13,149075 -3400,13,169290 -3500,13,163425 -3600,13,163505 -3700,13,172354 -3800,13,162337 -3900,13,165508 -4000,13,157654 -4100,13,169721 -4200,13,169808 -4300,13,155183 -4400,13,161524 -4500,13,173310 -4600,13,164609 -4700,13,167364 -4800,13,164068 -4900,13,164419 -5000,13,171408 -5100,13,155593 -5200,13,154966 -5300,13,159210 -5400,13,172890 -5500,13,163265 -5600,13,164338 -5700,13,164365 -5800,13,168435 -5900,13,178986 -6000,13,169348 -6100,13,159058 -6200,13,162892 -6300,13,171969 -6400,13,173550 -6500,13,159948 -6600,13,167196 -6700,13,152811 -6800,13,167532 -6900,13,163612 -7000,13,167954 -7100,13,170212 -7200,13,168208 -7300,13,168520 -7400,13,162469 -7500,13,164015 -7600,13,166972 -7700,13,156152 -7800,13,166500 -7900,13,160128 -8000,13,174886 -8100,13,165453 -8200,13,146864 -8300,13,163398 -8400,13,162866 -8500,13,154392 -8600,13,164500 -8700,13,157977 -8800,13,156887 -8900,13,167084 -9000,13,176834 -9100,13,169118 -9200,13,150738 -9300,13,156054 -9400,13,165180 -9500,13,156494 -9600,13,149276 -9700,13,152322 -9800,13,167364 -9900,13,159974 -10000,13,151791 -10100,13,163907 -10200,13,166611 -10300,13,162100 -10400,13,158478 -10500,13,159872 -10600,13,161864 -10700,13,153162 -10800,13,155496 -10900,13,152137 -11000,13,147994 -11100,13,165207 -11200,13,163800 -11300,13,158227 -11400,13,158730 -11500,13,168095 -11600,13,167392 -11700,13,162100 -11800,13,170969 -11900,13,164257 -12000,13,166112 -12100,13,163826 -12200,13,158152 -12300,13,155472 -12400,13,164149 -12500,13,150150 -12600,13,164636 -12700,13,166333 -12800,13,159718 -12900,13,174155 -13000,13,172146 -13100,13,160230 -13200,13,164717 -13300,13,163934 -13400,13,170706 -13500,13,168776 -13600,13,165398 -13700,13,165920 -13800,13,163559 -13900,13,160076 -14000,13,159083 -14100,13,160384 -14200,13,164365 -14300,13,165809 -14400,13,160462 -14500,13,153303 -14600,13,170910 -14700,13,164473 -14800,13,164338 -14900,13,157133 -15000,13,171467 -100,15,171526 -200,15,168406 -300,15,168861 -400,15,161681 -500,15,166722 -600,15,160307 -700,15,165261 -800,15,168180 -900,15,160539 -1000,15,168577 -1100,15,162813 -1200,15,169548 -1300,15,155376 -1400,15,169090 -1500,15,156396 -1600,15,164880 -1700,15,186323 -1800,15,158002 -1900,15,162972 -2000,15,162919 -2100,15,168180 -2200,15,169434 -2300,15,155617 -2400,15,165098 -2500,15,166223 -2600,15,162522 -2700,15,161864 -2800,15,162707 -2900,15,167644 -3000,15,171909 -3100,15,168804 -3200,15,176897 -3300,15,171939 -3400,15,163666 -3500,15,161524 -3600,15,168435 -3700,15,153775 -3800,15,168605 -3900,15,167757 -4000,15,160384 -4100,15,169981 -4200,15,176491 -4300,15,174520 -4400,15,164962 -4500,15,170096 -4600,15,171027 -4700,15,164826 -4800,15,169290 -4900,15,170386 -5000,15,156396 -5100,15,165480 -5200,15,154273 -5300,15,151699 -5400,15,168634 -5500,15,159591 -5600,15,162258 -5700,15,156887 -5800,15,161446 -5900,15,176647 -6000,15,169262 -6100,15,161995 -6200,15,160616 -6300,15,170998 -6400,15,169348 -6500,15,176056 -6600,15,159235 -6700,15,167926 -6800,15,162390 -6900,15,158252 -7000,15,161550 -7100,15,158780 -7200,15,170096 -7300,15,166417 -7400,15,161368 -7500,15,155666 -7600,15,166057 -7700,15,164880 -7800,15,170910 -7900,15,161030 -8000,15,171791 -8100,15,160384 -8200,15,160256 -8300,15,174125 -8400,15,169319 -8500,15,167028 -8600,15,163345 -8700,15,166030 -8800,15,169176 -8900,15,169319 -9000,15,172771 -9100,15,162999 -9200,15,162153 -9300,15,176772 -9400,15,155666 -9500,15,161890 -9600,15,158881 -9700,15,162786 -9800,15,167841 -9900,15,170648 -10000,15,166085 -10100,15,164176 -10200,15,160797 -10300,15,166500 -10400,15,169405 -10500,15,158102 -10600,15,169606 -10700,15,177777 -10800,15,167644 -10900,15,177147 -11000,15,168605 -11100,15,162733 -11200,15,175438 -11300,15,177904 -11400,15,166889 -11500,15,165098 -11600,15,171880 -11700,15,161759 -11800,15,164690 -11900,15,172384 -12000,15,165207 -12100,15,162786 -12200,15,168350 -12300,15,164392 -12400,15,163452 -12500,15,159184 -12600,15,154344 -12700,15,160952 -12800,15,170794 -12900,15,155617 -13000,15,170096 -13100,15,175561 -13200,15,163559 -13300,15,156176 -13400,15,169548 -13500,15,162972 -13600,15,167420 -13700,15,169923 -13800,15,170619 -13900,15,166195 -14000,15,172681 -14100,15,169606 -14200,15,167954 -14300,15,156666 -14400,15,152021 -14500,15,161655 -14600,15,155279 -14700,15,169290 -14800,15,162999 -14900,15,166030 -15000,15,162364 -100,17,171262 -200,17,162179 -300,17,168463 -400,17,160282 -500,17,174398 -600,17,170328 -700,17,160668 -800,17,175746 -900,17,168350 -1000,17,170823 -1100,17,160307 -1200,17,174003 -1300,17,169348 -1400,17,165398 -1500,17,160797 -1600,17,168634 -1700,17,162786 -1800,17,172235 -1900,17,154535 -2000,17,161969 -2100,17,153798 -2200,17,163398 -2300,17,158805 -2400,17,171939 -2500,17,172503 -2600,17,157728 -2700,17,162946 -2800,17,162919 -2900,17,150534 -3000,17,156641 -3100,17,164230 -3200,17,165152 -3300,17,157529 -3400,17,173490 -3500,17,162946 -3600,17,159667 -3700,17,164068 -3800,17,163052 -3900,17,162707 -4000,17,156862 -4100,17,166527 -4200,17,164500 -4300,17,167112 -4400,17,161160 -4500,17,177053 -4600,17,174794 -4700,17,164095 -4800,17,163746 -4900,17,175839 -5000,17,164500 -5100,17,171027 -5200,17,176118 -5300,17,165562 -5400,17,163639 -5500,17,164934 -5600,17,170328 -5700,17,170010 -5800,17,154942 -5900,17,158077 -6000,17,168236 -6100,17,170852 -6200,17,174947 -6300,17,156739 -6400,17,153139 -6500,17,167785 -6600,17,153209 -6700,17,177588 -6800,17,167644 -6900,17,162495 -7000,17,178380 -7100,17,170299 -7200,17,176304 -7300,17,159159 -7400,17,170415 -7500,17,162443 -7600,17,162469 -7700,17,171644 -7800,17,165207 -7900,17,176834 -8000,17,166278 -8100,17,166611 -8200,17,160076 -8300,17,170881 -8400,17,159565 -8500,17,182848 -8600,17,170357 -8700,17,164041 -8800,17,162469 -8900,17,171115 -9000,17,163559 -9100,17,151998 -9200,17,162760 -9300,17,161812 -9400,17,164446 -9500,17,162206 -9600,17,164826 -9700,17,172324 -9800,17,157529 -9900,17,164365 -10000,17,163105 -10100,17,165371 -10200,17,167954 -10300,17,158805 -10400,17,175070 -10500,17,162416 -10600,17,174125 -10700,17,167168 -10800,17,162048 -10900,17,161733 -11000,17,168038 -11100,17,155038 -11200,17,169033 -11300,17,172562 -11400,17,166944 -11500,17,165152 -11600,17,170677 -11700,17,167897 -11800,17,164068 -11900,17,173701 -12000,17,157331 -12100,17,173882 -12200,17,163800 -12300,17,166417 -12400,17,163961 -12500,17,161995 -12600,17,161004 -12700,17,164527 -12800,17,151699 -12900,17,156641 -13000,17,171497 -13100,17,156225 -13200,17,154035 -13300,17,163371 -13400,17,160230 -13500,17,166444 -13600,17,169865 -13700,17,171703 -13800,17,163291 -13900,17,155496 -14000,17,171350 -14100,17,165453 -14200,17,171291 -14300,17,169692 -14400,17,163185 -14500,17,161733 -14600,17,161030 -14700,17,160642 -14800,17,163532 -14900,17,162048 -15000,17,174733 -100,19,166085 -200,19,173160 -300,19,171909 -400,19,164609 -500,19,155933 -600,19,161969 -700,19,161759 -800,19,171086 -900,19,174003 -1000,19,161186 -1100,19,161004 -1200,19,173310 -1300,19,170910 -1400,19,168520 -1500,19,173490 -1600,19,168520 -1700,19,161316 -1800,19,162946 -1900,19,160642 -2000,19,170794 -2100,19,158378 -2200,19,154798 -2300,19,166417 -2400,19,169090 -2500,19,159744 -2600,19,174886 -2700,19,159438 -2800,19,167616 -2900,19,163238 -3000,19,165098 -3100,19,165125 -3200,19,165425 -3300,19,174337 -3400,19,169865 -3500,19,163185 -3600,19,162022 -3700,19,164230 -3800,19,166694 -3900,19,154726 -4000,19,159489 -4100,19,160539 -4200,19,158579 -4300,19,175377 -4400,19,162048 -4500,19,163719 -4600,19,164068 -4700,19,153940 -4800,19,165590 -4900,19,171408 -5000,19,166140 -5100,19,165070 -5200,19,173340 -5300,19,161186 -5400,19,155908 -5500,19,165480 -5600,19,160076 -5700,19,174978 -5800,19,171644 -5900,19,168918 -6000,19,161890 -6100,19,162127 -6200,19,169033 -6300,19,163826 -6400,19,161160 -6500,19,165617 -6600,19,170328 -6700,19,166057 -6800,19,161186 -6900,19,166278 -7000,19,157381 -7100,19,165480 -7200,19,177556 -7300,19,177210 -7400,19,168208 -7500,19,164826 -7600,19,169520 -7700,19,162892 -7800,19,160771 -7900,19,169664 -8000,19,162416 -8100,19,168662 -8200,19,156911 -8300,19,164338 -8400,19,165152 -8500,19,173040 -8600,19,158654 -8700,19,163612 -8800,19,163291 -8900,19,163692 -9000,19,156862 -9100,19,152555 -9200,19,175808 -9300,19,164095 -9400,19,156421 -9500,19,157084 -9600,19,162892 -9700,19,169692 -9800,19,153115 -9900,19,172146 -10000,19,162601 -10100,19,169491 -10200,19,166805 -10300,19,163025 -10400,19,165125 -10500,19,155207 -10600,19,163371 -10700,19,178890 -10800,19,160745 -10900,19,149767 -11000,19,156543 -11100,19,167504 -11200,19,173010 -11300,19,170561 -11400,19,170910 -11500,19,169233 -11600,19,165016 -11700,19,168293 -11800,19,164095 -11900,19,159591 -12000,19,159744 -12100,19,153491 -12200,19,161056 -12300,19,163961 -12400,19,166861 -12500,19,155183 -12600,19,160256 -12700,19,166223 -12800,19,164149 -12900,19,177085 -13000,19,165480 -13100,19,172354 -13200,19,163559 -13300,19,167954 -13400,19,170010 -13500,19,166306 -13600,19,177493 -13700,19,156764 -13800,19,163078 -13900,19,169664 -14000,19,166555 -14100,19,174733 -14200,19,158982 -14300,19,165700 -14400,19,153491 -14500,19,164473 -14600,19,155472 -14700,19,154726 -14800,19,155376 -14900,19,166030 -15000,19,159515 - diff --git a/bin/benchmark_metrics/metrics/centralized/pipeline.csv b/bin/benchmark_metrics/metrics/centralized/pipeline.csv deleted file mode 100644 index de373c4d..00000000 --- a/bin/benchmark_metrics/metrics/centralized/pipeline.csv +++ /dev/null @@ -1,1502 +0,0 @@ -concurrency,pool,throughput -100,1,185425 -200,1,186323 -300,1,190150 -400,1,189645 -500,1,182415 -600,1,189645 -700,1,186776 -800,1,186393 -900,1,185494 -1000,1,185219 -1100,1,182982 -1200,1,188536 -1300,1,188857 -1400,1,180115 -1500,1,186985 -1600,1,192678 -1700,1,185185 -1800,1,181554 -1900,1,186358 -2000,1,182548 -2100,1,191277 -2200,1,187899 -2300,1,186950 -2400,1,184638 -2500,1,177967 -2600,1,176335 -2700,1,185666 -2800,1,184569 -2900,1,186011 -3000,1,187546 -3100,1,184842 -3200,1,187511 -3300,1,180733 -3400,1,189717 -3500,1,181983 -3600,1,185356 -3700,1,184331 -3800,1,188005 -3900,1,186219 -4000,1,179340 -4100,1,187582 -4200,1,183452 -4300,1,183418 -4400,1,185116 -4500,1,185873 -4600,1,184706 -4700,1,190839 -4800,1,191828 -4900,1,183823 -5000,1,191497 -5100,1,185185 -5200,1,186462 -5300,1,184128 -5400,1,181653 -5500,1,183519 -5600,1,182448 -5700,1,183083 -5800,1,180180 -5900,1,180310 -6000,1,182581 -6100,1,179791 -6200,1,184467 -6300,1,177999 -6400,1,185563 -6500,1,177367 -6600,1,185977 -6700,1,180472 -6800,1,187300 -6900,1,180864 -7000,1,183857 -7100,1,184195 -7200,1,181818 -7300,1,174246 -7400,1,180440 -7500,1,182949 -7600,1,181950 -7700,1,188928 -7800,1,188253 -7900,1,187125 -8000,1,180603 -8100,1,184740 -8200,1,183722 -8300,1,183688 -8400,1,182149 -8500,1,180733 -8600,1,182781 -8700,1,188394 -8800,1,180147 -8900,1,183688 -9000,1,183250 -9100,1,180342 -9200,1,182548 -9300,1,182215 -9400,1,183722 -9500,1,183891 -9600,1,184604 -9700,1,183553 -9800,1,183891 -9900,1,178794 -10000,1,187055 -10100,1,180245 -10200,1,178667 -10300,1,184297 -10400,1,187687 -10500,1,178316 -10600,1,177999 -10700,1,181290 -10800,1,177777 -10900,1,182581 -11000,1,184604 -11100,1,182848 -11200,1,181422 -11300,1,183250 -11400,1,176366 -11500,1,184706 -11600,1,175994 -11700,1,176803 -11800,1,182949 -11900,1,177588 -12000,1,181455 -12100,1,183016 -12200,1,182049 -12300,1,184945 -12400,1,184060 -12500,1,180995 -12600,1,177935 -12700,1,180147 -12800,1,182715 -12900,1,181389 -13000,1,178986 -13100,1,185528 -13200,1,178667 -13300,1,184569 -13400,1,181653 -13500,1,183183 -13600,1,182448 -13700,1,182381 -13800,1,176709 -13900,1,185494 -14000,1,179307 -14100,1,183722 -14200,1,188750 -14300,1,183654 -14400,1,183016 -14500,1,178826 -14600,1,175623 -14700,1,181620 -14800,1,180864 -14900,1,181422 -15000,1,180310 -100,3,673854 -200,3,638569 -300,3,660938 -400,3,621504 -500,3,633713 -600,3,639795 -700,3,623052 -800,3,642260 -900,3,644329 -1000,3,642260 -1100,3,619195 -1200,3,631711 -1300,3,648929 -1400,3,626959 -1500,3,619195 -1600,3,679809 -1700,3,644329 -1800,3,661375 -1900,3,640614 -2000,3,670241 -2100,3,646412 -2200,3,657462 -2300,3,685871 -2400,3,641436 -2500,3,654022 -2600,3,654450 -2700,3,637348 -2800,3,650618 -2900,3,679347 -3000,3,646830 -3100,3,654450 -3200,3,646412 -3300,3,615763 -3400,3,640204 -3500,3,649772 -3600,3,648929 -3700,3,632111 -3800,3,644329 -3900,3,652741 -4000,3,653594 -4100,3,669792 -4200,3,620732 -4300,3,641025 -4400,3,648088 -4500,3,650618 -4600,3,641436 -4700,3,658761 -4800,3,686341 -4900,3,660501 -5000,3,661813 -5100,3,666666 -5200,3,644329 -5300,3,645994 -5400,3,642260 -5500,3,618811 -5600,3,624609 -5700,3,625782 -5800,3,633713 -5900,3,625390 -6000,3,651890 -6100,3,631711 -6200,3,634115 -6300,3,645577 -6400,3,651041 -6500,3,640204 -6600,3,631711 -6700,3,654450 -6800,3,647249 -6900,3,643086 -7000,3,654878 -7100,3,624219 -7200,3,659195 -7300,3,665335 -7400,3,654878 -7500,3,687757 -7600,3,631313 -7700,3,652741 -7800,3,637348 -7900,3,627352 -8000,3,628140 -8100,3,645994 -8200,3,652741 -8300,3,653167 -8400,3,641848 -8500,3,636132 -8600,3,623830 -8700,3,646830 -8800,3,645994 -8900,3,642673 -9000,3,632111 -9100,3,658327 -9200,3,610873 -9300,3,641436 -9400,3,648508 -9500,3,623441 -9600,3,659195 -9700,3,651041 -9800,3,640614 -9900,3,632511 -10000,3,636942 -10100,3,648088 -10200,3,605326 -10300,3,625000 -10400,3,631711 -10500,3,650195 -10600,3,645994 -10700,3,632511 -10800,3,638977 -10900,3,626959 -11000,3,645577 -11100,3,644745 -11200,3,629722 -11300,3,623052 -11400,3,629326 -11500,3,612745 -11600,3,634115 -11700,3,623052 -11800,3,628140 -11900,3,623830 -12000,3,625782 -12100,3,611620 -12200,3,621890 -12300,3,626959 -12400,3,632511 -12500,3,610500 -12600,3,622665 -12700,3,633713 -12800,3,628140 -12900,3,630119 -13000,3,627352 -13100,3,614628 -13200,3,635324 -13300,3,636132 -13400,3,641025 -13500,3,651465 -13600,3,627746 -13700,3,619962 -13800,3,648929 -13900,3,640614 -14000,3,635727 -14100,3,634115 -14200,3,626566 -14300,3,600961 -14400,3,618429 -14500,3,633312 -14600,3,628930 -14700,3,615384 -14800,3,624219 -14900,3,630119 -15000,3,618811 -100,5,837520 -200,5,1054852 -300,5,1070663 -400,5,1112347 -500,5,1082251 -600,5,1127395 -700,5,1055966 -800,5,1104972 -900,5,1075268 -1000,5,1094091 -1100,5,1061571 -1200,5,1161440 -1300,5,1112347 -1400,5,1176470 -1500,5,1107419 -1600,5,1137656 -1700,5,1102535 -1800,5,1179245 -1900,5,1063829 -2000,5,1108647 -2100,5,1071811 -2200,5,1162790 -2300,5,1114827 -2400,5,1173708 -2500,5,1102535 -2600,5,1084598 -2700,5,1079913 -2800,5,1078748 -2900,5,1121076 -3000,5,1066098 -3100,5,1095290 -3200,5,1066098 -3300,5,1059322 -3400,5,1124859 -3500,5,1137656 -3600,5,1116071 -3700,5,1152073 -3800,5,1129943 -3900,5,1152073 -4000,5,1145475 -4100,5,1156069 -4200,5,1117318 -4300,5,1104972 -4400,5,1111111 -4500,5,1081081 -4600,5,1089324 -4700,5,1067235 -4800,5,1104972 -4900,5,1146788 -5000,5,1053740 -5100,5,1121076 -5200,5,1086956 -5300,5,1071811 -5400,5,1038421 -5500,5,1048218 -5600,5,1149425 -5700,5,1091703 -5800,5,1088139 -5900,5,1175088 -6000,5,1135073 -6100,5,1098901 -6200,5,1129943 -6300,5,1127395 -6400,5,1166861 -6500,5,1079913 -6600,5,1144164 -6700,5,1118568 -6800,5,1133786 -6900,5,1154734 -7000,5,1191895 -7100,5,1156069 -7200,5,1129943 -7300,5,1119820 -7400,5,1126126 -7500,5,1101321 -7600,5,1166861 -7700,5,1170960 -7800,5,1149425 -7900,5,1150747 -8000,5,1119820 -8100,5,1175088 -8200,5,1129943 -8300,5,1107419 -8400,5,1128668 -8500,5,1054852 -8600,5,1096491 -8700,5,1117318 -8800,5,1111111 -8900,5,1082251 -9000,5,1090512 -9100,5,1123595 -9200,5,1162790 -9300,5,1075268 -9400,5,1161440 -9500,5,1156069 -9600,5,1118568 -9700,5,1121076 -9800,5,1118568 -9900,5,1153402 -10000,5,1153402 -10100,5,1141552 -10200,5,1141552 -10300,5,1154734 -10400,5,1194743 -10500,5,1179245 -10600,5,1222493 -10700,5,1176470 -10800,5,1308900 -10900,5,1132502 -11000,5,1173708 -11100,5,1129943 -11200,5,1102535 -11300,5,1113585 -11400,5,1157407 -11500,5,1124859 -11600,5,1106194 -11700,5,1141552 -11800,5,1132502 -11900,5,1194743 -12000,5,1097694 -12100,5,1216545 -12200,5,1162790 -12300,5,1129943 -12400,5,1236093 -12500,5,1128668 -12600,5,1128668 -12700,5,1133786 -12800,5,1210653 -12900,5,1168224 -13000,5,1177856 -13100,5,1137656 -13200,5,1161440 -13300,5,1219512 -13400,5,1069518 -13500,5,1199040 -13600,5,1109877 -13700,5,1088139 -13800,5,1201923 -13900,5,1090512 -14000,5,1122334 -14100,5,1111111 -14200,5,1182033 -14300,5,1137656 -14400,5,1119820 -14500,5,1138952 -14600,5,1175088 -14700,5,1160092 -14800,5,1082251 -14900,5,1176470 -15000,5,1103752 -100,7,690607 -200,7,1078748 -300,7,1222493 -400,7,1277139 -500,7,1243781 -600,7,1336898 -700,7,1287001 -800,7,1385041 -900,7,1366120 -1000,7,1410437 -1100,7,1298701 -1200,7,1440922 -1300,7,1351351 -1400,7,1506024 -1500,7,1434720 -1600,7,1531393 -1700,7,1494768 -1800,7,1526717 -1900,7,1497005 -2000,7,1572327 -2100,7,1464128 -2200,7,1543209 -2300,7,1355013 -2400,7,1547987 -2500,7,1564945 -2600,7,1587301 -2700,7,1543209 -2800,7,1574803 -2900,7,1517450 -3000,7,1607717 -3100,7,1481481 -3200,7,1594896 -3300,7,1567398 -3400,7,1642036 -3500,7,1618122 -3600,7,1626016 -3700,7,1543209 -3800,7,1620745 -3900,7,1587301 -4000,7,1579778 -4100,7,1639344 -4200,7,1718213 -4300,7,1631321 -4400,7,1661129 -4500,7,1712328 -4600,7,1683501 -4700,7,1623376 -4800,7,1680672 -4900,7,1703577 -5000,7,1392757 -5100,7,1706484 -5200,7,1642036 -5300,7,1647446 -5400,7,1655629 -5500,7,1589825 -5600,7,1661129 -5700,7,1689189 -5800,7,1733102 -5900,7,1612903 -6000,7,1529051 -6100,7,1736111 -6200,7,1661129 -6300,7,1718213 -6400,7,1631321 -6500,7,1721170 -6600,7,1666666 -6700,7,1677852 -6800,7,1592356 -6900,7,1680672 -7000,7,1607717 -7100,7,1692047 -7200,7,1633986 -7300,7,1675041 -7400,7,1639344 -7500,7,1703577 -7600,7,1531393 -7700,7,1715265 -7800,7,1642036 -7900,7,1697792 -8000,7,1602564 -8100,7,1680672 -8200,7,1615508 -8300,7,1697792 -8400,7,1612903 -8500,7,1697792 -8600,7,1607717 -8700,7,1715265 -8800,7,1642036 -8900,7,1715265 -9000,7,1669449 -9100,7,1739130 -9200,7,1639344 -9300,7,1692047 -9400,7,1434720 -9500,7,1675041 -9600,7,1605136 -9700,7,1661129 -9800,7,1607717 -9900,7,1712328 -10000,7,1626016 -10100,7,1689189 -10200,7,1626016 -10300,7,1666666 -10400,7,1623376 -10500,7,1703577 -10600,7,1615508 -10700,7,1628664 -10800,7,1512859 -10900,7,1703577 -11000,7,1547987 -11100,7,1647446 -11200,7,1461988 -11300,7,1647446 -11400,7,1547987 -11500,7,1633986 -11600,7,1550387 -11700,7,1683501 -11800,7,1602564 -11900,7,1642036 -12000,7,1485884 -12100,7,1675041 -12200,7,1597444 -12300,7,1633986 -12400,7,1540832 -12500,7,1658374 -12600,7,1574803 -12700,7,1666666 -12800,7,1529051 -12900,7,1639344 -13000,7,1560062 -13100,7,1644736 -13200,7,1531393 -13300,7,1658374 -13400,7,1579778 -13500,7,1620745 -13600,7,1587301 -13700,7,1620745 -13800,7,1453488 -13900,7,1628664 -14000,7,1574803 -14100,7,1602564 -14200,7,1552795 -14300,7,1597444 -14400,7,1529051 -14500,7,1644736 -14600,7,1564945 -14700,7,1612903 -14800,7,1547987 -14900,7,1650165 -15000,7,1547987 -100,9,597014 -200,9,881057 -300,9,1076426 -400,9,1240694 -500,9,1225490 -600,9,1251564 -700,9,1364256 -800,9,1326259 -900,9,1434720 -1000,9,1396648 -1100,9,1406469 -1200,9,1457725 -1300,9,1531393 -1400,9,1466275 -1500,9,1540832 -1600,9,1485884 -1700,9,1555209 -1800,9,1572327 -1900,9,1550387 -2000,9,1600000 -2100,9,1589825 -2200,9,1612903 -2300,9,1522070 -2400,9,1610305 -2500,9,1597444 -2600,9,1639344 -2700,9,1538461 -2800,9,1655629 -2900,9,1562500 -3000,9,1623376 -3100,9,1543209 -3200,9,1633986 -3300,9,1615508 -3400,9,1639344 -3500,9,1536098 -3600,9,1647446 -3700,9,1594896 -3800,9,1675041 -3900,9,1584786 -4000,9,1652892 -4100,9,1633986 -4200,9,1669449 -4300,9,1597444 -4400,9,1700680 -4500,9,1631321 -4600,9,1709401 -4700,9,1647446 -4800,9,1689189 -4900,9,1626016 -5000,9,1672240 -5100,9,1557632 -5200,9,1694915 -5300,9,1647446 -5400,9,1709401 -5500,9,1623376 -5600,9,1677852 -5700,9,1416430 -5800,9,1689189 -5900,9,1636661 -6000,9,1666666 -6100,9,1672240 -6200,9,1715265 -6300,9,1663893 -6400,9,1663893 -6500,9,1677852 -6600,9,1718213 -6700,9,1655629 -6800,9,1694915 -6900,9,1683501 -7000,9,1706484 -7100,9,1615508 -7200,9,1715265 -7300,9,1385041 -7400,9,1706484 -7500,9,1628664 -7600,9,1709401 -7700,9,1669449 -7800,9,1739130 -7900,9,1642036 -8000,9,1733102 -8100,9,1663893 -8200,9,1724137 -8300,9,1636661 -8400,9,1718213 -8500,9,1388888 -8600,9,1706484 -8700,9,1623376 -8800,9,1736111 -8900,9,1406469 -9000,9,1724137 -9100,9,1639344 -9200,9,1709401 -9300,9,1675041 -9400,9,1709401 -9500,9,1626016 -9600,9,1718213 -9700,9,1650165 -9800,9,1669449 -9900,9,1623376 -10000,9,1709401 -10100,9,1400560 -10200,9,1663893 -10300,9,1642036 -10400,9,1633986 -10500,9,1636661 -10600,9,1683501 -10700,9,1639344 -10800,9,1692047 -10900,9,1579778 -11000,9,1697792 -11100,9,1592356 -11200,9,1706484 -11300,9,1636661 -11400,9,1683501 -11500,9,1508295 -11600,9,1672240 -11700,9,1626016 -11800,9,1686340 -11900,9,1639344 -12000,9,1712328 -12100,9,1628664 -12200,9,1677852 -12300,9,1552795 -12400,9,1628664 -12500,9,1610305 -12600,9,1612903 -12700,9,1379310 -12800,9,1631321 -12900,9,1597444 -13000,9,1672240 -13100,9,1584786 -13200,9,1650165 -13300,9,1612903 -13400,9,1677852 -13500,9,1589825 -13600,9,1642036 -13700,9,1607717 -13800,9,1647446 -13900,9,1615508 -14000,9,1650165 -14100,9,1592356 -14200,9,1636661 -14300,9,1562500 -14400,9,1655629 -14500,9,1587301 -14600,9,1620745 -14700,9,1364256 -14800,9,1647446 -14900,9,1582278 -15000,9,1618122 -100,11,531067 -200,11,864304 -300,11,1031991 -400,11,1135073 -500,11,1193317 -600,11,1264222 -700,11,1262626 -800,11,1353179 -900,11,1406469 -1000,11,1335113 -1100,11,1438848 -1200,11,1398601 -1300,11,1451378 -1400,11,1432664 -1500,11,1488095 -1600,11,1453488 -1700,11,1515151 -1800,11,1474926 -1900,11,1538461 -2000,11,1287001 -2100,11,1557632 -2200,11,1499250 -2300,11,1594896 -2400,11,1550387 -2500,11,1615508 -2600,11,1524390 -2700,11,1639344 -2800,11,1569858 -2900,11,1633986 -3000,11,1540832 -3100,11,1666666 -3200,11,1574803 -3300,11,1663893 -3400,11,1545595 -3500,11,1647446 -3600,11,1600000 -3700,11,1675041 -3800,11,1612903 -3900,11,1669449 -4000,11,1597444 -4100,11,1706484 -4200,11,1579778 -4300,11,1700680 -4400,11,1631321 -4500,11,1689189 -4600,11,1628664 -4700,11,1700680 -4800,11,1626016 -4900,11,1694915 -5000,11,1628664 -5100,11,1700680 -5200,11,1628664 -5300,11,1663893 -5400,11,1620745 -5500,11,1700680 -5600,11,1607717 -5700,11,1677852 -5800,11,1618122 -5900,11,1686340 -6000,11,1466275 -6100,11,1712328 -6200,11,1597444 -6300,11,1675041 -6400,11,1569858 -6500,11,1647446 -6600,11,1610305 -6700,11,1666666 -6800,11,1574803 -6900,11,1658374 -7000,11,1724137 -7100,11,1642036 -7200,11,1730103 -7300,11,1652892 -7400,11,1697792 -7500,11,1644736 -7600,11,1700680 -7700,11,1683501 -7800,11,1683501 -7900,11,1628664 -8000,11,1712328 -8100,11,1479289 -8200,11,1745200 -8300,11,1697792 -8400,11,1712328 -8500,11,1689189 -8600,11,1712328 -8700,11,1639344 -8800,11,1706484 -8900,11,1658374 -9000,11,1733102 -9100,11,1697792 -9200,11,1754385 -9300,11,1689189 -9400,11,1718213 -9500,11,1628664 -9600,11,1721170 -9700,11,1683501 -9800,11,1739130 -9900,11,1631321 -10000,11,1721170 -10100,11,1633986 -10200,11,1718213 -10300,11,1620745 -10400,11,1706484 -10500,11,1658374 -10600,11,1718213 -10700,11,1618122 -10800,11,1712328 -10900,11,1636661 -11000,11,1721170 -11100,11,1602564 -11200,11,1703577 -11300,11,1402524 -11400,11,1703577 -11500,11,1594896 -11600,11,1736111 -11700,11,1663893 -11800,11,1683501 -11900,11,1572327 -12000,11,1712328 -12100,11,1633986 -12200,11,1694915 -12300,11,1683501 -12400,11,1700680 -12500,11,1572327 -12600,11,1712328 -12700,11,1589825 -12800,11,1727115 -12900,11,1615508 -13000,11,1677852 -13100,11,1587301 -13200,11,1666666 -13300,11,1631321 -13400,11,1655629 -13500,11,1305483 -13600,11,1647446 -13700,11,1597444 -13800,11,1677852 -13900,11,1557632 -14000,11,1663893 -14100,11,1589825 -14200,11,1683501 -14300,11,1529051 -14400,11,1663893 -14500,11,1602564 -14600,11,1677852 -14700,11,1512859 -14800,11,1652892 -14900,11,1550387 -15000,11,1689189 -100,13,438788 -200,13,775795 -300,13,937207 -400,13,989119 -500,13,1077586 -600,13,1189060 -700,13,1204819 -800,13,1321003 -900,13,1300390 -1000,13,1377410 -1100,13,1367989 -1200,13,1426533 -1300,13,1408450 -1400,13,1445086 -1500,13,1390820 -1600,13,1492537 -1700,13,1508295 -1800,13,1360544 -1900,13,1526717 -2000,13,1474926 -2100,13,1550387 -2200,13,1488095 -2300,13,1557632 -2400,13,1390820 -2500,13,1569858 -2600,13,1492537 -2700,13,1612903 -2800,13,1287001 -2900,13,1615508 -3000,13,1474926 -3100,13,1612903 -3200,13,1522070 -3300,13,1618122 -3400,13,1497005 -3500,13,1633986 -3600,13,1587301 -3700,13,1628664 -3800,13,1519756 -3900,13,1626016 -4000,13,1584786 -4100,13,1626016 -4200,13,1490312 -4300,13,1623376 -4400,13,1600000 -4500,13,1650165 -4600,13,1543209 -4700,13,1658374 -4800,13,1582278 -4900,13,1666666 -5000,13,1567398 -5100,13,1715265 -5200,13,1675041 -5300,13,1631321 -5400,13,1697792 -5500,13,1677852 -5600,13,1697792 -5700,13,1689189 -5800,13,1680672 -5900,13,1636661 -6000,13,1692047 -6100,13,1647446 -6200,13,1680672 -6300,13,1628664 -6400,13,1680672 -6500,13,1669449 -6600,13,1692047 -6700,13,1636661 -6800,13,1675041 -6900,13,1557632 -7000,13,1669449 -7100,13,1633986 -7200,13,1706484 -7300,13,1607717 -7400,13,1718213 -7500,13,1626016 -7600,13,1655629 -7700,13,1644736 -7800,13,1683501 -7900,13,1683501 -8000,13,1694915 -8100,13,1658374 -8200,13,1709401 -8300,13,1639344 -8400,13,1666666 -8500,13,1644736 -8600,13,1680672 -8700,13,1658374 -8800,13,1721170 -8900,13,1642036 -9000,13,1680672 -9100,13,1503759 -9200,13,1692047 -9300,13,1577287 -9400,13,1715265 -9500,13,1388888 -9600,13,1697792 -9700,13,1550387 -9800,13,1700680 -9900,13,1677852 -10000,13,1703577 -10100,13,1633986 -10200,13,1709401 -10300,13,1615508 -10400,13,1689189 -10500,13,1567398 -10600,13,1677852 -10700,13,1639344 -10800,13,1697792 -10900,13,1562500 -11000,13,1672240 -11100,13,1623376 -11200,13,1675041 -11300,13,1562500 -11400,13,1712328 -11500,13,1644736 -11600,13,1666666 -11700,13,1574803 -11800,13,1661129 -11900,13,1658374 -12000,13,1552795 -12100,13,1666666 -12200,13,1686340 -12300,13,1650165 -12400,13,1666666 -12500,13,1642036 -12600,13,1683501 -12700,13,1626016 -12800,13,1666666 -12900,13,1677852 -13000,13,1669449 -13100,13,1594896 -13200,13,1666666 -13300,13,1663893 -13400,13,1692047 -13500,13,1610305 -13600,13,1658374 -13700,13,1631321 -13800,13,1669449 -13900,13,1600000 -14000,13,1639344 -14100,13,1597444 -14200,13,1680672 -14300,13,1555209 -14400,13,1628664 -14500,13,1519756 -14600,13,1672240 -14700,13,1557632 -14800,13,1661129 -14900,13,1506024 -15000,13,1663893 -100,15,400160 -200,15,695894 -300,15,851063 -400,15,926784 -500,15,1012145 -600,15,1101321 -700,15,1187648 -800,15,1257861 -900,15,1225490 -1000,15,1340482 -1100,15,1356852 -1200,15,1412429 -1300,15,1213592 -1400,15,1430615 -1500,15,1400560 -1600,15,1483679 -1700,15,1503759 -1800,15,1479289 -1900,15,1522070 -2000,15,1434720 -2100,15,1555209 -2200,15,1381215 -2300,15,1560062 -2400,15,1529051 -2500,15,1582278 -2600,15,1557632 -2700,15,1600000 -2800,15,1555209 -2900,15,1618122 -3000,15,1545595 -3100,15,1615508 -3200,15,1543209 -3300,15,1623376 -3400,15,1529051 -3500,15,1607717 -3600,15,1470588 -3700,15,1615508 -3800,15,1562500 -3900,15,1661129 -4000,15,1543209 -4100,15,1666666 -4200,15,1620745 -4300,15,1639344 -4400,15,1512859 -4500,15,1626016 -4600,15,1582278 -4700,15,1626016 -4800,15,1547987 -4900,15,1652892 -5000,15,1610305 -5100,15,1647446 -5200,15,1512859 -5300,15,1663893 -5400,15,1607717 -5500,15,1663893 -5600,15,1538461 -5700,15,1703577 -5800,15,1647446 -5900,15,1652892 -6000,15,1605136 -6100,15,1683501 -6200,15,1666666 -6300,15,1620745 -6400,15,1683501 -6500,15,1633986 -6600,15,1672240 -6700,15,1661129 -6800,15,1672240 -6900,15,1631321 -7000,15,1652892 -7100,15,1584786 -7200,15,1666666 -7300,15,1607717 -7400,15,1666666 -7500,15,1574803 -7600,15,1647446 -7700,15,1605136 -7800,15,1663893 -7900,15,1574803 -8000,15,1697792 -8100,15,1639344 -8200,15,1715265 -8300,15,1631321 -8400,15,1706484 -8500,15,1658374 -8600,15,1715265 -8700,15,1594896 -8800,15,1692047 -8900,15,1642036 -9000,15,1709401 -9100,15,1631321 -9200,15,1700680 -9300,15,1633986 -9400,15,1692047 -9500,15,1597444 -9600,15,1669449 -9700,15,1628664 -9800,15,1718213 -9900,15,1572327 -10000,15,1700680 -10100,15,1642036 -10200,15,1709401 -10300,15,1533742 -10400,15,1697792 -10500,15,1612903 -10600,15,1672240 -10700,15,1602564 -10800,15,1709401 -10900,15,1675041 -11000,15,1721170 -11100,15,1607717 -11200,15,1706484 -11300,15,1647446 -11400,15,1700680 -11500,15,1620745 -11600,15,1703577 -11700,15,1628664 -11800,15,1700680 -11900,15,1557632 -12000,15,1683501 -12100,15,1633986 -12200,15,1661129 -12300,15,1600000 -12400,15,1703577 -12500,15,1602564 -12600,15,1680672 -12700,15,1562500 -12800,15,1666666 -12900,15,1607717 -13000,15,1689189 -13100,15,1602564 -13200,15,1650165 -13300,15,1650165 -13400,15,1669449 -13500,15,1519756 -13600,15,1658374 -13700,15,1636661 -13800,15,1675041 -13900,15,1577287 -14000,15,1686340 -14100,15,1597444 -14200,15,1658374 -14300,15,1569858 -14400,15,1650165 -14500,15,1540832 -14600,15,1647446 -14700,15,1543209 -14800,15,1647446 -14900,15,1574803 -15000,15,1663893 -100,17,365630 -200,17,549450 -300,17,759878 -400,17,846740 -500,17,934579 -600,17,1052631 -700,17,1097694 -800,17,1194743 -900,17,1218026 -1000,17,1291989 -1100,17,1291989 -1200,17,1310615 -1300,17,1364256 -1400,17,1392757 -1500,17,1414427 -1600,17,1355013 -1700,17,1440922 -1800,17,1246882 -1900,17,1472754 -2000,17,1410437 -2100,17,1510574 -2200,17,1477104 -2300,17,1531393 -2400,17,1412429 -2500,17,1543209 -2600,17,1455604 -2700,17,1522070 -2800,17,1543209 -2900,17,1562500 -3000,17,1488095 -3100,17,1569858 -3200,17,1543209 -3300,17,1587301 -3400,17,1522070 -3500,17,1602564 -3600,17,1526717 -3700,17,1605136 -3800,17,1503759 -3900,17,1602564 -4000,17,1555209 -4100,17,1620745 -4200,17,1582278 -4300,17,1620745 -4400,17,1587301 -4500,17,1628664 -4600,17,1597444 -4700,17,1639344 -4800,17,1612903 -4900,17,1647446 -5000,17,1488095 -5100,17,1620745 -5200,17,1572327 -5300,17,1543209 -5400,17,1650165 -5500,17,1582278 -5600,17,1633986 -5700,17,1569858 -5800,17,1672240 -5900,17,1626016 -6000,17,1658374 -6100,17,1577287 -6200,17,1658374 -6300,17,1572327 -6400,17,1689189 -6500,17,1597444 -6600,17,1694915 -6700,17,1647446 -6800,17,1697792 -6900,17,1587301 -7000,17,1694915 -7100,17,1398601 -7200,17,1689189 -7300,17,1600000 -7400,17,1683501 -7500,17,1438848 -7600,17,1692047 -7700,17,1582278 -7800,17,1697792 -7900,17,1414427 -8000,17,1706484 -8100,17,1602564 -8200,17,1718213 -8300,17,1398601 -8400,17,1675041 -8500,17,1602564 -8600,17,1677852 -8700,17,1319261 -8800,17,1680672 -8900,17,1564945 -9000,17,1697792 -9100,17,1652892 -9200,17,1683501 -9300,17,1607717 -9400,17,1689189 -9500,17,1623376 -9600,17,1666666 -9700,17,1623376 -9800,17,1680672 -9900,17,1607717 -10000,17,1677852 -10100,17,1602564 -10200,17,1650165 -10300,17,1618122 -10400,17,1683501 -10500,17,1584786 -10600,17,1666666 -10700,17,1428571 -10800,17,1689189 -10900,17,1587301 -11000,17,1647446 -11100,17,1398601 -11200,17,1689189 -11300,17,1628664 -11400,17,1655629 -11500,17,1356852 -11600,17,1680672 -11700,17,1642036 -11800,17,1663893 -11900,17,1355013 -12000,17,1650165 -12100,17,1652892 -12200,17,1658374 -12300,17,1683501 -12400,17,1633986 -12500,17,1692047 -12600,17,1524390 -12700,17,1650165 -12800,17,1620745 -12900,17,1661129 -13000,17,1319261 -13100,17,1661129 -13200,17,1564945 -13300,17,1663893 -13400,17,1492537 -13500,17,1697792 -13600,17,1615508 -13700,17,1644736 -13800,17,1529051 -13900,17,1686340 -14000,17,1536098 -14100,17,1658374 -14200,17,1432664 -14300,17,1628664 -14400,17,1545595 -14500,17,1636661 -14600,17,1584786 -14700,17,1642036 -14800,17,1550387 -14900,17,1612903 -15000,17,1555209 -100,19,330906 -200,19,565610 -300,19,756429 -400,19,827129 -500,19,916590 -600,19,971817 -700,19,1109877 -800,19,1119820 -900,19,1197604 -1000,19,1189060 -1100,19,1302083 -1200,19,1302083 -1300,19,1355013 -1400,19,1298701 -1500,19,1398601 -1600,19,1400560 -1700,19,1328021 -1800,19,1449275 -1900,19,1400560 -2000,19,1464128 -2100,19,1358695 -2200,19,1481481 -2300,19,1434720 -2400,19,1512859 -2500,19,1438848 -2600,19,1540832 -2700,19,1510574 -2800,19,1555209 -2900,19,1445086 -3000,19,1555209 -3100,19,1522070 -3200,19,1567398 -3300,19,1468428 -3400,19,1592356 -3500,19,1508295 -3600,19,1584786 -3700,19,1494768 -3800,19,1607717 -3900,19,1555209 -4000,19,1605136 -4100,19,1529051 -4200,19,1633986 -4300,19,1560062 -4400,19,1650165 -4500,19,1466275 -4600,19,1658374 -4700,19,1584786 -4800,19,1666666 -4900,19,1369863 -5000,19,1644736 -5100,19,1562500 -5200,19,1658374 -5300,19,1589825 -5400,19,1658374 -5500,19,1594896 -5600,19,1658374 -5700,19,1594896 -5800,19,1675041 -5900,19,1547987 -6000,19,1675041 -6100,19,1577287 -6200,19,1623376 -6300,19,1569858 -6400,19,1644736 -6500,19,1592356 -6600,19,1644736 -6700,19,1584786 -6800,19,1666666 -6900,19,1572327 -7000,19,1669449 -7100,19,1600000 -7200,19,1661129 -7300,19,1633986 -7400,19,1692047 -7500,19,1529051 -7600,19,1680672 -7700,19,1587301 -7800,19,1697792 -7900,19,1639344 -8000,19,1680672 -8100,19,1644736 -8200,19,1706484 -8300,19,1587301 -8400,19,1703577 -8500,19,1623376 -8600,19,1700680 -8700,19,1555209 -8800,19,1703577 -8900,19,1594896 -9000,19,1680672 -9100,19,1557632 -9200,19,1703577 -9300,19,1636661 -9400,19,1700680 -9500,19,1572327 -9600,19,1650165 -9700,19,1605136 -9800,19,1658374 -9900,19,1550387 -10000,19,1669449 -10100,19,1582278 -10200,19,1642036 -10300,19,1582278 -10400,19,1675041 -10500,19,1626016 -10600,19,1686340 -10700,19,1543209 -10800,19,1680672 -10900,19,1605136 -11000,19,1675041 -11100,19,1650165 -11200,19,1672240 -11300,19,1610305 -11400,19,1683501 -11500,19,1709401 -11600,19,1639344 -11700,19,1628664 -11800,19,1652892 -11900,19,1615508 -12000,19,1666666 -12100,19,1620745 -12200,19,1652892 -12300,19,1675041 -12400,19,1677852 -12500,19,1666666 -12600,19,1639344 -12700,19,1647446 -12800,19,1661129 -12900,19,1574803 -13000,19,1672240 -13100,19,1633986 -13200,19,1628664 -13300,19,1547987 -13400,19,1663893 -13500,19,1618122 -13600,19,1626016 -13700,19,1628664 -13800,19,1642036 -13900,19,1538461 -14000,19,1655629 -14100,19,1639344 -14200,19,1620745 -14300,19,1661129 -14400,19,1658374 -14500,19,1587301 -14600,19,1644736 -14700,19,1579778 -14800,19,1666666 -14900,19,1607717 -15000,19,1626016 - diff --git a/bin/benchmark_metrics/metrics/clustered/README.md b/bin/benchmark_metrics/metrics/clustered/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/bin/benchmark_metrics/metrics/clustered/no-pipeline.csv b/bin/benchmark_metrics/metrics/clustered/no-pipeline.csv deleted file mode 100644 index e0959f38..00000000 --- a/bin/benchmark_metrics/metrics/clustered/no-pipeline.csv +++ /dev/null @@ -1,1502 +0,0 @@ -concurrency,pool,throughput -100,1,30497 -200,1,31212 -300,1,27236 -400,1,27110 -500,1,25876 -600,1,28693 -700,1,27597 -800,1,28274 -900,1,26743 -1000,1,27249 -1100,1,27399 -1200,1,26793 -1300,1,27139 -1400,1,27289 -1500,1,27253 -1600,1,26754 -1700,1,28221 -1800,1,26820 -1900,1,28087 -2000,1,26750 -2100,1,26582 -2200,1,26529 -2300,1,29132 -2400,1,28740 -2500,1,28109 -2600,1,26905 -2700,1,27862 -2800,1,27622 -2900,1,27018 -3000,1,26294 -3100,1,26989 -3200,1,28156 -3300,1,29534 -3400,1,27922 -3500,1,27515 -3600,1,26803 -3700,1,27585 -3800,1,27595 -3900,1,28900 -4000,1,27489 -4100,1,27059 -4200,1,27848 -4300,1,27957 -4400,1,26838 -4500,1,30354 -4600,1,27474 -4700,1,27508 -4800,1,27319 -4900,1,27680 -5000,1,28278 -5100,1,27062 -5200,1,27895 -5300,1,27545 -5400,1,27809 -5500,1,27990 -5600,1,29153 -5700,1,27435 -5800,1,27297 -5900,1,27873 -6000,1,27588 -6100,1,27760 -6200,1,29293 -6300,1,27069 -6400,1,27357 -6500,1,27590 -6600,1,28275 -6700,1,27988 -6800,1,29189 -6900,1,29554 -7000,1,28300 -7100,1,28585 -7200,1,27555 -7300,1,29232 -7400,1,27807 -7500,1,28285 -7600,1,25892 -7700,1,26780 -7800,1,26994 -7900,1,26822 -8000,1,27676 -8100,1,28061 -8200,1,26512 -8300,1,26642 -8400,1,28310 -8500,1,27329 -8600,1,28108 -8700,1,27352 -8800,1,27681 -8900,1,27570 -9000,1,28577 -9100,1,29705 -9200,1,26678 -9300,1,25565 -9400,1,26690 -9500,1,26850 -9600,1,28470 -9700,1,27442 -9800,1,30091 -9900,1,26914 -10000,1,29371 -10100,1,27736 -10200,1,27769 -10300,1,28693 -10400,1,27395 -10500,1,27754 -10600,1,27954 -10700,1,29170 -10800,1,27480 -10900,1,30709 -11000,1,26402 -11100,1,27887 -11200,1,26158 -11300,1,28568 -11400,1,28961 -11500,1,27796 -11600,1,28047 -11700,1,27711 -11800,1,26021 -11900,1,27803 -12000,1,30163 -12100,1,27371 -12200,1,27889 -12300,1,27243 -12400,1,27177 -12500,1,28568 -12600,1,27885 -12700,1,27372 -12800,1,27246 -12900,1,26151 -13000,1,27962 -13100,1,27472 -13200,1,28278 -13300,1,26786 -13400,1,28150 -13500,1,27168 -13600,1,27131 -13700,1,27120 -13800,1,27922 -13900,1,29055 -14000,1,28589 -14100,1,28071 -14200,1,28254 -14300,1,28149 -14400,1,27183 -14500,1,27123 -14600,1,27440 -14700,1,28327 -14800,1,26962 -14900,1,27671 -15000,1,27502 -100,3,76622 -200,3,75148 -300,3,75103 -400,3,73768 -500,3,73480 -600,3,75855 -700,3,80762 -800,3,75221 -900,3,74727 -1000,3,78727 -1100,3,75924 -1200,3,72976 -1300,3,74465 -1400,3,74688 -1500,3,76846 -1600,3,74560 -1700,3,75471 -1800,3,73926 -1900,3,73524 -2000,3,74266 -2100,3,76283 -2200,3,71725 -2300,3,72317 -2400,3,74277 -2500,3,74382 -2600,3,78474 -2700,3,74940 -2800,3,74305 -2900,3,74382 -3000,3,74783 -3100,3,74532 -3200,3,77029 -3300,3,72817 -3400,3,74677 -3500,3,76958 -3600,3,75340 -3700,3,77214 -3800,3,72950 -3900,3,72254 -4000,3,72505 -4100,3,72118 -4200,3,73643 -4300,3,71184 -4400,3,75769 -4500,3,77202 -4600,3,73789 -4700,3,78523 -4800,3,74827 -4900,3,73795 -5000,3,71994 -5100,3,73362 -5200,3,76663 -5300,3,74233 -5400,3,74805 -5500,3,73507 -5600,3,75352 -5700,3,79321 -5800,3,72046 -5900,3,75901 -6000,3,73778 -6100,3,76190 -6200,3,73432 -6300,3,73276 -6400,3,77393 -6500,3,73308 -6600,3,74850 -6700,3,76734 -6800,3,74421 -6900,3,76359 -7000,3,75654 -7100,3,74277 -7200,3,73626 -7300,3,73524 -7400,3,71890 -7500,3,72046 -7600,3,72998 -7700,3,73664 -7800,3,76353 -7900,3,80860 -8000,3,76097 -8100,3,74288 -8200,3,74604 -8300,3,72066 -8400,3,75165 -8500,3,75018 -8600,3,72817 -8700,3,73115 -8800,3,71947 -8900,3,72327 -9000,3,72353 -9100,3,76663 -9200,3,73351 -9300,3,76068 -9400,3,74052 -9500,3,72113 -9600,3,72939 -9700,3,73708 -9800,3,73827 -9900,3,75511 -10000,3,76086 -10100,3,73800 -10200,3,74244 -10300,3,73405 -10400,3,74878 -10500,3,72748 -10600,3,75386 -10700,3,72590 -10800,3,73421 -10900,3,73904 -11000,3,73762 -11100,3,74393 -11200,3,74266 -11300,3,71219 -11400,3,73953 -11500,3,75557 -11600,3,74338 -11700,3,74493 -11800,3,74900 -11900,3,73626 -12000,3,74183 -12100,3,74626 -12200,3,74410 -12300,3,72854 -12400,3,74510 -12500,3,74861 -12600,3,78186 -12700,3,78155 -12800,3,73227 -12900,3,73040 -13000,3,74699 -13100,3,76184 -13200,3,71643 -13300,3,74677 -13400,3,73378 -13500,3,77387 -13600,3,75872 -13700,3,72875 -13800,3,72301 -13900,3,73362 -14000,3,72849 -14100,3,73991 -14200,3,74521 -14300,3,75289 -14400,3,71700 -14500,3,74543 -14600,3,76300 -14700,3,73491 -14800,3,75329 -14900,3,77387 -15000,3,72442 -100,5,149320 -200,5,155642 -300,5,153704 -400,5,146584 -500,5,156421 -600,5,152415 -700,5,151400 -800,5,155376 -900,5,150534 -1000,5,152207 -1100,5,148456 -1200,5,153233 -1300,5,154798 -1400,5,151240 -1500,5,152508 -1600,5,152322 -1700,5,151952 -1800,5,155642 -1900,5,149611 -2000,5,154583 -2100,5,146929 -2200,5,154273 -2300,5,152881 -2400,5,149543 -2500,5,153468 -2600,5,150060 -2700,5,148500 -2800,5,148875 -2900,5,148522 -3000,5,151998 -3100,5,147145 -3200,5,149723 -3300,5,149298 -3400,5,145137 -3500,5,149276 -3600,5,145053 -3700,5,147232 -3800,5,154846 -3900,5,150647 -4000,5,153397 -4100,5,148301 -4200,5,146220 -4300,5,153704 -4400,5,154511 -4500,5,153303 -4600,5,150966 -4700,5,152322 -4800,5,152998 -4900,5,150806 -5000,5,152648 -5100,5,151148 -5200,5,148038 -5300,5,147928 -5400,5,154559 -5500,5,147383 -5600,5,145032 -5700,5,147841 -5800,5,153751 -5900,5,150443 -6000,5,152183 -6100,5,148082 -6200,5,154273 -6300,5,147885 -6400,5,147863 -6500,5,154368 -6600,5,145391 -6700,5,146156 -6800,5,149992 -6900,5,151561 -7000,5,149253 -7100,5,148345 -7200,5,151791 -7300,5,153917 -7400,5,146950 -7500,5,147449 -7600,5,152718 -7700,5,147536 -7800,5,150172 -7900,5,150784 -8000,5,150217 -8100,5,153045 -8200,5,148345 -8300,5,153256 -8400,5,148898 -8500,5,153633 -8600,5,147318 -8700,5,147863 -8800,5,149499 -8900,5,151125 -9000,5,153327 -9100,5,149009 -9200,5,146370 -9300,5,155593 -9400,5,147383 -9500,5,147253 -9600,5,145327 -9700,5,146006 -9800,5,148920 -9900,5,152346 -10000,5,151469 -10100,5,148060 -10200,5,154392 -10300,5,146284 -10400,5,150852 -10500,5,147449 -10600,5,145454 -10700,5,152299 -10800,5,151171 -10900,5,153869 -11000,5,148367 -11100,5,147601 -11200,5,149543 -11300,5,153022 -11400,5,151929 -11500,5,151011 -11600,5,153022 -11700,5,148986 -11800,5,153704 -11900,5,152392 -12000,5,152045 -12100,5,145306 -12200,5,148676 -12300,5,152276 -12400,5,153421 -12500,5,154416 -12600,5,151929 -12700,5,144990 -12800,5,149142 -12900,5,154368 -13000,5,147885 -13100,5,149812 -13200,5,147492 -13300,5,155617 -13400,5,150670 -13500,5,141282 -13600,5,152299 -13700,5,149409 -13800,5,153139 -13900,5,149857 -14000,5,148235 -14100,5,148367 -14200,5,147123 -14300,5,150966 -14400,5,152508 -14500,5,148038 -14600,5,149298 -14700,5,149902 -14800,5,145793 -14900,5,149209 -15000,5,152021 -100,7,203624 -200,7,196232 -300,7,193911 -400,7,194325 -500,7,208986 -600,7,201938 -700,7,203873 -800,7,206398 -900,7,208724 -1000,7,204415 -1100,7,195503 -1200,7,204290 -1300,7,209511 -1400,7,197980 -1500,7,203252 -1600,7,191534 -1700,7,194061 -1800,7,194363 -1900,7,206526 -2000,7,210792 -2100,7,202798 -2200,7,199560 -2300,7,203873 -2400,7,204708 -2500,7,207770 -2600,7,196540 -2700,7,209819 -2800,7,206526 -2900,7,206313 -3000,7,208594 -3100,7,200561 -3200,7,194628 -3300,7,207253 -3400,7,212585 -3500,7,210437 -3600,7,197941 -3700,7,200200 -3800,7,202470 -3900,7,204582 -4000,7,207210 -4100,7,204123 -4200,7,202183 -4300,7,196540 -4400,7,204666 -4500,7,205254 -4600,7,205549 -4700,7,197784 -4800,7,199401 -4900,7,200080 -5000,7,207598 -5100,7,204792 -5200,7,200200 -5300,7,206355 -5400,7,201126 -5500,7,205380 -5600,7,203707 -5700,7,206058 -5800,7,197044 -5900,7,204206 -6000,7,206739 -6100,7,204792 -6200,7,206228 -6300,7,213356 -6400,7,204164 -6500,7,202020 -6600,7,207856 -6700,7,202142 -6800,7,203583 -6900,7,200040 -7000,7,204164 -7100,7,195503 -7200,7,198728 -7300,7,208029 -7400,7,202716 -7500,7,190694 -7600,7,203748 -7700,7,202552 -7800,7,208463 -7900,7,206953 -8000,7,204206 -8100,7,199680 -8200,7,208116 -8300,7,193836 -8400,7,195733 -8500,7,198846 -8600,7,201979 -8700,7,211327 -8800,7,212044 -8900,7,204123 -9000,7,206996 -9100,7,201775 -9200,7,192901 -9300,7,189000 -9400,7,204164 -9500,7,210084 -9600,7,203541 -9700,7,211505 -9800,7,206996 -9900,7,202552 -10000,7,202347 -10100,7,206270 -10200,7,198570 -10300,7,195198 -10400,7,195656 -10500,7,195007 -10600,7,202675 -10700,7,198491 -10800,7,197238 -10900,7,202429 -11000,7,197044 -11100,7,195924 -11200,7,193124 -11300,7,196155 -11400,7,209117 -11500,7,199441 -11600,7,201207 -11700,7,206825 -11800,7,210614 -11900,7,200561 -12000,7,200924 -12100,7,210349 -12200,7,212134 -12300,7,208507 -12400,7,192122 -12500,7,191607 -12600,7,192122 -12700,7,193911 -12800,7,204415 -12900,7,207598 -13000,7,207641 -13100,7,203004 -13200,7,201409 -13300,7,204624 -13400,7,204123 -13500,7,198925 -13600,7,208942 -13700,7,209117 -13800,7,200682 -13900,7,199401 -14000,7,208333 -14100,7,212089 -14200,7,202757 -14300,7,209380 -14400,7,201450 -14500,7,191644 -14600,7,196502 -14700,7,200320 -14800,7,212720 -14900,7,202061 -15000,7,210304 -100,9,252908 -200,9,262536 -300,9,253613 -400,9,250626 -500,9,241196 -600,9,246062 -700,9,248323 -800,9,241779 -900,9,243486 -1000,9,249625 -1100,9,244977 -1200,9,251445 -1300,9,254777 -1400,9,236016 -1500,9,253678 -1600,9,253742 -1700,9,256016 -1800,9,253678 -1900,9,255558 -2000,9,236518 -2100,9,242306 -2200,9,230202 -2300,9,248262 -2400,9,258464 -2500,9,263019 -2600,9,244081 -2700,9,246913 -2800,9,247463 -2900,9,246062 -3000,9,252206 -3100,9,242659 -3200,9,242189 -3300,9,238948 -3400,9,240269 -3500,9,236406 -3600,9,253164 -3700,9,248880 -3800,9,238492 -3900,9,254452 -4000,9,248818 -4100,9,245579 -4200,9,254065 -4300,9,250752 -4400,9,243131 -4500,9,249066 -4600,9,254000 -4700,9,244021 -4800,9,243013 -4900,9,245941 -5000,9,249687 -5100,9,257003 -5200,9,259672 -5300,9,246487 -5400,9,236854 -5500,9,234082 -5600,9,241021 -5700,9,233808 -5800,9,241604 -5900,9,243486 -6000,9,244379 -6100,9,249128 -6200,9,246184 -6300,9,243664 -6400,9,248323 -6500,9,251762 -6600,9,251572 -6700,9,251572 -6800,9,252780 -6900,9,255167 -7000,9,247892 -7100,9,255885 -7200,9,251889 -7300,9,254388 -7400,9,244618 -7500,9,253036 -7600,9,258197 -7700,9,259201 -7800,9,250626 -7900,9,248077 -8000,9,239923 -8100,9,243072 -8200,9,250501 -8300,9,241545 -8400,9,227531 -8500,9,235849 -8600,9,245398 -8700,9,251256 -8800,9,257599 -8900,9,252908 -9000,9,241721 -9100,9,252334 -9200,9,255102 -9300,9,251698 -9400,9,244558 -9500,9,248138 -9600,9,244140 -9700,9,235294 -9800,9,235626 -9900,9,241779 -10000,9,239635 -10100,9,244558 -10200,9,247892 -10300,9,237247 -10400,9,237304 -10500,9,245037 -10600,9,243427 -10700,9,248262 -10800,9,248880 -10900,9,250062 -11000,9,245941 -11100,9,232180 -11200,9,247096 -11300,9,251889 -11400,9,252206 -11500,9,249750 -11600,9,248941 -11700,9,246609 -11800,9,251319 -11900,9,264480 -12000,9,241021 -12100,9,235737 -12200,9,244021 -12300,9,248941 -12400,9,254258 -12500,9,256673 -12600,9,253292 -12700,9,255102 -12800,9,244379 -12900,9,245278 -13000,9,254841 -13100,9,248818 -13200,9,250815 -13300,9,247096 -13400,9,248570 -13500,9,260078 -13600,9,250062 -13700,9,248508 -13800,9,230043 -13900,9,238948 -14000,9,253936 -14100,9,258464 -14200,9,256081 -14300,9,243368 -14400,9,237925 -14500,9,234576 -14600,9,259134 -14700,9,257466 -14800,9,247770 -14900,9,236350 -15000,9,244140 -100,11,266951 -200,11,271149 -300,11,282167 -400,11,273672 -500,11,280347 -600,11,286779 -700,11,287026 -800,11,282167 -900,11,279642 -1000,11,278940 -1100,11,270562 -1200,11,287108 -1300,11,291375 -1400,11,289603 -1500,11,289017 -1600,11,286368 -1700,11,288433 -1800,11,276166 -1900,11,283607 -2000,11,282087 -2100,11,280426 -2200,11,285469 -2300,11,274499 -2400,11,286779 -2500,11,278241 -2600,11,281373 -2700,11,280898 -2800,11,273897 -2900,11,269832 -3000,11,268961 -3100,11,288267 -3200,11,274800 -3300,11,284738 -3400,11,282087 -3500,11,290360 -3600,11,275482 -3700,11,270051 -3800,11,277546 -3900,11,282565 -4000,11,280741 -4100,11,274876 -4200,11,277008 -4300,11,287191 -4400,11,290023 -4500,11,283125 -4600,11,276625 -4700,11,285877 -4800,11,284575 -4900,11,291205 -5000,11,291885 -5100,11,272553 -5200,11,280583 -5300,11,282087 -5400,11,273972 -5500,11,268528 -5600,11,270855 -5700,11,271591 -5800,11,278940 -5900,11,272851 -6000,11,279329 -6100,11,283286 -6200,11,260892 -6300,11,271370 -6400,11,272182 -6500,11,269251 -6600,11,279407 -6700,11,276166 -6800,11,264480 -6900,11,276854 -7000,11,276472 -7100,11,287604 -7200,11,285959 -7300,11,266311 -7400,11,283366 -7500,11,277469 -7600,11,289435 -7700,11,292141 -7800,11,280898 -7900,11,261643 -8000,11,274423 -8100,11,280347 -8200,11,284252 -8300,11,283446 -8400,11,286944 -8500,11,262054 -8600,11,281769 -8700,11,274649 -8800,11,286532 -8900,11,276931 -9000,11,287769 -9100,11,272108 -9200,11,273373 -9300,11,287604 -9400,11,276395 -9500,11,275785 -9600,11,278551 -9700,11,283446 -9800,11,273224 -9900,11,284171 -10000,11,269323 -10100,11,270855 -10200,11,286368 -10300,11,293599 -10400,11,290444 -10500,11,285795 -10600,11,285062 -10700,11,274499 -10800,11,276778 -10900,11,280504 -11000,11,276243 -11100,11,276090 -11200,11,271444 -11300,11,262950 -11400,11,277546 -11500,11,287356 -11600,11,281135 -11700,11,271223 -11800,11,267022 -11900,11,273672 -12000,11,275862 -12100,11,277238 -12200,11,277315 -12300,11,294204 -12400,11,272628 -12500,11,273074 -12600,11,273298 -12700,11,266453 -12800,11,271960 -12900,11,266098 -13000,11,279876 -13100,11,292997 -13200,11,282167 -13300,11,285714 -13400,11,286286 -13500,11,286944 -13600,11,285959 -13700,11,291290 -13800,11,270416 -13900,11,287026 -14000,11,281531 -14100,11,276090 -14200,11,284981 -14300,11,286123 -14400,11,291715 -14500,11,275254 -14600,11,285551 -14700,11,282246 -14800,11,271444 -14900,11,278940 -15000,11,282406 -100,13,305903 -200,13,306842 -300,13,300932 -400,13,303674 -500,13,301386 -600,13,297707 -700,13,297176 -800,13,309214 -900,13,305997 -1000,13,297885 -1100,13,295595 -1200,13,307219 -1300,13,300030 -1400,13,310848 -1500,13,316155 -1600,13,308641 -1700,13,301204 -1800,13,307125 -1900,13,294898 -2000,13,306936 -2100,13,307692 -2200,13,299311 -2300,13,300661 -2400,13,287191 -2500,13,302755 -2600,13,309310 -2700,13,296647 -2800,13,303951 -2900,13,312597 -3000,13,315357 -3100,13,313479 -3200,13,311720 -3300,13,301841 -3400,13,302755 -3500,13,292312 -3600,13,293255 -3700,13,298418 -3800,13,306748 -3900,13,309214 -4000,13,307787 -4100,13,308356 -4200,13,308071 -4300,13,292740 -4400,13,313676 -4500,13,304878 -4600,13,308832 -4700,13,297707 -4800,13,307787 -4900,13,297619 -5000,13,303214 -5100,13,299401 -5200,13,299132 -5300,13,312402 -5400,13,318572 -5500,13,313676 -5600,13,298596 -5700,13,305716 -5800,13,316555 -5900,13,304228 -6000,13,314762 -6100,13,307314 -6200,13,297530 -6300,13,294031 -6400,13,297707 -6500,13,299760 -6600,13,318066 -6700,13,315357 -6800,13,306936 -6900,13,308832 -7000,13,307408 -7100,13,295857 -7200,13,296208 -7300,13,279017 -7400,13,302206 -7500,13,295159 -7600,13,297973 -7700,13,296471 -7800,13,299580 -7900,13,304506 -8000,13,295945 -8100,13,292997 -8200,13,276090 -8300,13,297265 -8400,13,299580 -8500,13,314267 -8600,13,296384 -8700,13,310752 -8800,13,301114 -8900,13,290275 -9000,13,299670 -9100,13,289184 -9200,13,303490 -9300,13,302023 -9400,13,294637 -9500,13,288600 -9600,13,310848 -9700,13,312500 -9800,13,314465 -9900,13,311138 -10000,13,317561 -10100,13,300842 -10200,13,304785 -10300,13,304414 -10400,13,313185 -10500,13,301932 -10600,13,290782 -10700,13,299401 -10800,13,299850 -10900,13,304692 -11000,13,304321 -11100,13,313185 -11200,13,311138 -11300,13,305064 -11400,13,308451 -11500,13,306278 -11600,13,296912 -11700,13,291120 -11800,13,295945 -11900,13,303398 -12000,13,311235 -12100,13,307597 -12200,13,307219 -12300,13,311332 -12400,13,296471 -12500,13,305716 -12600,13,307219 -12700,13,309405 -12800,13,295159 -12900,13,306748 -13000,13,309501 -13100,13,306936 -13200,13,309023 -13300,13,303490 -13400,13,302480 -13500,13,305716 -13600,13,299132 -13700,13,312109 -13800,13,309981 -13900,13,313676 -14000,13,306560 -14100,13,312500 -14200,13,304692 -14300,13,303674 -14400,13,298775 -14500,13,307408 -14600,13,318471 -14700,13,307125 -14800,13,296296 -14900,13,290782 -15000,13,298685 -100,15,322061 -200,15,320102 -300,15,315955 -400,15,329380 -500,15,314960 -600,15,324149 -700,15,324569 -800,15,310077 -900,15,311138 -1000,15,312695 -1100,15,324780 -1200,15,320718 -1300,15,326370 -1400,15,308546 -1500,15,316555 -1600,15,318877 -1700,15,322997 -1800,15,318167 -1900,15,328947 -2000,15,317158 -2100,15,330469 -2200,15,326690 -2300,15,325414 -2400,15,323519 -2500,15,317965 -2600,15,323729 -2700,15,325945 -2800,15,324254 -2900,15,329380 -3000,15,321543 -3100,15,324464 -3200,15,338983 -3300,15,327546 -3400,15,320102 -3500,15,304506 -3600,15,316155 -3700,15,320410 -3800,15,334001 -3900,15,320512 -4000,15,322372 -4100,15,319897 -4200,15,306184 -4300,15,317762 -4400,15,318167 -4500,15,317258 -4600,15,317057 -4700,15,317359 -4800,15,321853 -4900,15,333555 -5000,15,323101 -5100,15,334112 -5200,15,328191 -5300,15,318979 -5400,15,330360 -5500,15,317561 -5600,15,318877 -5700,15,327868 -5800,15,327546 -5900,15,301841 -6000,15,311817 -6100,15,320307 -6200,15,333778 -6300,15,323939 -6400,15,330141 -6500,15,307597 -6600,15,314169 -6700,15,330033 -6800,15,316355 -6900,15,315756 -7000,15,324991 -7100,15,318572 -7200,15,314169 -7300,15,311720 -7400,15,319488 -7500,15,314267 -7600,15,311041 -7700,15,312793 -7800,15,321027 -7900,15,310077 -8000,15,307408 -8100,15,310077 -8200,15,319081 -8300,15,314564 -8400,15,333778 -8500,15,321336 -8600,15,304599 -8700,15,313873 -8800,15,321336 -8900,15,322893 -9000,15,308832 -9100,15,327761 -9200,15,329924 -9300,15,315258 -9400,15,307408 -9500,15,318471 -9600,15,312012 -9700,15,319488 -9800,15,329489 -9900,15,326904 -10000,15,337040 -10100,15,331455 -10200,15,316856 -10300,15,306091 -10400,15,312500 -10500,15,313283 -10600,15,313479 -10700,15,336813 -10800,15,325203 -10900,15,315457 -11000,15,328191 -11100,15,313381 -11200,15,313774 -11300,15,317460 -11400,15,308546 -11500,15,312695 -11600,15,310077 -11700,15,313971 -11800,15,314861 -11900,15,309789 -12000,15,309023 -12100,15,311526 -12200,15,321027 -12300,15,336021 -12400,15,318877 -12500,15,325203 -12600,15,308546 -12700,15,307408 -12800,15,315955 -12900,15,310752 -13000,15,314267 -13100,15,306091 -13200,15,302114 -13300,15,315457 -13400,15,329815 -13500,15,314960 -13600,15,318471 -13700,15,321957 -13800,15,336021 -13900,15,324149 -14000,15,326690 -14100,15,319182 -14200,15,305623 -14300,15,323834 -14400,15,306278 -14500,15,317258 -14600,15,326051 -14700,15,330250 -14800,15,324044 -14900,15,317863 -15000,15,314960 -100,17,330906 -200,17,339558 -300,17,334672 -400,17,336473 -500,17,341880 -600,17,338066 -700,17,332889 -800,17,323101 -900,17,328407 -1000,17,325626 -1100,17,339213 -1200,17,330578 -1300,17,331455 -1400,17,322684 -1500,17,331125 -1600,17,347463 -1700,17,343878 -1800,17,335457 -1900,17,339443 -2000,17,339328 -2100,17,338294 -2200,17,336927 -2300,17,331125 -2400,17,332667 -2500,17,334560 -2600,17,326264 -2700,17,330906 -2800,17,336927 -2900,17,341997 -3000,17,337381 -3100,17,329055 -3200,17,327761 -3300,17,330797 -3400,17,337837 -3500,17,327225 -3600,17,327761 -3700,17,330360 -3800,17,319386 -3900,17,333555 -4000,17,327011 -4100,17,329055 -4200,17,326583 -4300,17,325945 -4400,17,326264 -4500,17,329924 -4600,17,333000 -4700,17,326370 -4800,17,333444 -4900,17,326477 -5000,17,337952 -5100,17,335908 -5200,17,338524 -5300,17,323206 -5400,17,314861 -5500,17,344471 -5600,17,347222 -5700,17,333000 -5800,17,336473 -5900,17,331455 -6000,17,321646 -6100,17,331564 -6200,17,321027 -6300,17,324675 -6400,17,332005 -6500,17,325520 -6600,17,321646 -6700,17,325414 -6800,17,334896 -6900,17,332667 -7000,17,321957 -7100,17,323939 -7200,17,329815 -7300,17,337609 -7400,17,346380 -7500,17,334001 -7600,17,326157 -7700,17,318674 -7800,17,328623 -7900,17,337381 -8000,17,342231 -8100,17,340251 -8200,17,323206 -8300,17,330469 -8400,17,333555 -8500,17,338524 -8600,17,351864 -8700,17,343053 -8800,17,330687 -8900,17,337154 -9000,17,330360 -9100,17,332336 -9200,17,324254 -9300,17,329815 -9400,17,321130 -9500,17,332225 -9600,17,330250 -9700,17,345542 -9800,17,342114 -9900,17,327118 -10000,17,333333 -10100,17,344708 -10200,17,329597 -10300,17,324675 -10400,17,317863 -10500,17,322061 -10600,17,333111 -10700,17,341180 -10800,17,337723 -10900,17,343642 -11000,17,345303 -11100,17,335795 -11200,17,326157 -11300,17,329815 -11400,17,331895 -11500,17,318167 -11600,17,319795 -11700,17,330469 -11800,17,333444 -11900,17,323310 -12000,17,325097 -12100,17,335795 -12200,17,325097 -12300,17,348796 -12400,17,325203 -12500,17,338294 -12600,17,330033 -12700,17,342465 -12800,17,330797 -12900,17,345423 -13000,17,336021 -13100,17,345661 -13200,17,335908 -13300,17,338180 -13400,17,341180 -13500,17,336247 -13600,17,328407 -13700,17,329380 -13800,17,334560 -13900,17,327546 -14000,17,331125 -14100,17,328083 -14200,17,324044 -14300,17,338868 -14400,17,331125 -14500,17,339558 -14600,17,344827 -14700,17,343170 -14800,17,322476 -14900,17,330360 -15000,17,314070 -100,19,351000 -200,19,348432 -300,19,346860 -400,19,345781 -500,19,366032 -600,19,334001 -700,19,349650 -800,19,346860 -900,19,349162 -1000,19,357270 -1100,19,334560 -1200,19,358166 -1300,19,350877 -1400,19,353731 -1500,19,340599 -1600,19,347584 -1700,19,332889 -1800,19,348068 -1900,19,359582 -2000,19,339904 -2100,19,364431 -2200,19,344827 -2300,19,358422 -2400,19,345423 -2500,19,338294 -2600,19,344234 -2700,19,321233 -2800,19,341180 -2900,19,339904 -3000,19,347826 -3100,19,342348 -3200,19,347101 -3300,19,340136 -3400,19,335908 -3500,19,344946 -3600,19,339328 -3700,19,331455 -3800,19,348796 -3900,19,343053 -4000,19,337952 -4100,19,346860 -4200,19,349406 -4300,19,339328 -4400,19,327653 -4500,19,342114 -4600,19,340947 -4700,19,345661 -4800,19,347826 -4900,19,343053 -5000,19,326797 -5100,19,338868 -5200,19,338638 -5300,19,338294 -5400,19,340831 -5500,19,343524 -5600,19,345184 -5700,19,345781 -5800,19,352609 -5900,19,347947 -6000,19,353606 -6100,19,343406 -6200,19,343406 -6300,19,343524 -6400,19,343878 -6500,19,334224 -6600,19,330687 -6700,19,343642 -6800,19,343642 -6900,19,341180 -7000,19,336021 -7100,19,345303 -7200,19,347342 -7300,19,350508 -7400,19,339904 -7500,19,336021 -7600,19,336021 -7700,19,347101 -7800,19,358166 -7900,19,357909 -8000,19,357653 -8100,19,340947 -8200,19,343760 -8300,19,338180 -8400,19,333111 -8500,19,345423 -8600,19,336813 -8700,19,341296 -8800,19,347342 -8900,19,336700 -9000,19,331345 -9100,19,334672 -9200,19,338066 -9300,19,351123 -9400,19,340251 -9500,19,359324 -9600,19,332005 -9700,19,342231 -9800,19,342114 -9900,19,341413 -10000,19,330578 -10100,19,327868 -10200,19,346740 -10300,19,341880 -10400,19,341997 -10500,19,340020 -10600,19,343642 -10700,19,354233 -10800,19,359195 -10900,19,355745 -11000,19,356506 -11100,19,344115 -11200,19,335232 -11300,19,351493 -11400,19,341180 -11500,19,355998 -11600,19,354358 -11700,19,344827 -11800,19,352112 -11900,19,360100 -12000,19,344708 -12100,19,336700 -12200,19,340947 -12300,19,332667 -12400,19,347947 -12500,19,352858 -12600,19,352485 -12700,19,340947 -12800,19,339673 -12900,19,334784 -13000,19,346981 -13100,19,343878 -13200,19,347222 -13300,19,335570 -13400,19,339443 -13500,19,340947 -13600,19,337723 -13700,19,337837 -13800,19,345542 -13900,19,340367 -14000,19,348675 -14100,19,352982 -14200,19,356125 -14300,19,333778 -14400,19,340251 -14500,19,352609 -14600,19,336247 -14700,19,353481 -14800,19,338180 -14900,19,333667 -15000,19,336134 - diff --git a/bin/benchmark_metrics/metrics/clustered/pipeline.csv b/bin/benchmark_metrics/metrics/clustered/pipeline.csv deleted file mode 100644 index 55721936..00000000 --- a/bin/benchmark_metrics/metrics/clustered/pipeline.csv +++ /dev/null @@ -1,1502 +0,0 @@ -concurrency,pool,throughput -100,1,184911 -200,1,158805 -300,1,174550 -400,1,169348 -500,1,171791 -600,1,188786 -700,1,179953 -800,1,171614 -900,1,165837 -1000,1,177399 -1100,1,169090 -1200,1,167757 -1300,1,177367 -1400,1,158002 -1500,1,157331 -1600,1,158629 -1700,1,160410 -1800,1,158152 -1900,1,154655 -2000,1,159362 -2100,1,171998 -2200,1,160797 -2300,1,153468 -2400,1,154487 -2500,1,153233 -2600,1,159438 -2700,1,160359 -2800,1,158353 -2900,1,172741 -3000,1,166777 -3100,1,157728 -3200,1,165207 -3300,1,159565 -3400,1,165480 -3500,1,169664 -3600,1,167112 -3700,1,167056 -3800,1,160462 -3900,1,171262 -4000,1,151515 -4100,1,159362 -4200,1,152695 -4300,1,164176 -4400,1,152811 -4500,1,157678 -4600,1,157257 -4700,1,156250 -4800,1,150330 -4900,1,158453 -5000,1,160128 -5100,1,151217 -5200,1,152485 -5300,1,166805 -5400,1,164500 -5500,1,152555 -5600,1,154249 -5700,1,153092 -5800,1,157010 -5900,1,159821 -6000,1,167000 -6100,1,152068 -6200,1,150421 -6300,1,149812 -6400,1,155787 -6500,1,151584 -6600,1,156838 -6700,1,152392 -6800,1,150602 -6900,1,149387 -7000,1,151446 -7100,1,158202 -7200,1,156519 -7300,1,152207 -7400,1,148898 -7500,1,155448 -7600,1,152322 -7700,1,159642 -7800,1,152322 -7900,1,146563 -8000,1,147514 -8100,1,162548 -8200,1,157604 -8300,1,155110 -8400,1,151768 -8500,1,152299 -8600,1,152183 -8700,1,152928 -8800,1,152114 -8900,1,150966 -9000,1,148522 -9100,1,151103 -9200,1,153115 -9300,1,157480 -9400,1,147819 -9500,1,146455 -9600,1,150920 -9700,1,155110 -9800,1,158553 -9900,1,155086 -10000,1,154870 -10100,1,152439 -10200,1,147492 -10300,1,158604 -10400,1,149009 -10500,1,155279 -10600,1,146756 -10700,1,147885 -10800,1,147232 -10900,1,147210 -11000,1,156054 -11100,1,155763 -11200,1,149611 -11300,1,146993 -11400,1,151308 -11500,1,150466 -11600,1,164500 -11700,1,147318 -11800,1,155763 -11900,1,146006 -12000,1,150852 -12100,1,151469 -12200,1,146993 -12300,1,143884 -12400,1,148323 -12500,1,155520 -12600,1,154846 -12700,1,153657 -12800,1,149409 -12900,1,155811 -13000,1,151975 -13100,1,147885 -13200,1,157480 -13300,1,151676 -13400,1,152160 -13500,1,161943 -13600,1,146070 -13700,1,164365 -13800,1,153280 -13900,1,149409 -14000,1,147972 -14100,1,150966 -14200,1,156103 -14300,1,151354 -14400,1,150715 -14500,1,144487 -14600,1,144029 -14700,1,147405 -14800,1,148148 -14900,1,160926 -15000,1,149075 -100,3,802568 -200,3,476644 -300,3,526315 -400,3,537923 -500,3,532197 -600,3,513610 -700,3,503271 -800,3,524934 -900,3,514933 -1000,3,566572 -1100,3,551571 -1200,3,572737 -1300,3,525210 -1400,3,566251 -1500,3,593119 -1600,3,626566 -1700,3,613873 -1800,3,513874 -1900,3,550660 -2000,3,667111 -2100,3,698324 -2200,3,545851 -2300,3,619962 -2400,3,578034 -2500,3,703729 -2600,3,584453 -2700,3,600240 -2800,3,632111 -2900,3,571428 -3000,3,574712 -3100,3,537634 -3200,3,499750 -3300,3,530222 -3400,3,506329 -3500,3,518134 -3600,3,518134 -3700,3,547645 -3800,3,539374 -3900,3,502260 -4000,3,508388 -4100,3,513610 -4200,3,525762 -4300,3,551571 -4400,3,619195 -4500,3,516528 -4600,3,513874 -4700,3,503271 -4800,3,480769 -4900,3,555864 -5000,3,542005 -5100,3,522193 -5200,3,502260 -5300,3,494315 -5400,3,530503 -5500,3,575373 -5600,3,639386 -5700,3,594530 -5800,3,585823 -5900,3,576036 -6000,3,693962 -6100,3,579374 -6200,3,575373 -6300,3,580720 -6400,3,620732 -6500,3,564652 -6600,3,626959 -6700,3,552486 -6800,3,528262 -6900,3,563380 -7000,3,589622 -7100,3,655307 -7200,3,594177 -7300,3,599161 -7400,3,561167 -7500,3,551267 -7600,3,522193 -7700,3,569800 -7800,3,623052 -7900,3,513083 -8000,3,507872 -8100,3,541125 -8200,3,551267 -8300,3,576368 -8400,3,496770 -8500,3,490918 -8600,3,494559 -8700,3,505816 -8800,3,563380 -8900,3,581733 -9000,3,687757 -9100,3,535905 -9200,3,506585 -9300,3,502260 -9400,3,595238 -9500,3,579374 -9600,3,552791 -9700,3,517598 -9800,3,611620 -9900,3,556173 -10000,3,660501 -10100,3,579038 -10200,3,554631 -10300,3,538213 -10400,3,605326 -10500,3,600961 -10600,3,596658 -10700,3,532481 -10800,3,528541 -10900,3,539374 -11000,3,531914 -11100,3,548847 -11200,3,568828 -11300,3,565291 -11400,3,537056 -11500,3,549148 -11600,3,574052 -11700,3,585823 -11800,3,557103 -11900,3,614628 -12000,3,591715 -12100,3,498256 -12200,3,626959 -12300,3,612369 -12400,3,525210 -12500,3,573065 -12600,3,591016 -12700,3,579710 -12800,3,611995 -12900,3,539665 -13000,3,489476 -13100,3,477326 -13200,3,518672 -13300,3,533617 -13400,3,559597 -13500,3,508130 -13600,3,527148 -13700,3,518672 -13800,3,580720 -13900,3,604229 -14000,3,577700 -14100,3,539956 -14200,3,595592 -14300,3,490196 -14400,3,560852 -14500,3,693481 -14600,3,571102 -14700,3,590667 -14800,3,502008 -14900,3,480307 -15000,3,576701 -100,5,890471 -200,5,1282051 -300,5,1466275 -400,5,1474926 -500,5,1510574 -600,5,1434720 -700,5,1510574 -800,5,1529051 -900,5,1501501 -1000,5,1531393 -1100,5,1540832 -1200,5,1508295 -1300,5,1540832 -1400,5,1468428 -1500,5,1464128 -1600,5,1488095 -1700,5,1449275 -1800,5,1522070 -1900,5,1447178 -2000,5,1436781 -2100,5,1422475 -2200,5,1424501 -2300,5,1481481 -2400,5,1492537 -2500,5,1503759 -2600,5,1524390 -2700,5,1524390 -2800,5,1524390 -2900,5,1488095 -3000,5,1557632 -3100,5,1410437 -3200,5,1404494 -3300,5,1422475 -3400,5,1331557 -3500,5,1543209 -3600,5,1510574 -3700,5,1526717 -3800,5,1497005 -3900,5,1333333 -4000,5,1315789 -4100,5,1519756 -4200,5,1510574 -4300,5,1426533 -4400,5,1443001 -4500,5,1506024 -4600,5,1492537 -4700,5,1515151 -4800,5,1501501 -4900,5,1483679 -5000,5,1506024 -5100,5,1508295 -5200,5,1492537 -5300,5,1508295 -5400,5,1497005 -5500,5,1479289 -5600,5,1438848 -5700,5,1443001 -5800,5,1428571 -5900,5,1497005 -6000,5,1501501 -6100,5,1497005 -6200,5,1494768 -6300,5,1483679 -6400,5,1503759 -6500,5,1508295 -6600,5,1428571 -6700,5,1457725 -6800,5,1461988 -6900,5,1494768 -7000,5,1477104 -7100,5,1479289 -7200,5,1477104 -7300,5,1204819 -7400,5,1440922 -7500,5,1483679 -7600,5,1459854 -7700,5,1443001 -7800,5,1414427 -7900,5,1426533 -8000,5,1474926 -8100,5,1466275 -8200,5,1402524 -8300,5,1470588 -8400,5,1474926 -8500,5,1410437 -8600,5,1468428 -8700,5,1492537 -8800,5,1472754 -8900,5,1508295 -9000,5,1470588 -9100,5,1440922 -9200,5,1470588 -9300,5,1470588 -9400,5,1443001 -9500,5,1461988 -9600,5,1474926 -9700,5,1472754 -9800,5,1470588 -9900,5,1438848 -10000,5,1453488 -10100,5,1443001 -10200,5,1447178 -10300,5,1451378 -10400,5,1468428 -10500,5,1369863 -10600,5,1428571 -10700,5,1430615 -10800,5,1366120 -10900,5,1451378 -11000,5,1453488 -11100,5,1440922 -11200,5,1438848 -11300,5,1432664 -11400,5,1445086 -11500,5,1426533 -11600,5,1422475 -11700,5,1426533 -11800,5,1371742 -11900,5,1445086 -12000,5,1461988 -12100,5,1443001 -12200,5,1445086 -12300,5,1436781 -12400,5,1449275 -12500,5,1406469 -12600,5,1440922 -12700,5,1420454 -12800,5,1394700 -12900,5,1400560 -13000,5,1402524 -13100,5,1408450 -13200,5,1377410 -13300,5,1398601 -13400,5,1420454 -13500,5,1383125 -13600,5,1436781 -13700,5,1430615 -13800,5,1440922 -13900,5,1432664 -14000,5,1447178 -14100,5,1424501 -14200,5,1336898 -14300,5,1438848 -14400,5,1438848 -14500,5,1416430 -14600,5,1434720 -14700,5,1414427 -14800,5,1428571 -14900,5,1422475 -15000,5,1367989 -100,7,700280 -200,7,1164144 -300,7,1347708 -400,7,1515151 -500,7,1655629 -600,7,1736111 -700,7,1801801 -800,7,1869158 -900,7,1841620 -1000,7,1988071 -1100,7,1865671 -1200,7,1872659 -1300,7,1941747 -1400,7,1945525 -1500,7,1915708 -1600,7,1919385 -1700,7,1904761 -1800,7,1926782 -1900,7,1904761 -2000,7,1926782 -2100,7,1941747 -2200,7,1984126 -2300,7,1972386 -2400,7,1992031 -2500,7,1976284 -2600,7,1798561 -2700,7,1976284 -2800,7,1923076 -2900,7,1934235 -3000,7,1879699 -3100,7,1879699 -3200,7,1733102 -3300,7,1901140 -3400,7,1872659 -3500,7,1838235 -3600,7,1937984 -3700,7,1908396 -3800,7,1897533 -3900,7,1923076 -4000,7,1893939 -4100,7,1934235 -4200,7,1801801 -4300,7,1876172 -4400,7,1908396 -4500,7,1883239 -4600,7,1876172 -4700,7,1872659 -4800,7,1879699 -4900,7,1834862 -5000,7,1883239 -5100,7,1855287 -5200,7,1862197 -5300,7,1848428 -5400,7,1848428 -5500,7,1855287 -5600,7,1872659 -5700,7,1855287 -5800,7,1865671 -5900,7,1876172 -6000,7,1883239 -6100,7,1838235 -6200,7,1838235 -6300,7,1841620 -6400,7,1848428 -6500,7,1848428 -6600,7,1754385 -6700,7,1876172 -6800,7,1834862 -6900,7,1834862 -7000,7,1841620 -7100,7,1805054 -7200,7,1805054 -7300,7,1886792 -7400,7,1841620 -7500,7,1831501 -7600,7,1915708 -7700,7,1865671 -7800,7,1754385 -7900,7,1862197 -8000,7,1841620 -8100,7,1821493 -8200,7,1872659 -8300,7,1666666 -8400,7,1818181 -8500,7,1841620 -8600,7,1838235 -8700,7,1814882 -8800,7,1773049 -8900,7,1314060 -9000,7,1848428 -9100,7,1838235 -9200,7,1788908 -9300,7,1805054 -9400,7,1814882 -9500,7,1824817 -9600,7,1818181 -9700,7,1776198 -9800,7,1848428 -9900,7,1818181 -10000,7,1838235 -10100,7,1805054 -10200,7,1751313 -10300,7,1788908 -10400,7,1862197 -10500,7,1785714 -10600,7,1782531 -10700,7,1814882 -10800,7,1811594 -10900,7,1788908 -11000,7,1788908 -11100,7,1798561 -11200,7,1848428 -11300,7,1795332 -11400,7,1779359 -11500,7,1792114 -11600,7,1792114 -11700,7,1769911 -11800,7,1798561 -11900,7,1757469 -12000,7,1776198 -12100,7,1821493 -12200,7,1766784 -12300,7,1745200 -12400,7,1769911 -12500,7,1782531 -12600,7,1792114 -12700,7,1748251 -12800,7,1782531 -12900,7,1666666 -13000,7,1792114 -13100,7,1776198 -13200,7,1757469 -13300,7,1776198 -13400,7,1788908 -13500,7,1776198 -13600,7,1779359 -13700,7,1785714 -13800,7,1792114 -13900,7,1779359 -14000,7,1776198 -14100,7,1773049 -14200,7,1773049 -14300,7,1763668 -14400,7,1779359 -14500,7,1808318 -14600,7,1751313 -14700,7,1766784 -14800,7,1733102 -14900,7,1773049 -15000,7,1736111 -100,9,614628 -200,9,992063 -300,9,1251564 -400,9,1445086 -500,9,1524390 -600,9,1683501 -700,9,1686340 -800,9,1851851 -900,9,1886792 -1000,9,1960784 -1100,9,1968503 -1200,9,1869158 -1300,9,1919385 -1400,9,2040816 -1500,9,2164502 -1600,9,2164502 -1700,9,2207505 -1800,9,2105263 -1900,9,2114164 -2000,9,2304147 -2100,9,2293577 -2200,9,2283105 -2300,9,2188183 -2400,9,2252252 -2500,9,2227171 -2600,9,2262443 -2700,9,2267573 -2800,9,2252252 -2900,9,2222222 -3000,9,2257336 -3100,9,2118644 -3200,9,2272727 -3300,9,2237136 -3400,9,2277904 -3500,9,2314814 -3600,9,2331002 -3700,9,2272727 -3800,9,2277904 -3900,9,2178649 -4000,9,2217294 -4100,9,2309468 -4200,9,2277904 -4300,9,2109704 -4400,9,2298850 -4500,9,2217294 -4600,9,2212389 -4700,9,2331002 -4800,9,2262443 -4900,9,2212389 -5000,9,2257336 -5100,9,2197802 -5200,9,2169197 -5300,9,2217294 -5400,9,2169197 -5500,9,2169197 -5600,9,2242152 -5700,9,2257336 -5800,9,2222222 -5900,9,2277904 -6000,9,2197802 -6100,9,2159827 -6200,9,2212389 -6300,9,2155172 -6400,9,2202643 -6500,9,2277904 -6600,9,2267573 -6700,9,2118644 -6800,9,2237136 -6900,9,2141327 -7000,9,1984126 -7100,9,2173913 -7200,9,2217294 -7300,9,2169197 -7400,9,2178649 -7500,9,2207505 -7600,9,2127659 -7700,9,2169197 -7800,9,2188183 -7900,9,2145922 -8000,9,2155172 -8100,9,2150537 -8200,9,2127659 -8300,9,2207505 -8400,9,2145922 -8500,9,2183406 -8600,9,2192982 -8700,9,2173913 -8800,9,2252252 -8900,9,2242152 -9000,9,2092050 -9100,9,2164502 -9200,9,2178649 -9300,9,2092050 -9400,9,2159827 -9500,9,2178649 -9600,9,2109704 -9700,9,2178649 -9800,9,2169197 -9900,9,2109704 -10000,9,2136752 -10100,9,2132196 -10200,9,2087682 -10300,9,2100840 -10400,9,2000000 -10500,9,2118644 -10600,9,2159827 -10700,9,1923076 -10800,9,2096436 -10900,9,2136752 -11000,9,2083333 -11100,9,2150537 -11200,9,2169197 -11300,9,2057613 -11400,9,2127659 -11500,9,2096436 -11600,9,2083333 -11700,9,2127659 -11800,9,2096436 -11900,9,1890359 -12000,9,2136752 -12100,9,2024291 -12200,9,2173913 -12300,9,2105263 -12400,9,2066115 -12500,9,2040816 -12600,9,2155172 -12700,9,2079002 -12800,9,2105263 -12900,9,2092050 -13000,9,2092050 -13100,9,2049180 -13200,9,2100840 -13300,9,2066115 -13400,9,2118644 -13500,9,2044989 -13600,9,1876172 -13700,9,2049180 -13800,9,2087682 -13900,9,2061855 -14000,9,2074688 -14100,9,2049180 -14200,9,1937984 -14300,9,2083333 -14400,9,2066115 -14500,9,2057613 -14600,9,2036659 -14700,9,2074688 -14800,9,1972386 -14900,9,2044989 -15000,9,2020202 -100,11,533617 -200,11,860585 -300,11,1111111 -400,11,1315789 -500,11,1440922 -600,11,1564945 -700,11,1540832 -800,11,1697792 -900,11,1757469 -1000,11,1845018 -1100,11,1893939 -1200,11,2036659 -1300,11,2020202 -1400,11,2118644 -1500,11,2083333 -1600,11,2173913 -1700,11,2109704 -1800,11,2237136 -1900,11,2288329 -2000,11,2304147 -2100,11,2227171 -2200,11,2320185 -2300,11,2427184 -2400,11,2358490 -2500,11,2398081 -2600,11,1972386 -2700,11,2364066 -2800,11,2439024 -2900,11,2347417 -3000,11,2421307 -3100,11,2409638 -3200,11,2197802 -3300,11,2433090 -3400,11,2347417 -3500,11,2500000 -3600,11,2506265 -3700,11,2341920 -3800,11,2262443 -3900,11,2415458 -4000,11,2392344 -4100,11,2444987 -4200,11,2439024 -4300,11,2481389 -4400,11,2487562 -4500,11,2398081 -4600,11,2421307 -4700,11,2427184 -4800,11,2463054 -4900,11,2141327 -5000,11,2427184 -5100,11,2457002 -5200,11,2352941 -5300,11,2487562 -5400,11,2409638 -5500,11,2439024 -5600,11,2409638 -5700,11,2457002 -5800,11,2444987 -5900,11,2421307 -6000,11,2551020 -6100,11,2439024 -6200,11,2403846 -6300,11,2347417 -6400,11,2398081 -6500,11,2457002 -6600,11,2421307 -6700,11,2118644 -6800,11,2403846 -6900,11,2375296 -7000,11,2398081 -7100,11,2433090 -7200,11,2358490 -7300,11,2336448 -7400,11,2493765 -7500,11,2212389 -7600,11,2439024 -7700,11,2217294 -7800,11,2433090 -7900,11,2380952 -8000,11,2369668 -8100,11,2380952 -8200,11,2403846 -8300,11,2392344 -8400,11,2288329 -8500,11,2392344 -8600,11,2325581 -8700,11,2415458 -8800,11,2320185 -8900,11,2433090 -9000,11,2304147 -9100,11,2392344 -9200,11,2386634 -9300,11,2369668 -9400,11,2314814 -9500,11,2352941 -9600,11,2409638 -9700,11,2375296 -9800,11,2375296 -9900,11,2386634 -10000,11,2369668 -10100,11,2277904 -10200,11,2331002 -10300,11,2309468 -10400,11,2415458 -10500,11,2309468 -10600,11,1739130 -10700,11,2380952 -10800,11,2398081 -10900,11,2293577 -11000,11,2375296 -11100,11,2358490 -11200,11,2325581 -11300,11,2247191 -11400,11,2325581 -11500,11,2283105 -11600,11,2398081 -11700,11,2320185 -11800,11,1919385 -11900,11,2288329 -12000,11,2288329 -12100,11,2309468 -12200,11,2341920 -12300,11,2320185 -12400,11,2173913 -12500,11,2288329 -12600,11,2293577 -12700,11,2232142 -12800,11,2242152 -12900,11,2232142 -13000,11,2242152 -13100,11,2309468 -13200,11,2283105 -13300,11,2369668 -13400,11,2262443 -13500,11,2267573 -13600,11,2277904 -13700,11,2314814 -13800,11,2227171 -13900,11,2336448 -14000,11,2222222 -14100,11,1876172 -14200,11,2262443 -14300,11,2309468 -14400,11,2178649 -14500,11,2293577 -14600,11,2293577 -14700,11,2232142 -14800,11,2304147 -14900,11,2262443 -15000,11,2155172 -100,13,490918 -200,13,773993 -300,13,972762 -400,13,1191895 -500,13,1369863 -600,13,1412429 -700,13,1550387 -800,13,1589825 -900,13,1700680 -1000,13,1811594 -1100,13,1893939 -1200,13,1930501 -1300,13,1919385 -1400,13,2083333 -1500,13,2100840 -1600,13,2083333 -1700,13,2066115 -1800,13,1960784 -1900,13,2183406 -2000,13,2114164 -2100,13,2325581 -2200,13,2314814 -2300,13,2314814 -2400,13,2352941 -2500,13,2325581 -2600,13,2202643 -2700,13,2331002 -2800,13,2386634 -2900,13,2386634 -3000,13,2493765 -3100,13,2469135 -3200,13,2293577 -3300,13,2257336 -3400,13,2518891 -3500,13,2457002 -3600,13,2358490 -3700,13,1394700 -3800,13,2463054 -3900,13,2415458 -4000,13,2192982 -4100,13,2421307 -4200,13,2457002 -4300,13,2222222 -4400,13,2481389 -4500,13,2525252 -4600,13,2500000 -4700,13,2375296 -4800,13,2409638 -4900,13,2475247 -5000,13,2469135 -5100,13,2525252 -5200,13,2450980 -5300,13,2475247 -5400,13,2114164 -5500,13,2512562 -5600,13,2531645 -5700,13,2364066 -5800,13,2518891 -5900,13,2538071 -6000,13,2237136 -6100,13,2475247 -6200,13,2500000 -6300,13,2577319 -6400,13,2398081 -6500,13,2415458 -6600,13,2232142 -6700,13,2439024 -6800,13,2518891 -6900,13,2487562 -7000,13,2512562 -7100,13,2590673 -7200,13,2403846 -7300,13,2518891 -7400,13,2525252 -7500,13,2398081 -7600,13,2457002 -7700,13,2551020 -7800,13,2375296 -7900,13,2398081 -8000,13,2544529 -8100,13,2487562 -8200,13,2544529 -8300,13,2364066 -8400,13,2506265 -8500,13,2463054 -8600,13,2386634 -8700,13,2415458 -8800,13,2487562 -8900,13,2358490 -9000,13,2439024 -9100,13,2444987 -9200,13,2551020 -9300,13,2364066 -9400,13,2398081 -9500,13,2024291 -9600,13,2392344 -9700,13,2433090 -9800,13,2487562 -9900,13,2469135 -10000,13,2444987 -10100,13,2242152 -10200,13,2375296 -10300,13,2551020 -10400,13,2369668 -10500,13,2252252 -10600,13,2415458 -10700,13,2173913 -10800,13,2415458 -10900,13,2375296 -11000,13,2392344 -11100,13,2358490 -11200,13,2347417 -11300,13,2409638 -11400,13,2331002 -11500,13,2380952 -11600,13,2358490 -11700,13,2293577 -11800,13,2159827 -11900,13,2369668 -12000,13,2364066 -12100,13,2336448 -12200,13,2331002 -12300,13,2364066 -12400,13,2227171 -12500,13,2358490 -12600,13,2433090 -12700,13,2272727 -12800,13,2369668 -12900,13,2380952 -13000,13,2136752 -13100,13,2421307 -13200,13,2364066 -13300,13,2398081 -13400,13,2283105 -13500,13,2347417 -13600,13,2262443 -13700,13,2386634 -13800,13,2403846 -13900,13,2298850 -14000,13,2336448 -14100,13,2347417 -14200,13,2267573 -14300,13,2288329 -14400,13,2267573 -14500,13,2320185 -14600,13,2364066 -14700,13,2227171 -14800,13,2304147 -14900,13,2262443 -15000,13,2336448 -100,15,494804 -200,15,708215 -300,15,892060 -400,15,1097694 -500,15,1259445 -600,15,1364256 -700,15,1501501 -800,15,1366120 -900,15,1612903 -1000,15,1811594 -1100,15,1828153 -1200,15,1848428 -1300,15,1988071 -1400,15,1869158 -1500,15,1956947 -1600,15,2049180 -1700,15,2150537 -1800,15,2074688 -1900,15,2087682 -2000,15,1818181 -2100,15,2237136 -2200,15,2277904 -2300,15,2217294 -2400,15,2252252 -2500,15,2173913 -2600,15,2288329 -2700,15,2341920 -2800,15,2380952 -2900,15,2320185 -3000,15,2380952 -3100,15,1584786 -3200,15,2380952 -3300,15,2415458 -3400,15,2481389 -3500,15,2493765 -3600,15,2380952 -3700,15,2415458 -3800,15,2469135 -3900,15,2450980 -4000,15,2409638 -4100,15,2512562 -4200,15,2415458 -4300,15,2457002 -4400,15,2506265 -4500,15,2487562 -4600,15,2450980 -4700,15,2444987 -4800,15,2380952 -4900,15,2475247 -5000,15,2531645 -5100,15,2444987 -5200,15,2469135 -5300,15,2481389 -5400,15,2415458 -5500,15,2512562 -5600,15,2512562 -5700,15,2433090 -5800,15,2493765 -5900,15,2506265 -6000,15,2386634 -6100,15,2427184 -6200,15,2518891 -6300,15,2512562 -6400,15,2506265 -6500,15,2380952 -6600,15,2469135 -6700,15,1798561 -6800,15,2444987 -6900,15,2525252 -7000,15,2386634 -7100,15,2531645 -7200,15,2433090 -7300,15,2500000 -7400,15,2583979 -7500,15,2283105 -7600,15,2421307 -7700,15,2518891 -7800,15,2427184 -7900,15,2336448 -8000,15,2590673 -8100,15,2421307 -8200,15,2433090 -8300,15,2518891 -8400,15,2481389 -8500,15,2403846 -8600,15,2450980 -8700,15,2409638 -8800,15,2500000 -8900,15,2439024 -9000,15,2439024 -9100,15,2386634 -9200,15,2427184 -9300,15,2493765 -9400,15,2358490 -9500,15,2409638 -9600,15,2392344 -9700,15,2386634 -9800,15,2463054 -9900,15,2427184 -10000,15,2409638 -10100,15,2403846 -10200,15,2444987 -10300,15,2409638 -10400,15,2469135 -10500,15,2375296 -10600,15,2364066 -10700,15,2439024 -10800,15,2375296 -10900,15,2444987 -11000,15,2398081 -11100,15,2386634 -11200,15,2415458 -11300,15,2457002 -11400,15,2341920 -11500,15,2380952 -11600,15,2380952 -11700,15,2380952 -11800,15,2427184 -11900,15,2398081 -12000,15,2392344 -12100,15,2325581 -12200,15,2352941 -12300,15,2392344 -12400,15,2392344 -12500,15,2364066 -12600,15,2409638 -12700,15,2352941 -12800,15,2331002 -12900,15,2386634 -13000,15,2369668 -13100,15,2083333 -13200,15,2386634 -13300,15,2336448 -13400,15,2257336 -13500,15,2364066 -13600,15,2386634 -13700,15,2283105 -13800,15,2325581 -13900,15,2320185 -14000,15,2309468 -14100,15,2352941 -14200,15,1443001 -14300,15,2288329 -14400,15,2331002 -14500,15,2392344 -14600,15,2398081 -14700,15,2320185 -14800,15,2320185 -14900,15,2277904 -15000,15,2352941 -100,17,471031 -200,17,659195 -300,17,829187 -400,17,998003 -500,17,1175088 -600,17,1295336 -700,17,1333333 -800,17,1515151 -900,17,1564945 -1000,17,1642036 -1100,17,1739130 -1200,17,1766784 -1300,17,1776198 -1400,17,1912045 -1500,17,1996007 -1600,17,1968503 -1700,17,2032520 -1800,17,2079002 -1900,17,2141327 -2000,17,2145922 -2100,17,1700680 -2200,17,2257336 -2300,17,2061855 -2400,17,1763668 -2500,17,2155172 -2600,17,1984126 -2700,17,2008032 -2800,17,2386634 -2900,17,2298850 -3000,17,2309468 -3100,17,2380952 -3200,17,2212389 -3300,17,2309468 -3400,17,2421307 -3500,17,2398081 -3600,17,2283105 -3700,17,2457002 -3800,17,2262443 -3900,17,2358490 -4000,17,2444987 -4100,17,2369668 -4200,17,2450980 -4300,17,2475247 -4400,17,2369668 -4500,17,2380952 -4600,17,2352941 -4700,17,2481389 -4800,17,2386634 -4900,17,2506265 -5000,17,2493765 -5100,17,2450980 -5200,17,2506265 -5300,17,2481389 -5400,17,2450980 -5500,17,2493765 -5600,17,2506265 -5700,17,2386634 -5800,17,2427184 -5900,17,2481389 -6000,17,2481389 -6100,17,2304147 -6200,17,2487562 -6300,17,2450980 -6400,17,2481389 -6500,17,2487562 -6600,17,2481389 -6700,17,2557544 -6800,17,2512562 -6900,17,2481389 -7000,17,2403846 -7100,17,2518891 -7200,17,2512562 -7300,17,2352941 -7400,17,2493765 -7500,17,2375296 -7600,17,2444987 -7700,17,2463054 -7800,17,2531645 -7900,17,2427184 -8000,17,2544529 -8100,17,2500000 -8200,17,2358490 -8300,17,2506265 -8400,17,2475247 -8500,17,2257336 -8600,17,2500000 -8700,17,2433090 -8800,17,2506265 -8900,17,2463054 -9000,17,2487562 -9100,17,2314814 -9200,17,2463054 -9300,17,2518891 -9400,17,2487562 -9500,17,2450980 -9600,17,2439024 -9700,17,2132196 -9800,17,2487562 -9900,17,2386634 -10000,17,2380952 -10100,17,2444987 -10200,17,2392344 -10300,17,2341920 -10400,17,2457002 -10500,17,2433090 -10600,17,2347417 -10700,17,2450980 -10800,17,2421307 -10900,17,2283105 -11000,17,2457002 -11100,17,2475247 -11200,17,2304147 -11300,17,2409638 -11400,17,2403846 -11500,17,2364066 -11600,17,2386634 -11700,17,2421307 -11800,17,2347417 -11900,17,2415458 -12000,17,2427184 -12100,17,2364066 -12200,17,2358490 -12300,17,2403846 -12400,17,2352941 -12500,17,2386634 -12600,17,2380952 -12700,17,2272727 -12800,17,2336448 -12900,17,2336448 -13000,17,2352941 -13100,17,2352941 -13200,17,1760563 -13300,17,2439024 -13400,17,2341920 -13500,17,2331002 -13600,17,2421307 -13700,17,2352941 -13800,17,2314814 -13900,17,2331002 -14000,17,2325581 -14100,17,2320185 -14200,17,2369668 -14300,17,2380952 -14400,17,2173913 -14500,17,2336448 -14600,17,2304147 -14700,17,2336448 -14800,17,2336448 -14900,17,2277904 -15000,17,2283105 -100,19,487567 -200,19,636942 -300,19,810372 -400,19,940733 -500,19,1067235 -600,19,1216545 -700,19,1331557 -800,19,1388888 -900,19,1449275 -1000,19,1589825 -1100,19,1584786 -1200,19,1700680 -1300,19,1739130 -1400,19,1821493 -1500,19,1834862 -1600,19,1908396 -1700,19,1890359 -1800,19,1960784 -1900,19,1960784 -2000,19,2070393 -2100,19,2008032 -2200,19,2109704 -2300,19,2074688 -2400,19,2127659 -2500,19,2192982 -2600,19,2173913 -2700,19,2304147 -2800,19,2262443 -2900,19,2283105 -3000,19,1689189 -3100,19,2304147 -3200,19,2257336 -3300,19,2325581 -3400,19,2398081 -3500,19,2257336 -3600,19,2347417 -3700,19,2320185 -3800,19,2341920 -3900,19,2403846 -4000,19,2493765 -4100,19,2341920 -4200,19,2375296 -4300,19,2336448 -4400,19,2444987 -4500,19,2369668 -4600,19,2457002 -4700,19,2398081 -4800,19,2409638 -4900,19,2463054 -5000,19,2481389 -5100,19,2439024 -5200,19,2375296 -5300,19,2544529 -5400,19,2409638 -5500,19,2433090 -5600,19,2506265 -5700,19,2457002 -5800,19,2427184 -5900,19,2444987 -6000,19,2531645 -6100,19,2493765 -6200,19,2518891 -6300,19,2544529 -6400,19,2538071 -6500,19,2386634 -6600,19,2525252 -6700,19,2427184 -6800,19,2525252 -6900,19,2481389 -7000,19,2469135 -7100,19,2564102 -7200,19,2500000 -7300,19,2500000 -7400,19,2500000 -7500,19,2500000 -7600,19,2398081 -7700,19,2433090 -7800,19,2518891 -7900,19,2500000 -8000,19,2409638 -8100,19,2463054 -8200,19,2433090 -8300,19,2392344 -8400,19,2487562 -8500,19,2469135 -8600,19,2469135 -8700,19,2500000 -8800,19,2450980 -8900,19,2439024 -9000,19,2463054 -9100,19,2481389 -9200,19,2457002 -9300,19,2544529 -9400,19,2457002 -9500,19,2463054 -9600,19,2439024 -9700,19,2463054 -9800,19,2439024 -9900,19,2386634 -10000,19,2463054 -10100,19,2463054 -10200,19,2500000 -10300,19,2409638 -10400,19,2463054 -10500,19,2463054 -10600,19,2493765 -10700,19,2392344 -10800,19,2421307 -10900,19,2427184 -11000,19,2336448 -11100,19,2444987 -11200,19,2439024 -11300,19,2475247 -11400,19,2421307 -11500,19,2415458 -11600,19,2247191 -11700,19,2450980 -11800,19,2450980 -11900,19,2325581 -12000,19,2433090 -12100,19,2415458 -12200,19,2309468 -12300,19,2415458 -12400,19,2415458 -12500,19,2364066 -12600,19,2398081 -12700,19,2392344 -12800,19,2386634 -12900,19,2398081 -13000,19,2409638 -13100,19,2336448 -13200,19,2392344 -13300,19,2252252 -13400,19,2409638 -13500,19,2403846 -13600,19,2320185 -13700,19,2415458 -13800,19,2347417 -13900,19,2227171 -14000,19,2336448 -14100,19,2331002 -14200,19,2320185 -14300,19,2369668 -14400,19,2427184 -14500,19,2293577 -14600,19,2364066 -14700,19,2347417 -14800,19,2283105 -14900,19,2325581 -15000,19,2352941 - diff --git a/bin/benchmark_metrics/run.sh b/bin/benchmark_metrics/run.sh deleted file mode 100755 index 27b8777f..00000000 --- a/bin/benchmark_metrics/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker-compose -f ../../tests/docker/compose/cluster.yml -f ../../tests/docker/compose/centralized.yml -f ./docker-compose.yml \ - run -u $(id -u ${USER}):$(id -g ${USER}) --rm fred-benchmark-metrics cargo run --release -- "${@:1}" \ No newline at end of file diff --git a/bin/benchmark_metrics/rustfmt.toml b/bin/benchmark_metrics/rustfmt.toml deleted file mode 100644 index 08e76d74..00000000 --- a/bin/benchmark_metrics/rustfmt.toml +++ /dev/null @@ -1,52 +0,0 @@ -binop_separator = "Front" -blank_lines_upper_bound = 1 -brace_style = "SameLineWhere" -combine_control_expr = true -comment_width = 125 -condense_wildcard_suffixes = false -control_brace_style = "AlwaysSameLine" -edition = "2021" -empty_item_single_line = true -enum_discrim_align_threshold = 0 -error_on_line_overflow = false -error_on_unformatted = false -fn_params_layout = "Tall" -fn_single_line = false -force_explicit_abi = true -force_multiline_blocks = false -format_code_in_doc_comments = true -format_macro_bodies = true -format_macro_matchers = true -format_strings = true -hard_tabs = false -imports_granularity = "Crate" -imports_indent = "Block" -imports_layout = "HorizontalVertical" -indent_style = "Block" -inline_attribute_width = 0 -match_arm_blocks = true -match_block_trailing_comma = true -max_width = 118 -merge_derives = true -newline_style = "Auto" -normalize_comments = true -normalize_doc_attributes = true -overflow_delimited_expr = true -remove_nested_parens = true -reorder_impl_items = true -reorder_imports = true -reorder_modules = true -show_parse_errors = true -space_after_colon = true -space_before_colon = false -spaces_around_ranges = true -struct_field_align_threshold = 50 -struct_lit_single_line = true -tab_spaces = 2 -trailing_semicolon = true -type_punctuation_density = "Wide" -use_field_init_shorthand = true -use_small_heuristics = "Default" -use_try_shorthand = true -where_single_line = false -wrap_comments = true diff --git a/bin/benchmark_metrics/src/main.rs b/bin/benchmark_metrics/src/main.rs deleted file mode 100644 index d9678a05..00000000 --- a/bin/benchmark_metrics/src/main.rs +++ /dev/null @@ -1,205 +0,0 @@ -#[macro_use] -extern crate log; -extern crate pretty_env_logger; - -use clap::{load_yaml, App}; -use csv::WriterBuilder; -use indicatif::ProgressBar; -use std::time::Duration; -use subprocess::{Popen, PopenConfig, Redirection}; - -// TODO update clap -struct Argv { - pub count: u64, - pub pipeline: bool, - pub pool_range: (u32, u32), - pub pool_step: u32, - pub concurrency_range: (u32, u32), - pub concurrency_step: u32, - pub host: String, - pub port: u16, - pub cluster: bool, -} - -fn parse_argv() -> Argv { - let yaml = load_yaml!("../cli.yml"); - let matches = App::from_yaml(yaml).get_matches(); - let cluster = matches.is_present("cluster"); - - let count = matches - .value_of("count") - .map(|v| { - v.parse::().unwrap_or_else(|_| { - panic!("Invalid command count: {}.", v); - }) - }) - .expect("Invalid count"); - let concurrency_range = matches - .value_of("concurrency") - .map(|v| { - let parts: Vec<&str> = v.split("-").collect(); - ( - parts[0].parse::().expect("Invalid concurrency range"), - parts[1].parse::().expect("Invalid concurrency range"), - ) - }) - .expect("Invalid concurrency"); - let concurrency_step = matches - .value_of("concurrency-step") - .map(|v| v.parse::().expect("Invalid concurrency.")) - .expect("Invalid concurrency step"); - let pool_range = matches - .value_of("pool") - .map(|v| { - let parts: Vec<&str> = v.split("-").collect(); - ( - parts[0].parse::().expect("Invalid pool range"), - parts[1].parse::().expect("Invalid pool range"), - ) - }) - .expect("Invalid pool range"); - let pool_step = matches - .value_of("pool-step") - .map(|v| v.parse::().expect("Invalid pool.")) - .expect("Invalid pool range"); - let host = matches - .value_of("host") - .map(|v| v.to_owned()) - .unwrap_or("127.0.0.1".into()); - let port = matches - .value_of("port") - .map(|v| v.parse::().expect("Invalid port")) - .unwrap_or(6379); - let pipeline = matches.subcommand_matches("pipeline").is_some(); - - Argv { - pool_range, - pool_step, - concurrency_range, - concurrency_step, - cluster, - count, - host, - port, - pipeline, - } -} - -struct Metrics { - pub concurrency: u32, - pub pool: u32, - pub throughput: f64, -} - -fn run_command(argv: &Argv, bar: &ProgressBar, concurrency: u32, pool: u32) -> Metrics { - let mut parts = vec![ - "cargo".into(), - "run".into(), - "--release".into(), - "--manifest-path".into(), - // if not using docker - //"../benchmark/Cargo.toml".into(), - // if using docker - "/benchmark/Cargo.toml".into(), - "--".into(), - "-q".into(), - "-h".into(), - argv.host.clone(), - "-p".into(), - argv.port.to_string(), - "-n".into(), - argv.count.to_string(), - ]; - if argv.cluster { - parts.push("--cluster".into()); - } - parts.extend(vec!["-c".into(), concurrency.to_string()]); - parts.extend(vec!["-P".into(), pool.to_string()]); - parts.push(if argv.pipeline { - "pipeline".into() - } else { - "no-pipeline".into() - }); - debug!("Running command: {:?}", parts); - - let mut process = Popen::create(&parts, PopenConfig { - stdout: Redirection::Pipe, - stderr: Redirection::Pipe, - ..Default::default() - }) - .expect("Failed to spawn process"); - if process - .wait_timeout(Duration::from_secs(120)) - .expect("Failed to wait on subprocess.") - .is_none() - { - panic!("Timeout running with pool: {}, concurrency: {}", pool, concurrency); - } - let (throughput, stderr) = process.communicate(None).expect("Failed to read process output"); - debug!("Recv output: {:?}, stderr: {:?}", throughput, stderr); - bar.inc(1); - - let throughput = throughput - .expect("Missing output") - .trim() - .parse::() - .expect("Failed to parse output"); - Metrics { - concurrency, - pool, - throughput, - } -} - -fn print_output(data: Vec) { - let mut wtr = WriterBuilder::new().from_writer(vec![]); - let _ = wtr.write_record(&["concurrency", "pool", "throughput"]); - - for metrics in data.into_iter() { - let _ = wtr.write_record(&[ - metrics.concurrency.to_string(), - metrics.pool.to_string(), - metrics.throughput.to_string(), - ]); - } - - println!( - "{}", - String::from_utf8(wtr.into_inner().expect("Failed to write CSV.")).expect("Failed to convert CSV to string.") - ); -} - -fn main() { - pretty_env_logger::init(); - let argv = parse_argv(); - let concurrency_runs = (argv.concurrency_range.1 - argv.concurrency_range.0 + 1) / argv.concurrency_step; - let pool_runs = (argv.pool_range.1 - argv.pool_range.0 + 1) / argv.pool_step; - trace!("Concurrency runs: {}, pool runs: {}", concurrency_runs, pool_runs); - let bar = ProgressBar::new((concurrency_runs * pool_runs) as u64); - - let mut output = Vec::with_capacity((concurrency_runs * pool_runs) as usize); - - let mut pool = argv.pool_range.0; - while pool <= argv.pool_range.1 { - let mut concurrency = argv.concurrency_range.0; - - while concurrency <= argv.concurrency_range.1 { - debug!("Running with concurrency: {}, pool: {}", concurrency, pool); - - if concurrency < pool { - bar.inc(1); - concurrency += argv.concurrency_step; - continue; - } - - let metrics = run_command(&argv, &bar, concurrency, pool); - output.push(metrics); - concurrency += argv.concurrency_step; - } - - pool += argv.pool_step; - } - bar.finish(); - - print_output(output); -} diff --git a/bin/inf_loop/Cargo.toml b/bin/inf_loop/Cargo.toml deleted file mode 100644 index 66a1a073..00000000 --- a/bin/inf_loop/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "inf_loop" -version = "0.1.0" -authors = ["Alec Embke "] -edition = "2018" -description = "A script to run `INCR foo` in an infinite loop." - -[profile.release] -debug = true - -[dependencies] -clap = { version = "2.33", features = ["yaml"] } -log = "0.4" -pretty_env_logger = "0.5" -tokio = { version = "1", features = ["full"] } -futures = "0.3" -rand = "0.8" -opentelemetry = { version = "0.18.0", features = ["rt-tokio", "trace"] } -opentelemetry-jaeger = { version = "0.17.0", features = ["tokio", "isahc_collector_client", "isahc", "collector_client", "rt-tokio"] } -tracing-attributes = "0.1.23" -tracing-opentelemetry = "0.18.0" -tracing-core = "0.1.30" -tracing-subscriber = "0.3.16" -tracing = "0.1.37" - -[dependencies.fred] -#path = "../.." -path = "/fred" -features = ["network-logs", "debug-ids", "replicas", "i-all"] - -[features] -stdout-tracing = ["fred/partial-tracing"] -partial-tracing = ["fred/partial-tracing"] -full-tracing = ["fred/full-tracing"] \ No newline at end of file diff --git a/bin/inf_loop/README.md b/bin/inf_loop/README.md deleted file mode 100644 index 6d60bed8..00000000 --- a/bin/inf_loop/README.md +++ /dev/null @@ -1,28 +0,0 @@ -Inf Loop -======== - -A simple test script that sends `INCR foo` via a `RedisPool` on an interval forever using an infinite reconnect policy. - -``` -USAGE: - inf_loop [FLAGS] [OPTIONS] - -FLAGS: - --cluster Whether to use a clustered deployment. - --help Prints help information - -V, --version Prints version information - -OPTIONS: - -h, --host The hostname of the redis server. [default: 127.0.0.1] - -i, --interval The time to wait between INCR commands in milliseconds. [default: 1000] - -P, --pool The number of clients in the redis connection pool. [default: 1] - -p, --port The port for the redis server. [default: 6379] - -w, --wait Add a delay, in milliseconds, after connecting but before starting the INCR loop. - [default: 0] -``` - -If using docker: - -``` -./run.sh --cluster -h redis-cluster-1 -p 30001 -a key -``` \ No newline at end of file diff --git a/bin/inf_loop/cli.yml b/bin/inf_loop/cli.yml deleted file mode 100644 index 2a1d111a..00000000 --- a/bin/inf_loop/cli.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: inf_loop -version: "1.0" -author: Alec Embke -about: Run a script that sends `INCR foo` on an interval forever, with an infinite reconnect policy. -args: - - cluster: - long: cluster - help: Whether to use a clustered deployment. - takes_value: false - - tracing: - long: tracing - help: Whether to enable tracing. - takes_value: false - - replicas: - long: replicas - help: Whether to use `GET` with replicas instead of `INCR` with primary nodes. - takes_value: false - - host: - short: h - long: host - value_name: "STRING" - help: The hostname of the redis server. - takes_value: true - default_value: "127.0.0.1" - - auth: - short: a - long: auth - value_name: "STRING" - help: An optional authentication key or password. - takes_value: true - default_value: "" - - port: - short: p - long: port - value_name: "NUMBER" - help: The port for the redis server. - takes_value: true - default_value: "6379" - - pool: - short: P - long: pool - value_name: "NUMBER" - help: The number of clients in the redis connection pool. - takes_value: true - default_value: "1" - - interval: - short: i - long: interval - value_name: "NUMBER" - help: The time to wait between INCR commands in milliseconds. - takes_value: true - default_value: "1000" - - wait: - short: w - long: wait - value_name: "NUMBER" - help: Add a delay, in milliseconds, after connecting but before starting the INCR loop. - takes_value: true - default_value: "0" \ No newline at end of file diff --git a/bin/inf_loop/docker-compose.yml b/bin/inf_loop/docker-compose.yml deleted file mode 100644 index 0bb31564..00000000 --- a/bin/inf_loop/docker-compose.yml +++ /dev/null @@ -1,23 +0,0 @@ -version: '2' - -services: - inf-loop: - depends_on: - - redis-main - - redis-cluster-6 - container_name: "inf-loop" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - entrypoint: "cargo run --release --features partial-tracing -- ${TEST_ARGV}" - environment: - RUST_LOG: "${RUST_LOG}" - REDIS_VERSION: "${REDIS_VERSION}" - volumes: - - "../../../bin/inf_loop:/project" - - "../../..:/fred" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/bin/inf_loop/run.sh b/bin/inf_loop/run.sh deleted file mode 100755 index 3273d0c7..00000000 --- a/bin/inf_loop/run.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -TEST_ARGV="$@" docker-compose \ - -f ../../tests/docker/compose/cluster.yml \ - -f ../../tests/docker/compose/centralized.yml \ - -f ./docker-compose.yml \ - run -u $(id -u ${USER}):$(id -g ${USER}) --rm inf-loop \ No newline at end of file diff --git a/bin/inf_loop/rustfmt.toml b/bin/inf_loop/rustfmt.toml deleted file mode 100644 index 08e76d74..00000000 --- a/bin/inf_loop/rustfmt.toml +++ /dev/null @@ -1,52 +0,0 @@ -binop_separator = "Front" -blank_lines_upper_bound = 1 -brace_style = "SameLineWhere" -combine_control_expr = true -comment_width = 125 -condense_wildcard_suffixes = false -control_brace_style = "AlwaysSameLine" -edition = "2021" -empty_item_single_line = true -enum_discrim_align_threshold = 0 -error_on_line_overflow = false -error_on_unformatted = false -fn_params_layout = "Tall" -fn_single_line = false -force_explicit_abi = true -force_multiline_blocks = false -format_code_in_doc_comments = true -format_macro_bodies = true -format_macro_matchers = true -format_strings = true -hard_tabs = false -imports_granularity = "Crate" -imports_indent = "Block" -imports_layout = "HorizontalVertical" -indent_style = "Block" -inline_attribute_width = 0 -match_arm_blocks = true -match_block_trailing_comma = true -max_width = 118 -merge_derives = true -newline_style = "Auto" -normalize_comments = true -normalize_doc_attributes = true -overflow_delimited_expr = true -remove_nested_parens = true -reorder_impl_items = true -reorder_imports = true -reorder_modules = true -show_parse_errors = true -space_after_colon = true -space_before_colon = false -spaces_around_ranges = true -struct_field_align_threshold = 50 -struct_lit_single_line = true -tab_spaces = 2 -trailing_semicolon = true -type_punctuation_density = "Wide" -use_field_init_shorthand = true -use_small_heuristics = "Default" -use_try_shorthand = true -where_single_line = false -wrap_comments = true diff --git a/bin/inf_loop/src/main.rs b/bin/inf_loop/src/main.rs deleted file mode 100644 index ce7a4634..00000000 --- a/bin/inf_loop/src/main.rs +++ /dev/null @@ -1,216 +0,0 @@ -#[macro_use] -extern crate clap; -extern crate fred; -extern crate futures; -extern crate tokio; - -#[macro_use] -extern crate log; -extern crate pretty_env_logger; - -use clap::App; -use fred::{ - bytes::Bytes, - prelude::*, - types::{ReplicaConfig, UnresponsiveConfig}, -}; -use opentelemetry::{ - global, - sdk::{ - export::trace::stdout, - runtime::{Runtime, Tokio}, - trace::{self, RandomIdGenerator, Sampler, TraceRuntime}, - }, -}; -use rand::{self, distributions::Alphanumeric, Rng}; -use std::{default::Default, time::Duration}; -use tokio::time::sleep; -use tracing_subscriber::{layer::SubscriberExt, Layer, Registry}; - -#[derive(Debug)] -struct Argv { - pub cluster: bool, - pub replicas: bool, - pub host: String, - pub port: u16, - pub pool: usize, - pub interval: u64, - pub wait: u64, - pub auth: String, - pub tracing: bool, -} - -fn parse_argv() -> Argv { - let yaml = load_yaml!("../cli.yml"); - let matches = App::from_yaml(yaml).get_matches(); - let cluster = matches.is_present("cluster"); - let replicas = matches.is_present("replicas"); - let tracing = matches.is_present("tracing"); - - let host = matches - .value_of("host") - .map(|v| v.to_owned()) - .unwrap_or("127.0.0.1".into()); - let port = matches - .value_of("port") - .map(|v| v.parse::().expect("Invalid port")) - .unwrap_or(6379); - let pool = matches - .value_of("pool") - .map(|v| v.parse::().expect("Invalid pool")) - .unwrap_or(1); - let interval = matches - .value_of("interval") - .map(|v| v.parse::().expect("Invalid interval")) - .unwrap_or(1000); - let wait = matches - .value_of("wait") - .map(|v| v.parse::().expect("Invalid wait")) - .unwrap_or(0); - let auth = matches.value_of("auth").map(|v| v.to_owned()).unwrap_or("".into()); - - Argv { - cluster, - auth, - host, - port, - pool, - interval, - wait, - replicas, - tracing, - } -} - -#[cfg(all( - not(feature = "partial-tracing"), - not(feature = "stdout-tracing"), - not(feature = "full-tracing") -))] -pub fn setup_tracing(enable: bool) {} - -#[cfg(feature = "stdout-tracing")] -pub fn setup_tracing(enable: bool) { - if enable { - info!("Starting stdout tracing..."); - let layer = tracing_subscriber::fmt::layer() - .with_writer(std::io::stdout) - .with_ansi(false) - .event_format(tracing_subscriber::fmt::format().pretty()) - .with_thread_names(true) - .with_level(true) - .with_line_number(true) - .with_filter(tracing_subscriber::filter::LevelFilter::TRACE); - let subscriber = Registry::default().with(layer); - tracing::subscriber::set_global_default(subscriber).expect("Failed to set global tracing subscriber"); - } -} - -#[cfg(any(feature = "partial-tracing", feature = "full-tracing"))] -pub fn setup_tracing(enable: bool) { - let sampler = if enable { - info!("Starting tracing..."); - Sampler::AlwaysOn - } else { - Sampler::AlwaysOff - }; - - global::set_text_map_propagator(opentelemetry_jaeger::Propagator::new()); - let jaeger_install = opentelemetry_jaeger::new_agent_pipeline() - .with_service_name("fred-inf-loop") - .with_endpoint("jaeger:6831") - .with_trace_config( - trace::config() - .with_sampler(sampler) - .with_id_generator(RandomIdGenerator::default()) - .with_max_attributes_per_span(32), - ) - .install_simple(); - - let tracer = match jaeger_install { - Ok(t) => t, - Err(e) => panic!("Fatal error initializing tracing: {:?}", e), - }; - - let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); - let subscriber = Registry::default().with(telemetry); - tracing::subscriber::set_global_default(subscriber).expect("Failed to set global tracing subscriber"); - - info!("Initialized opentelemetry-jaeger pipeline."); -} - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - pretty_env_logger::init_timed(); - let argv = parse_argv(); - info!("Running with configuration: {:?}", argv); - setup_tracing(argv.tracing); - - let config = RedisConfig { - #[cfg(any(feature = "partial-tracing", feature = "stdout-tracing", feature = "full-tracing"))] - tracing: TracingConfig { - enabled: argv.tracing, - ..Default::default() - }, - server: if argv.cluster { - ServerConfig::new_clustered(vec![(&argv.host, argv.port)]) - } else { - ServerConfig::new_centralized(&argv.host, argv.port) - }, - password: if argv.auth.is_empty() { - None - } else { - Some(argv.auth.clone()) - }, - ..Default::default() - }; - let pool = Builder::from_config(config) - .with_connection_config(|config| { - config.max_command_attempts = 3; - config.unresponsive = UnresponsiveConfig { - interval: Duration::from_secs(1), - max_timeout: Some(Duration::from_secs(5)), - }; - config.connection_timeout = Duration::from_secs(3); - config.internal_command_timeout = Duration::from_secs(2); - config.cluster_cache_update_delay = Duration::from_secs(20); - if argv.replicas { - config.replica = ReplicaConfig { - lazy_connections: true, - primary_fallback: true, - connection_error_count: 1, - ..Default::default() - }; - } - }) - .with_performance_config(|config| { - config.auto_pipeline = true; - config.default_command_timeout = Duration::from_secs(60 * 5); - }) - .set_policy(ReconnectPolicy::new_linear(0, 5000, 100)) - .build_pool(argv.pool) - .expect("Failed to create pool"); - - info!("Connecting to {}:{}...", argv.host, argv.port); - pool.init().await?; - info!("Connected to {}:{}", argv.host, argv.port); - pool.flushall_cluster().await?; - - if argv.wait > 0 { - info!("Waiting for {} ms", argv.wait); - sleep(Duration::from_millis(argv.wait)).await; - } - - tokio::spawn(async move { - tokio::signal::ctrl_c().await; - std::process::exit(0); - }); - loop { - if argv.replicas { - let _: Option = pool.replicas().get("foo").await.expect("Failed to GET"); - } else { - let _: i64 = pool.incr("foo").await.expect("Failed to INCR"); - } - sleep(Duration::from_millis(argv.interval)).await; - } -} diff --git a/bin/inf_loop/src/utils.rs b/bin/inf_loop/src/utils.rs deleted file mode 100644 index 8b137891..00000000 --- a/bin/inf_loop/src/utils.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bin/replica_consistency/Cargo.toml b/bin/replica_consistency/Cargo.toml deleted file mode 100644 index 181cfda6..00000000 --- a/bin/replica_consistency/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "replica_consistency" -version = "0.1.0" -authors = ["Alec Embke "] -edition = "2018" - -[profile.release] -debug = true - -[dependencies] -clap = { version = "2.33", features = ["yaml"] } -log = "0.4" -pretty_env_logger = "0.5" -tokio = { version = "1", features = ["full"] } -futures = "0.3" -rand = "0.8" - -[dependencies.fred] -#path = "../.." -path = "/fred" -default-features = false -features = ["network-logs", "debug-ids", "replicas", "custom-reconnect-errors"] - -[features] -default = [] diff --git a/bin/replica_consistency/README.md b/bin/replica_consistency/README.md deleted file mode 100644 index 883cc808..00000000 --- a/bin/replica_consistency/README.md +++ /dev/null @@ -1,26 +0,0 @@ -``` -Run a script that tests cluster consistency. - -USAGE: - replica_consistency [FLAGS] [OPTIONS] - -FLAGS: - --help Prints help information - -V, --version Prints version information - --wait Whether to send `WAIT 1 10` after each `SET` operation - -OPTIONS: - -a, --auth An optional authentication key or password. [default: ] - -c, --concurrency The number of concurrent set-get commands to set each `interval`. [default: 500] - -h, --host The hostname of the redis server. [default: 127.0.0.1] - -i, --interval The time to wait between commands in milliseconds. [default: 500] - -P, --pool The number of clients in the redis connection pool. [default: 1] - -p, --port The port for the redis server. [default: 6379] -``` - -``` -cd path/to/fred -source ./tests/environ -cd bin/replica_consistency -RUST_LOG=replica_consistency=info,fred=debug ./run.sh -a bar -h redis-cluster-1 -p 30001 -P 6 -i 500 -c 500 -``` \ No newline at end of file diff --git a/bin/replica_consistency/cli.yml b/bin/replica_consistency/cli.yml deleted file mode 100644 index 83185549..00000000 --- a/bin/replica_consistency/cli.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: replica_consistency -version: "1.0" -author: Alec Embke -about: Run a script that tests cluster consistency. -args: - - wait: - long: wait - help: Whether to send `WAIT 1 10` after each `SET` operation - takes_value: false - - host: - short: h - long: host - value_name: "STRING" - help: The hostname of the redis server. - takes_value: true - default_value: "127.0.0.1" - - auth: - short: a - long: auth - value_name: "STRING" - help: An optional authentication key or password. - takes_value: true - default_value: "" - - port: - short: p - long: port - value_name: "NUMBER" - help: The port for the redis server. - takes_value: true - default_value: "6379" - - pool: - short: P - long: pool - value_name: "NUMBER" - help: The number of clients in the redis connection pool. - takes_value: true - default_value: "1" - - interval: - short: i - long: interval - value_name: "NUMBER" - help: The time to wait between commands in milliseconds. - takes_value: true - default_value: "500" - - concurrency: - short: c - long: concurrency - value_name: "NUMBER" - help: The number of concurrent set-get commands to set each `interval`. - takes_value: true - default_value: "500" \ No newline at end of file diff --git a/bin/replica_consistency/docker-compose.yml b/bin/replica_consistency/docker-compose.yml deleted file mode 100644 index 768b0128..00000000 --- a/bin/replica_consistency/docker-compose.yml +++ /dev/null @@ -1,24 +0,0 @@ -version: '2' - -services: - replica_consistency: - depends_on: - - redis-main - - redis-cluster-6 - container_name: "replica_consistency" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - entrypoint: "cargo run --release -- ${TEST_ARGV}" - environment: - RUST_LOG: "${RUST_LOG}" - REDIS_VERSION: "${REDIS_VERSION}" - RUST_BACKTRACE: "full" - volumes: - - "../../../bin/replica_consistency:/project" - - "../../..:/fred" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/bin/replica_consistency/run.sh b/bin/replica_consistency/run.sh deleted file mode 100755 index 832d2f29..00000000 --- a/bin/replica_consistency/run.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -TEST_ARGV="$@" docker-compose \ - -f ../../tests/docker/compose/cluster.yml \ - -f ../../tests/docker/compose/centralized.yml \ - -f ./docker-compose.yml \ - run -u $(id -u ${USER}):$(id -g ${USER}) --rm replica_consistency \ No newline at end of file diff --git a/bin/replica_consistency/rustfmt.toml b/bin/replica_consistency/rustfmt.toml deleted file mode 100644 index 08e76d74..00000000 --- a/bin/replica_consistency/rustfmt.toml +++ /dev/null @@ -1,52 +0,0 @@ -binop_separator = "Front" -blank_lines_upper_bound = 1 -brace_style = "SameLineWhere" -combine_control_expr = true -comment_width = 125 -condense_wildcard_suffixes = false -control_brace_style = "AlwaysSameLine" -edition = "2021" -empty_item_single_line = true -enum_discrim_align_threshold = 0 -error_on_line_overflow = false -error_on_unformatted = false -fn_params_layout = "Tall" -fn_single_line = false -force_explicit_abi = true -force_multiline_blocks = false -format_code_in_doc_comments = true -format_macro_bodies = true -format_macro_matchers = true -format_strings = true -hard_tabs = false -imports_granularity = "Crate" -imports_indent = "Block" -imports_layout = "HorizontalVertical" -indent_style = "Block" -inline_attribute_width = 0 -match_arm_blocks = true -match_block_trailing_comma = true -max_width = 118 -merge_derives = true -newline_style = "Auto" -normalize_comments = true -normalize_doc_attributes = true -overflow_delimited_expr = true -remove_nested_parens = true -reorder_impl_items = true -reorder_imports = true -reorder_modules = true -show_parse_errors = true -space_after_colon = true -space_before_colon = false -spaces_around_ranges = true -struct_field_align_threshold = 50 -struct_lit_single_line = true -tab_spaces = 2 -trailing_semicolon = true -type_punctuation_density = "Wide" -use_field_init_shorthand = true -use_small_heuristics = "Default" -use_try_shorthand = true -where_single_line = false -wrap_comments = true diff --git a/bin/replica_consistency/src/main.rs b/bin/replica_consistency/src/main.rs deleted file mode 100644 index c2a08736..00000000 --- a/bin/replica_consistency/src/main.rs +++ /dev/null @@ -1,189 +0,0 @@ -#[macro_use] -extern crate clap; -extern crate fred; -extern crate futures; -extern crate tokio; - -#[macro_use] -extern crate log; -extern crate pretty_env_logger; - -use clap::App; -use fred::{ - bytes::Bytes, - prelude::*, - types::{BackpressureConfig, ReconnectError, ReplicaConfig, UnresponsiveConfig}, -}; -use rand::{self, distributions::Alphanumeric, Rng}; -use std::{ - default::Default, - sync::Arc, - time::{Duration, Instant}, -}; -use tokio::{task::JoinSet, time::sleep}; - -#[derive(Debug)] -struct Argv { - pub host: String, - pub port: u16, - pub pool: usize, - pub interval: u64, - pub concurrency: u64, - pub auth: String, - pub wait: bool, -} - -fn parse_argv() -> Arc { - let yaml = load_yaml!("../cli.yml"); - let matches = App::from_yaml(yaml).get_matches(); - let wait = matches.is_present("wait"); - - let host = matches - .value_of("host") - .map(|v| v.to_owned()) - .unwrap_or("127.0.0.1".into()); - let port = matches - .value_of("port") - .map(|v| v.parse::().expect("Invalid port")) - .unwrap_or(6379); - let pool = matches - .value_of("pool") - .map(|v| v.parse::().expect("Invalid pool")) - .unwrap_or(1); - let interval = matches - .value_of("interval") - .map(|v| v.parse::().expect("Invalid interval")) - .unwrap_or(500); - let concurrency = matches - .value_of("concurrency") - .map(|v| v.parse::().expect("Invalid concurrency")) - .unwrap_or(500); - let auth = matches.value_of("auth").map(|v| v.to_owned()).unwrap_or("".into()); - - Arc::new(Argv { - auth, - host, - port, - pool, - interval, - concurrency, - wait, - }) -} - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - pretty_env_logger::init_timed(); - let argv = parse_argv(); - info!("Running with configuration: {:?}", argv); - - let config = RedisConfig { - server: ServerConfig::new_clustered(vec![(&argv.host, argv.port)]), - password: if argv.auth.is_empty() { - None - } else { - Some(argv.auth.clone()) - }, - ..Default::default() - }; - let pool = Builder::from_config(config) - .with_connection_config(|config| { - config.max_command_attempts = 10; - config.unresponsive = UnresponsiveConfig { - interval: Duration::from_millis(500), - max_timeout: Some(Duration::from_secs(3)), - }; - config.connection_timeout = Duration::from_secs(3); - config.internal_command_timeout = Duration::from_secs(1); - config.cluster_cache_update_delay = Duration::from_millis(100); - // config.cluster_cache_update_delay = Duration::from_secs(20); - config.replica = ReplicaConfig { - connection_error_count: 1, - ..Default::default() - }; - config.reconnect_errors = vec![ - ReconnectError::ClusterDown, - ReconnectError::Loading, - ReconnectError::MasterDown, - ReconnectError::ReadOnly, - ReconnectError::Misconf, - ReconnectError::Busy, - ReconnectError::NoReplicas, - ]; - }) - .with_performance_config(|config| { - config.auto_pipeline = true; - config.backpressure = BackpressureConfig { - max_in_flight_commands: 50_000_000, - ..Default::default() - }; - config.default_command_timeout = Duration::from_secs(60); - }) - .set_policy(ReconnectPolicy::new_constant(0, 50)) - .build_pool(argv.pool) - .expect("Failed to create pool"); - - info!("Connecting to {}:{}...", argv.host, argv.port); - pool.init().await?; - info!("Connected to {}:{}.", argv.host, argv.port); - pool.flushall_cluster().await?; - - tokio::spawn(async move { - tokio::signal::ctrl_c().await; - std::process::exit(0); - }); - - let mut interval = tokio::time::interval(Duration::from_millis(argv.interval)); - loop { - interval.tick().await; - test(&argv, &pool).await.unwrap(); - } -} - -async fn test(argv: &Arc, pool: &RedisPool) -> Result<(), RedisError> { - let start = Instant::now(); - let mut tesks = (0 .. argv.concurrency).fold(JoinSet::new(), |mut tasks, _| { - let pool = pool.clone(); - let should_wait = argv.wait; - tasks.spawn(async move { - if should_wait { - let client = pool.next(); - client.set("foo", 12345u64, None, None, false).await?; - // TODO need a `hash_slot` field on `Options` for `WAIT` to be useful in this context. - client.wait(1, 10).await?; - client.replicas().get::, _>("foo").await - } else { - pool.set("foo", 12345u64, None, None, false).await?; - pool.replicas().get::, _>("foo").await - } - }); - tasks - }); - - let mut success = 0; - let mut errors = 0; - while let Some(res) = tesks.join_next().await { - match res? { - Ok(value) => { - if value != Some(12345u64) { - debug!("Redis error: empty value by key!"); - errors += 1; - } else { - success += 1; - } - }, - Err(e) => { - debug!("Redis error: {e}"); - errors += 1; - }, - } - } - - let elapsed = start.elapsed(); - if errors > 0 { - error!("-[ERR] {success}/{errors} Took {elapsed:?}"); - } else { - info!("+[OK] {success}/{errors} Took {elapsed:?}"); - } - Ok(()) -} diff --git a/bin/replica_consistency/src/utils.rs b/bin/replica_consistency/src/utils.rs deleted file mode 100644 index 8b137891..00000000 --- a/bin/replica_consistency/src/utils.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bin/benchmark_metrics/metrics/.gitkeep b/doc/.lock similarity index 100% rename from bin/benchmark_metrics/metrics/.gitkeep rename to doc/.lock diff --git a/doc/crates.js b/doc/crates.js new file mode 100644 index 00000000..93325520 --- /dev/null +++ b/doc/crates.js @@ -0,0 +1,2 @@ +window.ALL_CRATES = ["fred"]; +//{"start":21,"fragment_lengths":[6]} \ No newline at end of file diff --git a/doc/fred/all.html b/doc/fred/all.html new file mode 100644 index 00000000..b2e18f74 --- /dev/null +++ b/doc/fred/all.html @@ -0,0 +1 @@ +List of all items in this crate

List of all items

Structs

Enums

Traits

Macros

Functions

Type Aliases

Constants

\ No newline at end of file diff --git a/doc/fred/clients/index.html b/doc/fred/clients/index.html new file mode 100644 index 00000000..fb1b5dcc --- /dev/null +++ b/doc/fred/clients/index.html @@ -0,0 +1,2 @@ +fred::clients - Rust

Module fred::clients

source ·
Expand description

Redis client implementations.

+

Structs§

  • Send a series of commands in a pipeline.
  • A cheaply cloneable Redis client struct.
  • A cheaply cloneable round-robin client pool.
  • Replicasreplicas
    A struct for interacting with cluster replica nodes.
  • SentinelClientsentinel-client
    A struct for interacting directly with Sentinel nodes.
  • SubscriberClientsubscriber-client
    A subscriber client that will manage subscription state to any pubsub channels or patterns for the caller.
  • Transactiontransactions
    A cheaply cloneable transaction block.
  • A client interface used to customize command configuration options.
\ No newline at end of file diff --git a/doc/fred/clients/options/struct.WithOptions.html b/doc/fred/clients/options/struct.WithOptions.html new file mode 100644 index 00000000..f651ac65 --- /dev/null +++ b/doc/fred/clients/options/struct.WithOptions.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/clients/struct.WithOptions.html...

+ + + \ No newline at end of file diff --git a/doc/fred/clients/pipeline/struct.Pipeline.html b/doc/fred/clients/pipeline/struct.Pipeline.html new file mode 100644 index 00000000..3d7962dc --- /dev/null +++ b/doc/fred/clients/pipeline/struct.Pipeline.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/clients/struct.Pipeline.html...

+ + + \ No newline at end of file diff --git a/doc/fred/clients/pool/struct.RedisPool.html b/doc/fred/clients/pool/struct.RedisPool.html new file mode 100644 index 00000000..b9cf9c51 --- /dev/null +++ b/doc/fred/clients/pool/struct.RedisPool.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/clients/struct.RedisPool.html...

+ + + \ No newline at end of file diff --git a/doc/fred/clients/pubsub/struct.SubscriberClient.html b/doc/fred/clients/pubsub/struct.SubscriberClient.html new file mode 100644 index 00000000..a9ef02d2 --- /dev/null +++ b/doc/fred/clients/pubsub/struct.SubscriberClient.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/clients/struct.SubscriberClient.html...

+ + + \ No newline at end of file diff --git a/doc/fred/clients/redis/struct.RedisClient.html b/doc/fred/clients/redis/struct.RedisClient.html new file mode 100644 index 00000000..11ba05d3 --- /dev/null +++ b/doc/fred/clients/redis/struct.RedisClient.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/clients/struct.RedisClient.html...

+ + + \ No newline at end of file diff --git a/doc/fred/clients/replica/struct.Replicas.html b/doc/fred/clients/replica/struct.Replicas.html new file mode 100644 index 00000000..d624a14c --- /dev/null +++ b/doc/fred/clients/replica/struct.Replicas.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/clients/struct.Replicas.html...

+ + + \ No newline at end of file diff --git a/doc/fred/clients/sentinel/struct.SentinelClient.html b/doc/fred/clients/sentinel/struct.SentinelClient.html new file mode 100644 index 00000000..5c6c92d2 --- /dev/null +++ b/doc/fred/clients/sentinel/struct.SentinelClient.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/clients/struct.SentinelClient.html...

+ + + \ No newline at end of file diff --git a/doc/fred/clients/sidebar-items.js b/doc/fred/clients/sidebar-items.js new file mode 100644 index 00000000..d764adb1 --- /dev/null +++ b/doc/fred/clients/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["Pipeline","RedisClient","RedisPool","Replicas","SentinelClient","SubscriberClient","Transaction","WithOptions"]}; \ No newline at end of file diff --git a/doc/fred/clients/struct.Pipeline.html b/doc/fred/clients/struct.Pipeline.html new file mode 100644 index 00000000..c1e02e6f --- /dev/null +++ b/doc/fred/clients/struct.Pipeline.html @@ -0,0 +1,2240 @@ +Pipeline in fred::clients - Rust

Struct fred::clients::Pipeline

source ·
pub struct Pipeline<C: ClientLike> { /* private fields */ }
Expand description

Send a series of commands in a pipeline.

+

See the all, last, and try_all functions for more information.

+

Implementations§

source§

impl<C: ClientLike> Pipeline<C>

source

pub async fn all<R>(&self) -> Result<R, RedisError>
where + R: FromRedis,

Send the pipeline and respond with an array of all responses.

+ +
async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  let _ = client.mset(vec![("foo", 1), ("bar", 2)]).await?;
+
+  let pipeline = client.pipeline();
+  let _: () = pipeline.get("foo").await?; // returns when the command is queued in memory
+  let _: () = pipeline.get("bar").await?; // returns when the command is queued in memory
+
+  let results: Vec<i64> = pipeline.all().await?;
+  assert_eq!(results, vec![1, 2]);
+  Ok(())
+}
+
source

pub async fn try_all<R>(&self) -> Vec<RedisResult<R>>
where + R: FromRedis,

Send the pipeline and respond with each individual result.

+

Note: use RedisValue as the return type (and convert as needed) to +support an array of different return types.

+ +
async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  let _ = client.mset(vec![("foo", 1), ("bar", 2)]).await?;  
+
+  let pipeline = client.pipeline();
+  let _: () = pipeline.get("foo").await?;
+  let _: () = pipeline.hgetall("bar").await?; // this will error since `bar` is an integer
+
+  let results = pipeline.try_all::<RedisValue>().await;
+  assert_eq!(results[0].clone()?.convert::<i64>()?, 1);
+  assert!(results[1].is_err());
+
+  Ok(())
+}
+
source

pub async fn last<R>(&self) -> Result<R, RedisError>
where + R: FromRedis,

Send the pipeline and respond with only the result of the last command.

+ +
async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  let pipeline = client.pipeline();
+  let _: () = pipeline.incr("foo").await?; // returns when the command is queued in memory
+  let _: () = pipeline.incr("foo").await?; // returns when the command is queued in memory
+
+  assert_eq!(pipeline.last::<i64>().await?, 2);
+  // pipelines can also be reused
+  assert_eq!(pipeline.last::<i64>().await?, 4);
+  Ok(())
+}
+

Trait Implementations§

source§

impl<C: AclInterface> AclInterface for Pipeline<C>

Available on crate feature i-acl only.
source§

fn acl_setuser<S, V>( + &self, + username: S, + rules: V, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Create an ACL user with the specified rules or modify the rules of an existing user. Read more
source§

fn acl_load(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will reload +the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. Read more
source§

fn acl_save(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will save the +currently defined ACLs from the server memory to the ACL file. Read more
source§

fn acl_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the currently active ACL rules in the Redis server. Read more
source§

fn acl_users<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows a list of all the usernames of the currently configured users in the Redis ACL system. Read more
source§

fn acl_getuser<R, S>(&self, username: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns all the rules defined for an existing ACL user. Read more
source§

fn acl_deluser<R, S>( + &self, + usernames: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Delete all the specified ACL users and terminate all the connections that are authenticated with such users. Read more
source§

fn acl_cat<R>( + &self, + category: Option<Str>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the available ACL categories if called without arguments. If a category name is given, +the command shows all the Redis commands in the specified category. Read more
source§

fn acl_genpass<R>( + &self, + bits: Option<u16>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Generate a password with length bits, returning the password. Read more
source§

fn acl_whoami<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the username the current connection is authenticated with. New connections are authenticated +with the “default” user. Read more
source§

fn acl_log_count<R>( + &self, + count: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read count recent ACL security events. Read more
source§

fn acl_log_reset(&self) -> impl Future<Output = RedisResult<()>>

Clear the ACL security events logs. Read more
source§

impl<C: AuthInterface> AuthInterface for Pipeline<C>

Available on crate feature i-server only.
source§

fn auth<S>( + &self, + username: Option<String>, + password: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Request for authentication in a password-protected Redis server. Returns ok if successful. Read more
source§

fn hello( + &self, + version: RespVersion, + auth: Option<(Str, Str)>, + setname: Option<Str>, +) -> impl Future<Output = RedisResult<()>>

Switch to a different protocol, optionally authenticating in the process. Read more
source§

impl<C: ClientInterface> ClientInterface for Pipeline<C>

Available on crate feature i-client only.
source§

fn client_id<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the ID of the current connection. Read more
source§

fn connection_ids(&self) -> impl Future<Output = HashMap<Server, i64>>

Read the connection IDs for the active connections to each server. Read more
source§

fn client_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns information and statistics about the current client connection in a mostly human readable +format. Read more
source§

fn client_kill<R>( + &self, + filters: Vec<ClientKillFilter>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Close a given connection or set of connections. Read more
source§

fn client_list<R, I>( + &self, + type: Option<ClientKillType>, + ids: Option<Vec<String>>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT LIST command returns information and statistics about the client connections server in a mostly human +readable format. Read more
source§

fn client_getname<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT GETNAME returns the name of the current connection as set by CLIENT SETNAME. Read more
source§

fn client_setname<S>(&self, name: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Assign a name to the current connection. Read more
source§

fn client_pause( + &self, + timeout: i64, + mode: Option<ClientPauseKind>, +) -> impl Future<Output = RedisResult<()>>

CLIENT PAUSE is a connections control command able to suspend all the Redis clients for the specified amount of +time (in milliseconds). Read more
source§

fn client_unpause(&self) -> impl Future<Output = RedisResult<()>>

CLIENT UNPAUSE is used to resume command processing for all clients that were paused by CLIENT PAUSE. Read more
source§

fn client_reply( + &self, + flag: ClientReplyFlag, +) -> impl Future<Output = RedisResult<()>>

The CLIENT REPLY command controls whether the server will reply the client’s commands. The following modes are +available: Read more
source§

fn client_unblock<R, S>( + &self, + id: S, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisValue>,

This command can unblock, from a different connection, a client blocked in a blocking operation, such as for +instance BRPOP or XREAD or WAIT. Read more
source§

fn unblock_self( + &self, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<()>>

A convenience function to unblock any blocked connection on this client.
source§

fn client_tracking<R, T, P>( + &self, + toggle: T, + redirect: Option<i64>, + prefixes: P, + bcast: bool, + optin: bool, + optout: bool, + noloop: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<Toggle>, + T::Error: Into<RedisError>, + P: Into<MultipleStrings>,

Available on crate feature i-tracking only.
This command enables the tracking feature of the Redis server that is used for server assisted client side +caching. Read more
source§

fn client_trackinginfo<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
The command returns information about the current client connection’s use of the server assisted client side +caching feature. Read more
source§

fn client_getredir<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command returns the client ID we are redirecting our tracking notifications to. Read more
source§

fn client_caching<R>( + &self, + enabled: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command controls the tracking of the keys in the next command executed by the connection, when tracking is +enabled in OPTIN or OPTOUT mode. Read more
source§

impl<C: ClientLike> ClientLike for Pipeline<C>

source§

fn id(&self) -> &str

The unique ID identifying this client and underlying connections.
source§

fn client_config(&self) -> RedisConfig

Read the config used to initialize the client.
source§

fn client_reconnect_policy(&self) -> Option<ReconnectPolicy>

Read the reconnect policy used to initialize the client.
source§

fn connection_config(&self) -> &ConnectionConfig

Read the connection config used to initialize the client.
source§

fn protocol_version(&self) -> RespVersion

Read the RESP version used by the client when communicating with the server.
source§

fn has_reconnect_policy(&self) -> bool

Whether the client has a reconnection policy.
source§

fn is_pipelined(&self) -> bool

Whether the client will automatically pipeline commands.
source§

fn is_clustered(&self) -> bool

Whether the client is connected to a cluster.
source§

fn uses_sentinels(&self) -> bool

Whether the client uses the sentinel interface.
source§

fn update_perf_config(&self, config: PerformanceConfig)

Update the internal PerformanceConfig in place with new values.
source§

fn perf_config(&self) -> PerformanceConfig

Read the PerformanceConfig associated with this client.
source§

fn state(&self) -> ClientState

Read the state of the underlying connection(s). Read more
source§

fn is_connected(&self) -> bool

Whether all underlying connections are healthy.
source§

fn active_connections( + &self, +) -> impl Future<Output = Result<Vec<Server>, RedisError>>

Read the set of active connections managed by the client.
source§

fn server_version(&self) -> Option<Version>

Read the server version, if known.
source§

fn set_resolver(&self, resolver: Rc<dyn Resolve>) -> impl Future

Available on crate feature dns only.
Override the DNS resolution logic for the client.
source§

fn connect(&self) -> ConnectHandle

Connect to the server. Read more
source§

fn force_reconnection(&self) -> impl Future<Output = RedisResult<()>>

Force a reconnection to the server(s). Read more
source§

fn wait_for_connect(&self) -> impl Future<Output = RedisResult<()>>

Wait for the result of the next connection attempt. Read more
source§

fn init(&self) -> impl Future<Output = RedisResult<ConnectHandle>>

Initialize a new routing and connection task and wait for it to connect successfully. Read more
source§

fn quit(&self) -> impl Future<Output = RedisResult<()>>

Close the connection to the Redis server. The returned future resolves when the command has been written to the +socket, not when the connection has been fully closed. Some time after this future resolves the future +returned by connect will resolve which indicates that the connection has been fully closed. Read more
source§

fn shutdown( + &self, + flags: Option<ShutdownFlags>, +) -> impl Future<Output = RedisResult<()>>

Available on crate feature i-server only.
Shut down the server and quit the client. Read more
source§

fn flushall<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Delete the keys in all databases. Read more
source§

fn flushall_cluster(&self) -> impl Future<Output = RedisResult<()>>

Delete the keys on all nodes in the cluster. This is a special function that does not map directly to the Redis +interface.
source§

fn ping<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Ping the Redis server. Read more
source§

fn info<R>( + &self, + section: Option<InfoKind>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read info about the server. Read more
source§

fn custom<R, T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command that is not yet supported via another interface on this client. This is most useful when +interacting with third party modules or extensions. Read more
source§

fn custom_raw<T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<Resp3Frame>>
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command similar to custom, but return the response frame directly without any +parsing. Read more
source§

fn with_options(&self, options: &Options) -> WithOptions<Self>

Customize various configuration options on commands.
source§

impl<C: ClusterInterface> ClusterInterface for Pipeline<C>

Available on crate feature i-cluster only.
source§

fn cached_cluster_state(&self) -> Option<ClusterRouting>

Read the cached cluster state used for routing commands to the correct cluster nodes.
source§

fn num_primary_cluster_nodes(&self) -> usize

Read the number of known primary cluster nodes, or 0 if the cluster state is not known.
source§

fn sync_cluster(&self) -> impl Future<Output = Result<(), RedisError>>

Update the cached cluster state and add or remove any changed cluster node connections.
source§

fn cluster_bumpepoch<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Advances the cluster config epoch. Read more
source§

fn cluster_flushslots(&self) -> impl Future<Output = RedisResult<()>>

Deletes all slots from a node. Read more
source§

fn cluster_myid<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the node’s id. Read more
source§

fn cluster_nodes<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read the current cluster node configuration. Read more
source§

fn cluster_saveconfig(&self) -> impl Future<Output = RedisResult<()>>

Forces a node to save the nodes.conf configuration on disk. Read more
source§

fn cluster_slots<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER SLOTS returns details about which cluster slots map to which Redis instances. Read more
source§

fn cluster_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER INFO provides INFO style information about Redis Cluster vital parameters. Read more
source§

fn cluster_add_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

This command is useful in order to modify a node’s view of the cluster configuration. Specifically it assigns a +set of hash slots to the node receiving the command. Read more
source§

fn cluster_count_failure_reports<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns the number of failure reports for the specified node. Read more
source§

fn cluster_count_keys_in_slot<R>( + &self, + slot: u16, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of keys in the specified Redis Cluster hash slot. Read more
source§

fn cluster_del_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

The CLUSTER DELSLOTS command asks a particular Redis Cluster node to forget which master is serving the hash +slots specified as arguments. Read more
source§

fn cluster_failover( + &self, + flag: Option<ClusterFailoverFlag>, +) -> impl Future<Output = RedisResult<()>>

This command, that can only be sent to a Redis Cluster replica node, forces the replica to start a manual +failover of its master instance. Read more
source§

fn cluster_forget<S>(&self, node_id: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command is used in order to remove a node, specified via its node ID, from the set of known nodes of the +Redis Cluster node receiving the command. In other words the specified node is removed from the nodes table of +the node receiving the command. Read more
source§

fn cluster_get_keys_in_slot<R>( + &self, + slot: u16, + count: u64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns an array of keys names stored in the contacted node and hashing to the specified hash slot. Read more
source§

fn cluster_keyslot<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns an integer identifying the hash slot the specified key hashes to. Read more
source§

fn cluster_meet<S>( + &self, + ip: S, + port: u16, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

CLUSTER MEET is used in order to connect different Redis nodes with cluster support enabled, into a working +cluster. Read more
source§

fn cluster_replicate<S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command reconfigures a node as a replica of the specified master. If the node receiving the command is an +empty master, as a side effect of the command, the node role is changed from master to replica. Read more
source§

fn cluster_replicas<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command provides a list of replica nodes replicating from the specified master node. Read more
source§

fn cluster_reset( + &self, + mode: Option<ClusterResetFlag>, +) -> impl Future<Output = RedisResult<()>>

Reset a Redis Cluster node, in a more or less drastic way depending on the reset type, that can be hard or soft. +Note that this command does not work for masters if they hold one or more keys, in that case to completely +reset a master node keys must be removed first, e.g. by using FLUSHALL first, and then CLUSTER RESET. Read more
source§

fn cluster_set_config_epoch( + &self, + epoch: u64, +) -> impl Future<Output = RedisResult<()>>

This command sets a specific config epoch in a fresh node. Read more
source§

fn cluster_setslot( + &self, + slot: u16, + state: ClusterSetSlotState, +) -> impl Future<Output = RedisResult<()>>

CLUSTER SETSLOT is responsible for changing the state of a hash slot in the receiving node in different ways. Read more
source§

impl<C: ConfigInterface> ConfigInterface for Pipeline<C>

Available on crate feature i-config only.
source§

fn config_resetstat(&self) -> impl Future<Output = RedisResult<()>>

Resets the statistics reported by Redis using the INFO command. Read more
source§

fn config_rewrite(&self) -> impl Future<Output = RedisResult<()>>

The CONFIG REWRITE command rewrites the redis.conf file the server was started with, applying the minimal +changes needed to make it reflect the configuration currently used by the server, which may be different +compared to the original one because of the use of the CONFIG SET command. Read more
source§

fn config_get<R, S>(&self, parameter: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The CONFIG GET command is used to read the configuration parameters of a running Redis server. Read more
source§

fn config_set<P, V>( + &self, + parameter: P, + value: V, +) -> impl Future<Output = RedisResult<()>>
where + P: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The CONFIG SET command is used in order to reconfigure the server at run time without the need to restart Redis. Read more
source§

impl<C: ClientLike> Debug for Pipeline<C>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<C: FunctionInterface> FunctionInterface for Pipeline<C>

Available on crate feature i-scripts only.
source§

fn fcall<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Invoke a function. Read more
source§

fn fcall_ro<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

This is a read-only variant of the FCALL command that cannot execute commands that modify data. Read more
source§

fn function_delete<R, S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Delete a library and all its functions. Read more
source§

fn function_delete_cluster<S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Delete a library and all its functions from each cluster node concurrently. Read more
source§

fn function_dump<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the serialized payload of loaded libraries. Read more
source§

fn function_flush<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Deletes all the libraries. Read more
source§

fn function_flush_cluster( + &self, + async: bool, +) -> impl Future<Output = RedisResult<()>>

Deletes all the libraries on all cluster nodes concurrently. Read more
source§

fn function_kill<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Kill a function that is currently executing. Read more
source§

fn function_list<R, S>( + &self, + library_name: Option<S>, + withcode: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Return information about the functions and libraries. Read more
source§

fn function_load<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis. Read more
source§

fn function_load_cluster<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis on all cluster nodes concurrently. Read more
source§

fn function_restore<R, B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload. Read more
source§

fn function_restore_cluster<B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<()>>
where + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload on all cluster nodes concurrently. Read more
source§

fn function_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return information about the function that’s currently running and information about the available execution +engines. Read more
source§

impl<C: GeoInterface> GeoInterface for Pipeline<C>

Available on crate feature i-geo only.
source§

fn geoadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + changed: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: Into<MultipleGeoValues>,

Adds the specified geospatial items (longitude, latitude, name) to the specified key. Read more
source§

fn geohash<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return valid Geohash strings representing the position of one or more elements in a sorted set value +representing a geospatial index (where elements were added using GEOADD). Read more
source§

fn geopos<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by +the sorted set at key. Read more
source§

fn geodist<R, K, S, D>( + &self, + key: K, + src: S, + dest: D, + unit: Option<GeoUnit>, +) -> impl Future<Output = RedisResult<R>>

Return the distance between two members in the geospatial index represented by the sorted set. Read more
source§

fn georadius<R, K, P>( + &self, + key: K, + position: P, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<GeoPosition>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified with the center location and the maximum distance from the center (the radius). Read more
source§

fn georadiusbymember<R, K, V>( + &self, + key: K, + member: V, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

This command is exactly like GEORADIUS with the sole difference that instead of taking, as the center of the +area to query, a longitude and latitude value, it takes the name of a member already existing inside the +geospatial index represented by the sorted set. Read more
source§

fn geosearch<R, K>( + &self, + key: K, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + withcoord: bool, + withdist: bool, + withhash: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified by a given shape. Read more
source§

fn geosearchstore<R, D, S>( + &self, + dest: D, + source: S, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + storedist: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>,

This command is like GEOSEARCH, but stores the result in destination key. Returns the number of members added to +the destination key. Read more
source§

impl<C: HashesInterface> HashesInterface for Pipeline<C>

Available on crate feature i-hashes only.
source§

fn hgetall<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all fields and values of the hash stored at key. Read more
source§

fn hdel<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Removes the specified fields from the hash stored at key. Read more
source§

fn hexists<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns if field is an existing field in the hash stored at key. Read more
source§

fn hget<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the value associated with field in the hash stored at key. Read more
source§

fn hincrby<R, K, F>( + &self, + key: K, + field: F, + increment: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increments the number stored at field in the hash stored at key by increment. Read more
source§

fn hincrbyfloat<R, K, F>( + &self, + key: K, + field: F, + increment: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increment the specified field of a hash stored at key, and representing a floating point number, by the +specified increment. Read more
source§

fn hkeys<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all field names in the hash stored at key. Read more
source§

fn hlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of fields contained in the hash stored at key. Read more
source§

fn hmget<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Returns the values associated with the specified fields in the hash stored at key. Read more
source§

fn hmset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the specified fields to their respective values in the hash stored at key. Read more
source§

fn hset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets fields in the hash stored at key to their provided values. Read more
source§

fn hsetnx<R, K, F, V>( + &self, + key: K, + field: F, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets field in the hash stored at key to value, only if field does not yet exist. Read more
source§

fn hrandfield<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random field from the hash value stored at key. Read more
source§

fn hstrlen<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the string length of the value associated with field in the hash stored at key. Read more
source§

fn hvals<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all values in the hash stored at key. Read more
source§

impl<C: HyperloglogInterface> HyperloglogInterface for Pipeline<C>

Available on crate feature i-hyperloglog only.
source§

fn pfadd<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Adds all the element arguments to the HyperLogLog data structure stored at the variable name specified as first +argument. Read more
source§

fn pfcount<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

When called with a single key, returns the approximated cardinality computed by the HyperLogLog data structure +stored at the specified variable, which is 0 if the variable does not exist. Read more
source§

fn pfmerge<R, D, S>( + &self, + dest: D, + sources: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<MultipleKeys>,

Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of the +observed sets of the source HyperLogLog structures. Read more
source§

impl<C: KeysInterface> KeysInterface for Pipeline<C>

Available on crate feature i-keys only.
source§

fn watch<K>(&self, keys: K) -> impl Future<Output = RedisResult<()>>
where + K: Into<MultipleKeys>,

Marks the given keys to be watched for conditional execution of a transaction. Read more
source§

fn unwatch(&self) -> impl Future<Output = RedisResult<()>>

Flushes all the previously watched keys for a transaction. Read more
source§

fn randomkey<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return a random key from the currently selected database. Read more
source§

fn copy<R, S, D>( + &self, + source: S, + destination: D, + db: Option<u8>, + replace: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

This command copies the value stored at the source key to the destination key. Read more
source§

fn dump<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Serialize the value stored at key in a Redis-specific format and return it as bulk string. Read more
source§

fn restore<R, K>( + &self, + key: K, + ttl: i64, + serialized: RedisValue, + replace: bool, + absttl: bool, + idletime: Option<i64>, + frequency: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Create a key associated with a value that is obtained by deserializing the provided serialized value Read more
source§

fn set<R, K, V>( + &self, + key: K, + value: V, + expire: Option<Expiration>, + options: Option<SetOptions>, + get: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set a value with optional NX|XX, EX|PX|EXAT|PXAT|KEEPTTL, and GET arguments. Read more
source§

fn get<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Read a value from the server. Read more
source§

fn getrange<R, K>( + &self, + key: K, + start: usize, + end: usize, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the substring of the string value stored at key with offsets start and end (both inclusive). Read more
source§

fn setrange<R, K, V>( + &self, + key: K, + offset: u32, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Overwrites part of the string stored at key, starting at the specified offset, for the entire length of +value. Read more
source§

fn getset<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Atomically sets key to value and returns the old value stored at key. Read more
source§

fn getdel<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the value of key and delete the key. This command is similar to GET, except for the fact that it also +deletes the key on success (if and only if the key’s value type is a string). Read more
source§

fn strlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the string value stored at key. An error is returned when key holds a non-string value. Read more
source§

fn del<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Removes the specified keys. A key is ignored if it does not exist. Read more
Unlinks the specified keys. A key is ignored if it does not exist Read more
source§

fn rename<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination. Read more
source§

fn renamenx<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination if destination does not yet exist. Read more
source§

fn append<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Append value to key if it’s a string. Read more
source§

fn mget<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the +special value nil is returned. Read more
source§

fn mset<V>(&self, values: V) -> impl Future<Output = RedisResult<()>>
where + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. Read more
source§

fn msetnx<R, V>(&self, values: V) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. MSETNX will not perform any operation at all even if just a +single key already exists. Read more
source§

fn incr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by_float<R, K>( + &self, + key: K, + val: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increment the string representing a floating point number stored at key by val. If the key does not exist, it +is set to 0 before performing the operation. Read more
source§

fn decr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn decr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn ttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in seconds. Read more
source§

fn pttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in milliseconds. Read more
source§

fn persist<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Remove the existing timeout on a key, turning the key from volatile (a key with an expiration) +to persistent (a key that will never expire as no timeout is associated). Read more
source§

fn expire<R, K>( + &self, + key: K, + seconds: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on key. After the timeout has expired, the key will be automatically deleted. Read more
source§

fn expire_at<R, K>( + &self, + key: K, + timestamp: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on a key based on a UNIX timestamp. Read more
source§

fn pexpire<R, K>( + &self, + key: K, + milliseconds: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command works exactly like EXPIRE but the time to live of the key is specified in milliseconds instead of +seconds. Read more
source§

fn pexpire_at<R, K>( + &self, + key: K, + timestamp: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

PEXPIREAT has the same effect and semantic as EXPIREAT, but the Unix time at which the key will expire is +specified in milliseconds instead of seconds. Read more
source§

fn exists<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns number of keys that exist from the keys arguments. Read more
source§

fn lcs<R, K1, K2>( + &self, + key1: K1, + key2: K2, + len: bool, + idx: bool, + minmatchlen: Option<i64>, + withmatchlen: bool, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis, + K1: Into<RedisKey>, + K2: Into<RedisKey>,

Runs the longest common subsequence algorithm on two keys. Read more
source§

impl<C: ListInterface> ListInterface for Pipeline<C>

Available on crate feature i-lists only.
source§

fn blmpop<R, K>( + &self, + timeout: f64, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::lmpop. Read more
source§

fn blpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BLPOP is a blocking list pop primitive. It is the blocking version of LPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the head of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BRPOP is a blocking list pop primitive. It is the blocking version of RPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the tail of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpoplpush<R, S, D>( + &self, + source: S, + destination: D, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::rpoplpush. Read more
source§

fn blmove<R, S, D>( + &self, + source: S, + destination: D, + source_direction: LMoveDirection, + destination_direction: LMoveDirection, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::lmove. Read more
source§

fn lmpop<R, K>( + &self, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements from the first non-empty list key from the list of provided key names. Read more
source§

fn lindex<R, K>( + &self, + key: K, + index: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the element at index in the list stored at key. Read more
source§

fn linsert<R, K, P, V>( + &self, + key: K, + location: ListLocation, + pivot: P, + element: V, +) -> impl Future<Output = RedisResult<R>>

Inserts element in the list stored at key either before or after the reference value pivot. Read more
source§

fn llen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the list stored at key. Read more
source§

fn lpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the first elements of the list stored at key. Read more
source§

fn lpos<R, K, V>( + &self, + key: K, + element: V, + rank: Option<i64>, + count: Option<i64>, + maxlen: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The command returns the index of matching elements inside a Redis list. Read more
source§

fn lpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the head of the list stored at key. Read more
source§

fn lpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the head of the list stored at key, only if key already exists and holds a list. Read more
source§

fn lrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified elements of the list stored at key. Read more
source§

fn lrem<R, K, V>( + &self, + key: K, + count: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Removes the first count occurrences of elements equal to element from the list stored at key. Read more
source§

fn lset<R, K, V>( + &self, + key: K, + index: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets the list element at index to element. Read more
source§

fn ltrim<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Trim an existing list so that it will contain only the specified range of elements specified. Read more
source§

fn rpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the last elements of the list stored at key. Read more
source§

fn rpoplpush<R, S, D>( + &self, + source: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the last element (tail) of the list stored at source, and pushes the element at +the first element (head) of the list stored at destination. Read more
source§

fn lmove<R, S, D>( + &self, + source: S, + dest: D, + source_direction: LMoveDirection, + dest_direction: LMoveDirection, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the first/last element (head/tail depending on the source direction argument) of +the list stored at source, and pushes the element at the first/last element (head/tail depending on the +destination direction argument) of the list stored at destination. Read more
source§

fn rpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the tail of the list stored at key. Read more
source§

fn rpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the tail of the list stored at key, only if key already exists and holds a list. Read more
source§

fn sort<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, + store: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Returns or stores the elements contained in the list, set or sorted set at key. Read more
source§

fn sort_ro<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Read-only variant of the SORT command. It is exactly like the original SORT but refuses the STORE option and can +safely be used in read-only replicas. Read more
source§

impl<C: MemoryInterface> MemoryInterface for Pipeline<C>

Available on crate feature i-memory only.
source§

fn memory_doctor<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY DOCTOR command reports about different memory-related issues that the Redis server experiences, and +advises about possible remedies. Read more
source§

fn memory_malloc_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY MALLOC-STATS command provides an internal statistics report from the memory allocator. Read more
source§

fn memory_purge(&self) -> impl Future<Output = RedisResult<()>>

The MEMORY PURGE command attempts to purge dirty pages so these can be reclaimed by the allocator. Read more
source§

fn memory_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY STATS command returns an Array reply about the memory usage of the server. Read more
source§

fn memory_usage<R, K>( + &self, + key: K, + samples: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

The MEMORY USAGE command reports the number of bytes that a key and its value require to be stored in RAM. Read more
source§

impl<C: PubsubInterface> PubsubInterface for Pipeline<C>

Available on crate feature i-pubsub only.
source§

fn subscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribe to a channel on the publish-subscribe interface. Read more
source§

fn unsubscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribe from a channel on the PubSub interface. Read more
source§

fn psubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribes the client to the given patterns. Read more
source§

fn punsubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribes the client from the given patterns, or from all of them if none is given. Read more
source§

fn publish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Publish a message on the PubSub interface, returning the number of clients that received the message. Read more
source§

fn ssubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Subscribes the client to the specified shard channels. Read more
source§

fn sunsubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Unsubscribes the client from the given shard channels, or from all of them if none is given. Read more
source§

fn spublish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Posts a message to the given shard channel. Read more
source§

fn pubsub_channels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active channels. Read more
source§

fn pubsub_numpat<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of unique patterns that are subscribed to by clients. Read more
source§

fn pubsub_numsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers (exclusive of clients subscribed to patterns) for the specified channels. Read more
source§

fn pubsub_shardchannels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active shard channels. Read more
source§

fn pubsub_shardnumsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers for the specified shard channels. Read more
source§

impl<C: RediSearchInterface> RediSearchInterface for Pipeline<C>

Available on crate feature i-redisearch only.
source§

fn ft_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns a list of all existing indexes. Read more
source§

fn ft_aggregate<R, I, Q>( + &self, + index: I, + query: Q, + options: FtAggregateOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Run a search query on an index, and perform aggregate transformations on the results. Read more
Search the index with a textual query, returning either documents or just ids. Read more
source§

fn ft_create<R, I>( + &self, + index: I, + options: FtCreateOptions, + schema: Vec<SearchSchema>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Create an index with the given specification. Read more
source§

fn ft_alter<R, I>( + &self, + index: I, + options: FtAlterOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Add a new attribute to the index. Read more
source§

fn ft_aliasadd<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. Read more
source§

fn ft_aliasdel<R, A>(&self, alias: A) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>,

Remove an alias from an index. Read more
source§

fn ft_aliasupdate<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. If the alias is already associated with another index, FT.ALIASUPDATE removes the +alias association with the previous index. Read more
source§

fn ft_config_get<R, S>(&self, option: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Retrieve configuration options. Read more
source§

fn ft_config_set<R, S, V>( + &self, + option: S, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set the value of a RediSearch configuration parameter. Read more
source§

fn ft_cursor_del<R, I, C>( + &self, + index: I, + cursor: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Delete a cursor. Read more
source§

fn ft_cursor_read<R, I, C>( + &self, + index: I, + cursor: C, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Read next results from an existing cursor. Read more
source§

fn ft_dictadd<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Add terms to a dictionary. Read more
source§

fn ft_dictdel<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Remove terms from a dictionary. Read more
source§

fn ft_dictdump<R, D>(&self, dict: D) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>,

Dump all terms in the given dictionary. Read more
source§

fn ft_dropindex<R, I>( + &self, + index: I, + dd: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Delete an index. Read more
source§

fn ft_explain<R, I, Q>( + &self, + index: I, + query: Q, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Return the execution plan for a complex query. Read more
source§

fn ft_info<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Return information and statistics on the index. Read more
source§

fn ft_spellcheck<R, I, Q>( + &self, + index: I, + query: Q, + distance: Option<u8>, + terms: Option<SpellcheckTerms>, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Perform spelling correction on a query, returning suggestions for misspelled terms. Read more
source§

fn ft_sugadd<R, K, S>( + &self, + key: K, + string: S, + score: f64, + incr: bool, + payload: Option<Bytes>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Add a suggestion string to an auto-complete suggestion dictionary. Read more
source§

fn ft_sugdel<R, K, S>( + &self, + key: K, + string: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Delete a string from a suggestion index. Read more
source§

fn ft_sugget<R, K, P>( + &self, + key: K, + prefix: P, + fuzzy: bool, + withscores: bool, + withpayloads: bool, + max: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Get completion suggestions for a prefix. Read more
source§

fn ft_suglen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the size of an auto-complete suggestion dictionary. Read more
source§

fn ft_syndump<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Dump the contents of a synonym group. Read more
source§

fn ft_synupdate<R, I, S, T>( + &self, + index: I, + synonym_group_id: S, + skipinitialscan: bool, + terms: T, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + S: Into<Str>, + T: Into<MultipleStrings>,

Update a synonym group. Read more
source§

fn ft_tagvals<R, I, F>( + &self, + index: I, + field_name: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + F: Into<Str>,

Return a distinct set of values indexed in a Tag field. Read more
source§

impl<C: RedisJsonInterface> RedisJsonInterface for Pipeline<C>

Available on crate feature i-redis-json only.
source§

fn json_arrappend<R, K, P, V>( + &self, + key: K, + path: P, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json values into the array at path after the last element in it. Read more
source§

fn json_arrindex<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + start: Option<i64>, + stop: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Search for the first occurrence of a JSON value in an array. Read more
source§

fn json_arrinsert<R, K, P, V>( + &self, + key: K, + path: P, + index: i64, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Insert the json values into the array at path before the index (shifts to the right). Read more
source§

fn json_arrlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON array at path in key. Read more
source§

fn json_arrpop<R, K, P>( + &self, + key: K, + path: Option<P>, + index: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Remove and return an element from the index in the array Read more
source§

fn json_arrtrim<R, K, P>( + &self, + key: K, + path: P, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Trim an array so that it contains only the specified inclusive range of elements Read more
source§

fn json_clear<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Clear container values (arrays/objects) and set numeric values to 0 Read more
source§

fn json_debug_memory<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report a value’s memory usage in bytes Read more
source§

fn json_del<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Delete a value. Read more
source§

fn json_get<R, K, I, N, S, P>( + &self, + key: K, + indent: Option<I>, + newline: Option<N>, + space: Option<S>, + paths: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<Str>, + N: Into<Str>, + S: Into<Str>, + P: Into<MultipleStrings>,

Return the value at path in JSON serialized form. Read more
source§

fn json_merge<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Merge a given JSON value into matching paths. Read more
source§

fn json_mget<R, K, P>( + &self, + keys: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + P: Into<Str>,

Return the values at path from multiple key arguments. Read more
source§

fn json_mset<R, K, P, V>( + &self, + values: Vec<(K, P, V)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set or update one or more JSON values according to the specified key-path-value triplets. Read more
source§

fn json_numincrby<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Increment the number value stored at path by number Read more
source§

fn json_objkeys<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the keys in the object that’s referenced by path. Read more
source§

fn json_objlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the number of keys in the JSON object at path in key. Read more
source§

fn json_resp<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the JSON in key in Redis serialization protocol specification form. Read more
source§

fn json_set<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + options: Option<SetOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set the JSON value at path in key. Read more
source§

fn json_strappend<R, K, P, V>( + &self, + key: K, + path: Option<P>, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json-string values to the string at path. Read more
source§

fn json_strlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON String at path in key. Read more
source§

fn json_toggle<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Toggle a Boolean value stored at path. Read more
source§

fn json_type<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the type of JSON value at path. Read more
source§

impl<C: ServerInterface> ServerInterface for Pipeline<C>

Available on crate feature i-server only.
source§

fn bgrewriteaof<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Instruct Redis to start an Append Only File rewrite process. Read more
source§

fn bgsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Save the DB in background. Read more
source§

fn dbsize<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the number of keys in the selected database. Read more
source§

fn select(&self, db: u8) -> impl Future<Output = RedisResult<()>>

Select the database this client should use. Read more
source§

fn failover( + &self, + to: Option<(String, u16)>, + force: bool, + abort: bool, + timeout: Option<u32>, +) -> impl Future<Output = RedisResult<()>>

This command will start a coordinated failover between the currently-connected-to master and one of its +replicas. Read more
source§

fn lastsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the UNIX TIME of the last DB save executed with success. Read more
source§

fn wait<R>( + &self, + numreplicas: i64, + timeout: i64, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis,

This command blocks the current client until all the previous write commands are successfully transferred and +acknowledged by at least the specified number of replicas. If the timeout, specified in milliseconds, is +reached, the command returns even if the specified number of replicas were not yet reached. Read more
source§

fn sentinel_primary(&self) -> Option<Server>

Read the primary Redis server identifier returned from the sentinel nodes.
source§

fn sentinel_nodes(&self) -> Option<Vec<Server>>

Read the set of known sentinel nodes.
source§

impl<C: SetsInterface> SetsInterface for Pipeline<C>

Available on crate feature i-sets only.
source§

fn sadd<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Add the specified members to the set stored at key. Read more
source§

fn scard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the set cardinality (number of elements) of the set stored at key. Read more
source§

fn sdiff<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the difference between the first set and all the successive sets. Read more
source§

fn sdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SDIFF, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sinter<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the intersection of all the given sets. Read more
source§

fn sinterstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SINTER, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sismember<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns if member is a member of the set stored at key. Read more
source§

fn smismember<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns whether each member is a member of the set stored at key. Read more
source§

fn smembers<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all the members of the set value stored at key. Read more
source§

fn smove<R, S, D, V>( + &self, + source: S, + dest: D, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Move member from the set at source to the set at destination. Read more
source§

fn spop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns one or more random members from the set value store at key. Read more
source§

fn srandmember<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the set value stored at key. Read more
source§

fn srem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Remove the specified members from the set stored at key. Read more
source§

fn sunion<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the union of all the given sets. Read more
source§

fn sunionstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SUNION, but instead of returning the resulting set, it is stored in destination. Read more
source§

impl<C: SlowlogInterface> SlowlogInterface for Pipeline<C>

Available on crate feature i-slowlog only.
source§

fn slowlog_get<R>( + &self, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read the slow queries log. Read more
source§

fn slowlog_length<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read length of the slow queries log. Read more
source§

fn slowlog_reset(&self) -> impl Future<Output = RedisResult<()>>

This command is used to reset the slow queries log. Read more
source§

impl<C: SortedSetsInterface> SortedSetsInterface for Pipeline<C>

Available on crate feature i-sorted-sets only.
source§

fn bzmpop<R, K>( + &self, + timeout: f64, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zmpop. Read more
source§

fn bzpopmin<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmin. Read more
source§

fn bzpopmax<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmax. Read more
source§

fn zadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + ordering: Option<Ordering>, + changed: bool, + incr: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>

Adds all the specified members with the specified scores to the sorted set stored at key. Read more
source§

fn zcard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the sorted set cardinality (number of elements) of the sorted set stored at key. Read more
source§

fn zcount<R, K>( + &self, + key: K, + min: f64, + max: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of elements in the sorted set at key with a score between min and max. Read more
source§

fn zdiff<R, K>( + &self, + keys: K, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

This command is similar to ZDIFFSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

Computes the difference between the first and all successive input sorted sets and stores the result in +destination. Read more
source§

fn zincrby<R, K, V>( + &self, + key: K, + increment: f64, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Increments the score of member in the sorted set stored at key by increment. Read more
source§

fn zinter<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZINTERSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zinterstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the intersection of the sorted sets given by the specified keys, and stores the result in +destination. Read more
source§

fn zlexcount<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns the number of elements in the sorted set at key with a value between min and +max. Read more
source§

fn zpopmax<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the highest scores in the sorted set stored at key. Read more
source§

fn zpopmin<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the lowest scores in the sorted set stored at key. Read more
source§

fn zmpop<R, K>( + &self, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements, that are member-score pairs, from the first non-empty sorted set in the provided list +of key names. Read more
source§

fn zrandmember<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the sorted set value stored at key. Read more
source§

fn zrangestore<R, D, S, M, N>( + &self, + dest: D, + source: S, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

This command is like ZRANGE, but stores the result in the destination key. Read more
source§

fn zrange<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between min and max. Read more
source§

fn zrevrangebylex<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between max and min. Read more
source§

fn zrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between min and max (including elements +with score equal to min or max). Read more
source§

fn zrevrangebyscore<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between max and min (including +elements with score equal to max or min). Read more
source§

fn zrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from low to high. Read more
source§

fn zrem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Removes the specified members from the sorted set stored at key. Non existing members are ignored. Read more
source§

fn zremrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command removes all elements in the sorted set stored at key between the lexicographical range +specified by min and max. Read more
source§

fn zremrangebyrank<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes all elements in the sorted set stored at key with rank between start and stop. Read more
source§

fn zremrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Removes all elements in the sorted set stored at key with a score between min and max. Read more
source§

fn zrevrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrevrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from high to low. Read more
source§

fn zscore<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the score of member in the sorted set at key. Read more
source§

fn zunion<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZUNIONSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zunionstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the union of the sorted sets given by the specified keys, and stores the result in destination. Read more
source§

fn zmscore<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns the scores associated with the specified members in the sorted set stored at key. Read more
source§

impl<C: StreamsInterface> StreamsInterface for Pipeline<C>

Available on crate feature i-streams only.
source§

fn xinfo_consumers<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

This command returns the list of consumers that belong to the groupname consumer group of the stream stored at +key. Read more
source§

fn xinfo_groups<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns the list of all consumers groups of the stream stored at key. Read more
source§

fn xinfo_stream<R, K>( + &self, + key: K, + full: bool, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns information about the stream stored at key. Read more
source§

fn xadd<R, K, C, I, F>( + &self, + key: K, + nomkstream: bool, + cap: C, + id: I, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<XID>, + F: TryInto<MultipleOrderedPairs>, + F::Error: Into<RedisError>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Appends the specified stream entry to the stream at the specified key. If the key does not exist, as a side +effect of running this command the key is created with a stream value. The creation of stream’s key can be +disabled with the NOMKSTREAM option. Read more
source§

fn xtrim<R, K, C>(&self, key: K, cap: C) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Trims the stream by evicting older entries (entries with lower IDs) if needed. Read more
source§

fn xdel<R, K, S>(&self, key: K, ids: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Removes the specified entries from a stream, and returns the number of entries deleted. Read more
source§

fn xrange_values<Ri, Rk, Rv, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Return the stream entries matching the provided range of IDs, automatically converting to a less verbose type +definition. Read more
source§

fn xrange<R, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

The command returns the stream entries matching a given range of IDs. The range is specified by a minimum +and maximum ID. All the entries having an ID between the two specified or exactly one of the two IDs specified +(closed interval) are returned. Read more
source§

fn xrevrange_values<Ri, Rk, Rv, K, E, S>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Similar to XRANGE, but with the results returned in reverse order. The results will be automatically converted +to a less verbose type definition. Read more
source§

fn xrevrange<R, K, S, E>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

Similar to XRANGE, but with the results returned in reverse order. Read more
source§

fn xlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of entries inside a stream. Read more
source§

fn xread_map<Rk1, Rk2, Rk3, Rv, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xread<R, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xgroup_create<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, + mkstream: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

This command creates a new consumer group uniquely identified by groupname for the stream stored at key. Read more
source§

fn xgroup_createconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Create a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_delconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Delete a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_destroy<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Completely destroy a consumer group. Read more
source§

fn xgroup_setid<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

Set the last delivered ID for a consumer group. Read more
source§

fn xreadgroup_map<Rk1, Rk2, Rk3, Rv, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xreadgroup<R, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xack<R, K, G, I>( + &self, + key: K, + group: G, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + I: Into<MultipleIDs>,

Remove one or more messages from the Pending Entries List (PEL) of a stream consumer group. Read more
source§

fn xclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

A variation of xclaim with a less verbose return type.
source§

fn xclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

In the context of a stream consumer group, this command changes the ownership of a pending message, +so that the new owner is the consumer specified as the command argument. Read more
source§

fn xautoclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<(String, Vec<XReadValue<Ri, Rk, Rv>>)>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. It also converts +the response type to a less verbose type declaration and handles potential differences between RESP2 and RESP3. Read more
source§

fn xautoclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. Read more
source§

fn xpending<R, K, G, A>( + &self, + key: K, + group: G, + args: A, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + A: Into<XPendingArgs>,

Inspect the list of pending messages in a consumer group. Read more
source§

impl<C: TimeSeriesInterface> TimeSeriesInterface for Pipeline<C>

Available on crate feature i-time-series only.
source§

fn ts_add<R, K, T, L>( + &self, + key: K, + timestamp: T, + value: f64, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + on_duplicate: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + T: TryInto<Timestamp>, + T::Error: Into<RedisError>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Append a sample to a time series. Read more
source§

fn ts_alter<R, K, L>( + &self, + key: K, + retention: Option<u64>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Update the retention, chunk size, duplicate policy, and labels of an existing time series. Read more
source§

fn ts_create<R, K, L>( + &self, + key: K, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Create a new time series. Read more
source§

fn ts_createrule<R, S, D>( + &self, + src: S, + dest: D, + aggregation: (Aggregator, u64), + align_timestamp: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Create a compaction rule. Read more
source§

fn ts_decrby<R, K, L>( + &self, + key: K, + subtrahend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given decrement. Read more
source§

fn ts_del<R, K>( + &self, + key: K, + from: i64, + to: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Delete all samples between two timestamps for a given time series. Read more
source§

fn ts_deleterule<R, S, D>( + &self, + src: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Delete a compaction rule. Read more
source§

fn ts_get<R, K>( + &self, + key: K, + latest: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the sample with the highest timestamp from a given time series. Read more
source§

fn ts_incrby<R, K, L>( + &self, + key: K, + addend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given increment. Read more
source§

fn ts_info<R, K>( + &self, + key: K, + debug: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return information and statistics for a time series. Read more
source§

fn ts_madd<R, K, I>(&self, samples: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: IntoIterator<Item = (K, Timestamp, f64)>,

Append new samples to one or more time series. Read more
source§

fn ts_mget<R, L, S, I>( + &self, + latest: bool, + labels: Option<L>, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + L: Into<GetLabels>, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get the sample with the highest timestamp from each time series matching a specific filter. Read more
source§

fn ts_mrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the forward direction. Read more
source§

fn ts_mrevrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the reverse direction. Read more
source§

fn ts_queryindex<R, S, I>( + &self, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get all time series keys matching a filter list. Read more
source§

fn ts_range<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in forward direction. Read more
source§

fn ts_revrange<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in reverse direction. Read more

Auto Trait Implementations§

§

impl<C> Freeze for Pipeline<C>
where + C: Freeze,

§

impl<C> !RefUnwindSafe for Pipeline<C>

§

impl<C> !Send for Pipeline<C>

§

impl<C> !Sync for Pipeline<C>

§

impl<C> Unpin for Pipeline<C>
where + C: Unpin,

§

impl<C> !UnwindSafe for Pipeline<C>

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/clients/struct.RedisClient.html b/doc/fred/clients/struct.RedisClient.html new file mode 100644 index 00000000..43badee4 --- /dev/null +++ b/doc/fred/clients/struct.RedisClient.html @@ -0,0 +1,2368 @@ +RedisClient in fred::clients - Rust

Struct fred::clients::RedisClient

source ·
pub struct RedisClient { /* private fields */ }
Expand description

A cheaply cloneable Redis client struct.

+

Implementations§

source§

impl RedisClient

source

pub fn new( + config: RedisConfig, + perf: Option<PerformanceConfig>, + connection: Option<ConnectionConfig>, + policy: Option<ReconnectPolicy>, +) -> RedisClient

Create a new client instance without connecting to the server.

+

See the builder interface for more information.

+
source

pub fn clone_new(&self) -> Self

Create a new RedisClient from the config provided to this client.

+

The returned client will not be connected to the server.

+
source

pub fn split_cluster(&self) -> Result<Vec<RedisClient>, RedisError>

Split a clustered Redis client into a set of centralized clients - one for each primary node in the cluster.

+

Alternatively, callers can use with_cluster_node to avoid +creating new connections.

+

The clients returned by this function will not be connected to their associated servers. The caller needs to +call connect on each client before sending any commands.

+
source

pub fn scan<P>( + &self, + pattern: P, + count: Option<u32>, + type: Option<ScanType>, +) -> impl Stream<Item = Result<ScanResult, RedisError>>
where + P: Into<Str>,

Incrementally iterate over a set of keys matching the pattern argument, returning count results per page, if +specified.

+

The scan operation can be canceled by dropping the returned stream.

+

https://redis.io/commands/scan

+
source

pub fn scan_cluster<P>( + &self, + pattern: P, + count: Option<u32>, + type: Option<ScanType>, +) -> impl Stream<Item = Result<ScanResult, RedisError>>
where + P: Into<Str>,

Run the SCAN command on each primary/main node in a cluster concurrently.

+

In order for this function to work reliably the cluster state must not change while scanning. If nodes are added +or removed, or hash slots are rebalanced, it may result in missing keys or duplicate keys in the result +stream. See split_cluster for use cases that require scanning to work while the cluster +state changes.

+

Unlike SCAN, HSCAN, etc, the returned stream may continue even if +has_more returns false on a given page of keys.

+
source

pub fn hscan<K, P>( + &self, + key: K, + pattern: P, + count: Option<u32>, +) -> impl Stream<Item = Result<HScanResult, RedisError>>
where + K: Into<RedisKey>, + P: Into<Str>,

Incrementally iterate over pages of the hash map stored at key, returning count results per page, if +specified.

+

https://redis.io/commands/hscan

+
source

pub fn sscan<K, P>( + &self, + key: K, + pattern: P, + count: Option<u32>, +) -> impl Stream<Item = Result<SScanResult, RedisError>>
where + K: Into<RedisKey>, + P: Into<Str>,

Incrementally iterate over pages of the set stored at key, returning count results per page, if specified.

+

https://redis.io/commands/sscan

+
source

pub fn zscan<K, P>( + &self, + key: K, + pattern: P, + count: Option<u32>, +) -> impl Stream<Item = Result<ZScanResult, RedisError>>
where + K: Into<RedisKey>, + P: Into<Str>,

Incrementally iterate over pages of the sorted set stored at key, returning count results per page, if +specified.

+

https://redis.io/commands/zscan

+
source

pub fn pipeline(&self) -> Pipeline<RedisClient>

Send a series of commands in a pipeline.

+
source

pub fn with_cluster_node<S>(&self, server: S) -> WithOptions<Self>
where + S: Into<Server>,

Shorthand to route subsequent commands to the provided server.

+

See with_options for more information.

+ +
async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  // discover servers via the `RedisConfig` or active connections
+  let connections = client.active_connections().await?;
+
+  // ping each node in the cluster individually
+  for server in connections.into_iter() {
+    let _: () = client.with_cluster_node(server).ping().await?;
+  }
+
+  // or use the cached cluster routing table to discover servers
+  let servers = client
+    .cached_cluster_state()
+    .expect("Failed to read cached cluster state")
+    .unique_primary_nodes();
+
+  for server in servers.into_iter() {
+    // verify the server address with `CLIENT INFO`
+    let server_addr = client
+      .with_cluster_node(&server)
+      .client_info::<String>()
+      .await?
+      .split(" ")
+      .find_map(|s| {
+        let parts: Vec<&str> = s.split("=").collect();
+        if parts[0] == "laddr" {
+          Some(parts[1].to_owned())
+        } else {
+          None
+        }
+      })
+      .expect("Failed to read or parse client info.");
+
+    assert_eq!(server_addr, server.to_string());
+  }
+
+  Ok(())
+}
+
source

pub fn replicas(&self) -> Replicas

Available on crate feature replicas only.

Create a client that interacts with replica nodes.

+

Trait Implementations§

source§

impl AclInterface for RedisClient

Available on crate feature i-acl only.
source§

fn acl_setuser<S, V>( + &self, + username: S, + rules: V, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Create an ACL user with the specified rules or modify the rules of an existing user. Read more
source§

fn acl_load(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will reload +the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. Read more
source§

fn acl_save(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will save the +currently defined ACLs from the server memory to the ACL file. Read more
source§

fn acl_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the currently active ACL rules in the Redis server. Read more
source§

fn acl_users<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows a list of all the usernames of the currently configured users in the Redis ACL system. Read more
source§

fn acl_getuser<R, S>(&self, username: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns all the rules defined for an existing ACL user. Read more
source§

fn acl_deluser<R, S>( + &self, + usernames: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Delete all the specified ACL users and terminate all the connections that are authenticated with such users. Read more
source§

fn acl_cat<R>( + &self, + category: Option<Str>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the available ACL categories if called without arguments. If a category name is given, +the command shows all the Redis commands in the specified category. Read more
source§

fn acl_genpass<R>( + &self, + bits: Option<u16>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Generate a password with length bits, returning the password. Read more
source§

fn acl_whoami<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the username the current connection is authenticated with. New connections are authenticated +with the “default” user. Read more
source§

fn acl_log_count<R>( + &self, + count: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read count recent ACL security events. Read more
source§

fn acl_log_reset(&self) -> impl Future<Output = RedisResult<()>>

Clear the ACL security events logs. Read more
source§

impl AuthInterface for RedisClient

source§

fn auth<S>( + &self, + username: Option<String>, + password: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Request for authentication in a password-protected Redis server. Returns ok if successful. Read more
source§

fn hello( + &self, + version: RespVersion, + auth: Option<(Str, Str)>, + setname: Option<Str>, +) -> impl Future<Output = RedisResult<()>>

Switch to a different protocol, optionally authenticating in the process. Read more
source§

impl ClientInterface for RedisClient

Available on crate feature i-client only.
source§

fn client_id<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the ID of the current connection. Read more
source§

fn connection_ids(&self) -> impl Future<Output = HashMap<Server, i64>>

Read the connection IDs for the active connections to each server. Read more
source§

fn client_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns information and statistics about the current client connection in a mostly human readable +format. Read more
source§

fn client_kill<R>( + &self, + filters: Vec<ClientKillFilter>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Close a given connection or set of connections. Read more
source§

fn client_list<R, I>( + &self, + type: Option<ClientKillType>, + ids: Option<Vec<String>>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT LIST command returns information and statistics about the client connections server in a mostly human +readable format. Read more
source§

fn client_getname<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT GETNAME returns the name of the current connection as set by CLIENT SETNAME. Read more
source§

fn client_setname<S>(&self, name: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Assign a name to the current connection. Read more
source§

fn client_pause( + &self, + timeout: i64, + mode: Option<ClientPauseKind>, +) -> impl Future<Output = RedisResult<()>>

CLIENT PAUSE is a connections control command able to suspend all the Redis clients for the specified amount of +time (in milliseconds). Read more
source§

fn client_unpause(&self) -> impl Future<Output = RedisResult<()>>

CLIENT UNPAUSE is used to resume command processing for all clients that were paused by CLIENT PAUSE. Read more
source§

fn client_reply( + &self, + flag: ClientReplyFlag, +) -> impl Future<Output = RedisResult<()>>

The CLIENT REPLY command controls whether the server will reply the client’s commands. The following modes are +available: Read more
source§

fn client_unblock<R, S>( + &self, + id: S, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisValue>,

This command can unblock, from a different connection, a client blocked in a blocking operation, such as for +instance BRPOP or XREAD or WAIT. Read more
source§

fn unblock_self( + &self, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<()>>

A convenience function to unblock any blocked connection on this client.
source§

fn client_tracking<R, T, P>( + &self, + toggle: T, + redirect: Option<i64>, + prefixes: P, + bcast: bool, + optin: bool, + optout: bool, + noloop: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<Toggle>, + T::Error: Into<RedisError>, + P: Into<MultipleStrings>,

Available on crate feature i-tracking only.
This command enables the tracking feature of the Redis server that is used for server assisted client side +caching. Read more
source§

fn client_trackinginfo<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
The command returns information about the current client connection’s use of the server assisted client side +caching feature. Read more
source§

fn client_getredir<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command returns the client ID we are redirecting our tracking notifications to. Read more
source§

fn client_caching<R>( + &self, + enabled: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command controls the tracking of the keys in the next command executed by the connection, when tracking is +enabled in OPTIN or OPTOUT mode. Read more
source§

impl ClientLike for RedisClient

source§

fn id(&self) -> &str

The unique ID identifying this client and underlying connections.
source§

fn client_config(&self) -> RedisConfig

Read the config used to initialize the client.
source§

fn client_reconnect_policy(&self) -> Option<ReconnectPolicy>

Read the reconnect policy used to initialize the client.
source§

fn connection_config(&self) -> &ConnectionConfig

Read the connection config used to initialize the client.
source§

fn protocol_version(&self) -> RespVersion

Read the RESP version used by the client when communicating with the server.
source§

fn has_reconnect_policy(&self) -> bool

Whether the client has a reconnection policy.
source§

fn is_pipelined(&self) -> bool

Whether the client will automatically pipeline commands.
source§

fn is_clustered(&self) -> bool

Whether the client is connected to a cluster.
source§

fn uses_sentinels(&self) -> bool

Whether the client uses the sentinel interface.
source§

fn update_perf_config(&self, config: PerformanceConfig)

Update the internal PerformanceConfig in place with new values.
source§

fn perf_config(&self) -> PerformanceConfig

Read the PerformanceConfig associated with this client.
source§

fn state(&self) -> ClientState

Read the state of the underlying connection(s). Read more
source§

fn is_connected(&self) -> bool

Whether all underlying connections are healthy.
source§

fn active_connections( + &self, +) -> impl Future<Output = Result<Vec<Server>, RedisError>>

Read the set of active connections managed by the client.
source§

fn server_version(&self) -> Option<Version>

Read the server version, if known.
source§

fn set_resolver(&self, resolver: Rc<dyn Resolve>) -> impl Future

Available on crate feature dns only.
Override the DNS resolution logic for the client.
source§

fn connect(&self) -> ConnectHandle

Connect to the server. Read more
source§

fn force_reconnection(&self) -> impl Future<Output = RedisResult<()>>

Force a reconnection to the server(s). Read more
source§

fn wait_for_connect(&self) -> impl Future<Output = RedisResult<()>>

Wait for the result of the next connection attempt. Read more
source§

fn init(&self) -> impl Future<Output = RedisResult<ConnectHandle>>

Initialize a new routing and connection task and wait for it to connect successfully. Read more
source§

fn quit(&self) -> impl Future<Output = RedisResult<()>>

Close the connection to the Redis server. The returned future resolves when the command has been written to the +socket, not when the connection has been fully closed. Some time after this future resolves the future +returned by connect will resolve which indicates that the connection has been fully closed. Read more
source§

fn shutdown( + &self, + flags: Option<ShutdownFlags>, +) -> impl Future<Output = RedisResult<()>>

Available on crate feature i-server only.
Shut down the server and quit the client. Read more
source§

fn flushall<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Delete the keys in all databases. Read more
source§

fn flushall_cluster(&self) -> impl Future<Output = RedisResult<()>>

Delete the keys on all nodes in the cluster. This is a special function that does not map directly to the Redis +interface.
source§

fn ping<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Ping the Redis server. Read more
source§

fn info<R>( + &self, + section: Option<InfoKind>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read info about the server. Read more
source§

fn custom<R, T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command that is not yet supported via another interface on this client. This is most useful when +interacting with third party modules or extensions. Read more
source§

fn custom_raw<T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<Resp3Frame>>
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command similar to custom, but return the response frame directly without any +parsing. Read more
source§

fn with_options(&self, options: &Options) -> WithOptions<Self>

Customize various configuration options on commands.
source§

impl Clone for RedisClient

source§

fn clone(&self) -> RedisClient

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl ClusterInterface for RedisClient

Available on crate feature i-cluster only.
source§

fn cached_cluster_state(&self) -> Option<ClusterRouting>

Read the cached cluster state used for routing commands to the correct cluster nodes.
source§

fn num_primary_cluster_nodes(&self) -> usize

Read the number of known primary cluster nodes, or 0 if the cluster state is not known.
source§

fn sync_cluster(&self) -> impl Future<Output = Result<(), RedisError>>

Update the cached cluster state and add or remove any changed cluster node connections.
source§

fn cluster_bumpepoch<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Advances the cluster config epoch. Read more
source§

fn cluster_flushslots(&self) -> impl Future<Output = RedisResult<()>>

Deletes all slots from a node. Read more
source§

fn cluster_myid<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the node’s id. Read more
source§

fn cluster_nodes<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read the current cluster node configuration. Read more
source§

fn cluster_saveconfig(&self) -> impl Future<Output = RedisResult<()>>

Forces a node to save the nodes.conf configuration on disk. Read more
source§

fn cluster_slots<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER SLOTS returns details about which cluster slots map to which Redis instances. Read more
source§

fn cluster_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER INFO provides INFO style information about Redis Cluster vital parameters. Read more
source§

fn cluster_add_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

This command is useful in order to modify a node’s view of the cluster configuration. Specifically it assigns a +set of hash slots to the node receiving the command. Read more
source§

fn cluster_count_failure_reports<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns the number of failure reports for the specified node. Read more
source§

fn cluster_count_keys_in_slot<R>( + &self, + slot: u16, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of keys in the specified Redis Cluster hash slot. Read more
source§

fn cluster_del_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

The CLUSTER DELSLOTS command asks a particular Redis Cluster node to forget which master is serving the hash +slots specified as arguments. Read more
source§

fn cluster_failover( + &self, + flag: Option<ClusterFailoverFlag>, +) -> impl Future<Output = RedisResult<()>>

This command, that can only be sent to a Redis Cluster replica node, forces the replica to start a manual +failover of its master instance. Read more
source§

fn cluster_forget<S>(&self, node_id: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command is used in order to remove a node, specified via its node ID, from the set of known nodes of the +Redis Cluster node receiving the command. In other words the specified node is removed from the nodes table of +the node receiving the command. Read more
source§

fn cluster_get_keys_in_slot<R>( + &self, + slot: u16, + count: u64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns an array of keys names stored in the contacted node and hashing to the specified hash slot. Read more
source§

fn cluster_keyslot<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns an integer identifying the hash slot the specified key hashes to. Read more
source§

fn cluster_meet<S>( + &self, + ip: S, + port: u16, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

CLUSTER MEET is used in order to connect different Redis nodes with cluster support enabled, into a working +cluster. Read more
source§

fn cluster_replicate<S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command reconfigures a node as a replica of the specified master. If the node receiving the command is an +empty master, as a side effect of the command, the node role is changed from master to replica. Read more
source§

fn cluster_replicas<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command provides a list of replica nodes replicating from the specified master node. Read more
source§

fn cluster_reset( + &self, + mode: Option<ClusterResetFlag>, +) -> impl Future<Output = RedisResult<()>>

Reset a Redis Cluster node, in a more or less drastic way depending on the reset type, that can be hard or soft. +Note that this command does not work for masters if they hold one or more keys, in that case to completely +reset a master node keys must be removed first, e.g. by using FLUSHALL first, and then CLUSTER RESET. Read more
source§

fn cluster_set_config_epoch( + &self, + epoch: u64, +) -> impl Future<Output = RedisResult<()>>

This command sets a specific config epoch in a fresh node. Read more
source§

fn cluster_setslot( + &self, + slot: u16, + state: ClusterSetSlotState, +) -> impl Future<Output = RedisResult<()>>

CLUSTER SETSLOT is responsible for changing the state of a hash slot in the receiving node in different ways. Read more
source§

impl ConfigInterface for RedisClient

Available on crate feature i-config only.
source§

fn config_resetstat(&self) -> impl Future<Output = RedisResult<()>>

Resets the statistics reported by Redis using the INFO command. Read more
source§

fn config_rewrite(&self) -> impl Future<Output = RedisResult<()>>

The CONFIG REWRITE command rewrites the redis.conf file the server was started with, applying the minimal +changes needed to make it reflect the configuration currently used by the server, which may be different +compared to the original one because of the use of the CONFIG SET command. Read more
source§

fn config_get<R, S>(&self, parameter: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The CONFIG GET command is used to read the configuration parameters of a running Redis server. Read more
source§

fn config_set<P, V>( + &self, + parameter: P, + value: V, +) -> impl Future<Output = RedisResult<()>>
where + P: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The CONFIG SET command is used in order to reconfigure the server at run time without the need to restart Redis. Read more
source§

impl Debug for RedisClient

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for RedisClient

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl Display for RedisClient

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl EventInterface for RedisClient

source§

fn on_message<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Message) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each publish-subscribe message. Read more
source§

fn on_keyspace_event<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(KeyspaceEvent) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each keyspace event. Read more
source§

fn on_reconnect<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Server) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each reconnection event. Read more
source§

fn on_cluster_change<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Vec<ClusterStateChange>) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each cluster change event. Read more
source§

fn on_error<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(RedisError) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each connection error event. Read more
source§

fn on_unresponsive<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Server) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function whenever the client detects an unresponsive connection.
source§

fn on_any<Fe, Fr, Fc>( + &self, + error_fn: Fe, + reconnect_fn: Fr, + cluster_change_fn: Fc, +) -> JoinHandle<RedisResult<()>>
where + Fe: Fn(RedisError) -> RedisResult<()> + 'static, + Fr: Fn(Server) -> RedisResult<()> + 'static, + Fc: Fn(Vec<ClusterStateChange>) -> RedisResult<()> + 'static,

Spawn one task that listens for all connection management event types. Read more
source§

fn message_rx(&self) -> BroadcastReceiver<Message>

Listen for messages on the publish-subscribe interface. Read more
source§

fn keyspace_event_rx(&self) -> BroadcastReceiver<KeyspaceEvent>

Listen for keyspace and keyevent notifications on the publish-subscribe interface. Read more
source§

fn reconnect_rx(&self) -> BroadcastReceiver<Server>

Listen for reconnection notifications. Read more
source§

fn cluster_change_rx(&self) -> BroadcastReceiver<Vec<ClusterStateChange>>

Listen for notifications whenever the cluster state changes. Read more
source§

fn error_rx(&self) -> BroadcastReceiver<RedisError>

Listen for protocol and connection errors. This stream can be used to more intelligently handle errors that may +not appear in the request-response cycle, and so cannot be handled by response futures.
source§

fn unresponsive_rx(&self) -> BroadcastReceiver<Server>

Receive a message when the client initiates a reconnection after detecting an unresponsive connection.
source§

impl FunctionInterface for RedisClient

Available on crate feature i-scripts only.
source§

fn fcall<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Invoke a function. Read more
source§

fn fcall_ro<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

This is a read-only variant of the FCALL command that cannot execute commands that modify data. Read more
source§

fn function_delete<R, S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Delete a library and all its functions. Read more
source§

fn function_delete_cluster<S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Delete a library and all its functions from each cluster node concurrently. Read more
source§

fn function_dump<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the serialized payload of loaded libraries. Read more
source§

fn function_flush<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Deletes all the libraries. Read more
source§

fn function_flush_cluster( + &self, + async: bool, +) -> impl Future<Output = RedisResult<()>>

Deletes all the libraries on all cluster nodes concurrently. Read more
source§

fn function_kill<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Kill a function that is currently executing. Read more
source§

fn function_list<R, S>( + &self, + library_name: Option<S>, + withcode: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Return information about the functions and libraries. Read more
source§

fn function_load<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis. Read more
source§

fn function_load_cluster<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis on all cluster nodes concurrently. Read more
source§

fn function_restore<R, B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload. Read more
source§

fn function_restore_cluster<B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<()>>
where + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload on all cluster nodes concurrently. Read more
source§

fn function_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return information about the function that’s currently running and information about the available execution +engines. Read more
source§

impl GeoInterface for RedisClient

Available on crate feature i-geo only.
source§

fn geoadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + changed: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: Into<MultipleGeoValues>,

Adds the specified geospatial items (longitude, latitude, name) to the specified key. Read more
source§

fn geohash<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return valid Geohash strings representing the position of one or more elements in a sorted set value +representing a geospatial index (where elements were added using GEOADD). Read more
source§

fn geopos<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by +the sorted set at key. Read more
source§

fn geodist<R, K, S, D>( + &self, + key: K, + src: S, + dest: D, + unit: Option<GeoUnit>, +) -> impl Future<Output = RedisResult<R>>

Return the distance between two members in the geospatial index represented by the sorted set. Read more
source§

fn georadius<R, K, P>( + &self, + key: K, + position: P, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<GeoPosition>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified with the center location and the maximum distance from the center (the radius). Read more
source§

fn georadiusbymember<R, K, V>( + &self, + key: K, + member: V, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

This command is exactly like GEORADIUS with the sole difference that instead of taking, as the center of the +area to query, a longitude and latitude value, it takes the name of a member already existing inside the +geospatial index represented by the sorted set. Read more
source§

fn geosearch<R, K>( + &self, + key: K, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + withcoord: bool, + withdist: bool, + withhash: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified by a given shape. Read more
source§

fn geosearchstore<R, D, S>( + &self, + dest: D, + source: S, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + storedist: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>,

This command is like GEOSEARCH, but stores the result in destination key. Returns the number of members added to +the destination key. Read more
source§

impl HashesInterface for RedisClient

Available on crate feature i-hashes only.
source§

fn hgetall<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all fields and values of the hash stored at key. Read more
source§

fn hdel<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Removes the specified fields from the hash stored at key. Read more
source§

fn hexists<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns if field is an existing field in the hash stored at key. Read more
source§

fn hget<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the value associated with field in the hash stored at key. Read more
source§

fn hincrby<R, K, F>( + &self, + key: K, + field: F, + increment: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increments the number stored at field in the hash stored at key by increment. Read more
source§

fn hincrbyfloat<R, K, F>( + &self, + key: K, + field: F, + increment: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increment the specified field of a hash stored at key, and representing a floating point number, by the +specified increment. Read more
source§

fn hkeys<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all field names in the hash stored at key. Read more
source§

fn hlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of fields contained in the hash stored at key. Read more
source§

fn hmget<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Returns the values associated with the specified fields in the hash stored at key. Read more
source§

fn hmset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the specified fields to their respective values in the hash stored at key. Read more
source§

fn hset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets fields in the hash stored at key to their provided values. Read more
source§

fn hsetnx<R, K, F, V>( + &self, + key: K, + field: F, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets field in the hash stored at key to value, only if field does not yet exist. Read more
source§

fn hrandfield<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random field from the hash value stored at key. Read more
source§

fn hstrlen<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the string length of the value associated with field in the hash stored at key. Read more
source§

fn hvals<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all values in the hash stored at key. Read more
source§

impl HeartbeatInterface for RedisClient

Available on crate feature i-server only.
source§

fn enable_heartbeat( + &self, + interval: Duration, + break_on_error: bool, +) -> impl Future<Output = RedisResult<()>>

Return a future that will ping the server on an interval.
source§

impl HyperloglogInterface for RedisClient

Available on crate feature i-hyperloglog only.
source§

fn pfadd<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Adds all the element arguments to the HyperLogLog data structure stored at the variable name specified as first +argument. Read more
source§

fn pfcount<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

When called with a single key, returns the approximated cardinality computed by the HyperLogLog data structure +stored at the specified variable, which is 0 if the variable does not exist. Read more
source§

fn pfmerge<R, D, S>( + &self, + dest: D, + sources: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<MultipleKeys>,

Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of the +observed sets of the source HyperLogLog structures. Read more
source§

impl KeysInterface for RedisClient

Available on crate feature i-keys only.
source§

fn watch<K>(&self, keys: K) -> impl Future<Output = RedisResult<()>>
where + K: Into<MultipleKeys>,

Marks the given keys to be watched for conditional execution of a transaction. Read more
source§

fn unwatch(&self) -> impl Future<Output = RedisResult<()>>

Flushes all the previously watched keys for a transaction. Read more
source§

fn randomkey<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return a random key from the currently selected database. Read more
source§

fn copy<R, S, D>( + &self, + source: S, + destination: D, + db: Option<u8>, + replace: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

This command copies the value stored at the source key to the destination key. Read more
source§

fn dump<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Serialize the value stored at key in a Redis-specific format and return it as bulk string. Read more
source§

fn restore<R, K>( + &self, + key: K, + ttl: i64, + serialized: RedisValue, + replace: bool, + absttl: bool, + idletime: Option<i64>, + frequency: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Create a key associated with a value that is obtained by deserializing the provided serialized value Read more
source§

fn set<R, K, V>( + &self, + key: K, + value: V, + expire: Option<Expiration>, + options: Option<SetOptions>, + get: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set a value with optional NX|XX, EX|PX|EXAT|PXAT|KEEPTTL, and GET arguments. Read more
source§

fn get<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Read a value from the server. Read more
source§

fn getrange<R, K>( + &self, + key: K, + start: usize, + end: usize, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the substring of the string value stored at key with offsets start and end (both inclusive). Read more
source§

fn setrange<R, K, V>( + &self, + key: K, + offset: u32, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Overwrites part of the string stored at key, starting at the specified offset, for the entire length of +value. Read more
source§

fn getset<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Atomically sets key to value and returns the old value stored at key. Read more
source§

fn getdel<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the value of key and delete the key. This command is similar to GET, except for the fact that it also +deletes the key on success (if and only if the key’s value type is a string). Read more
source§

fn strlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the string value stored at key. An error is returned when key holds a non-string value. Read more
source§

fn del<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Removes the specified keys. A key is ignored if it does not exist. Read more
Unlinks the specified keys. A key is ignored if it does not exist Read more
source§

fn rename<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination. Read more
source§

fn renamenx<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination if destination does not yet exist. Read more
source§

fn append<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Append value to key if it’s a string. Read more
source§

fn mget<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the +special value nil is returned. Read more
source§

fn mset<V>(&self, values: V) -> impl Future<Output = RedisResult<()>>
where + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. Read more
source§

fn msetnx<R, V>(&self, values: V) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. MSETNX will not perform any operation at all even if just a +single key already exists. Read more
source§

fn incr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by_float<R, K>( + &self, + key: K, + val: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increment the string representing a floating point number stored at key by val. If the key does not exist, it +is set to 0 before performing the operation. Read more
source§

fn decr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn decr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn ttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in seconds. Read more
source§

fn pttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in milliseconds. Read more
source§

fn persist<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Remove the existing timeout on a key, turning the key from volatile (a key with an expiration) +to persistent (a key that will never expire as no timeout is associated). Read more
source§

fn expire<R, K>( + &self, + key: K, + seconds: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on key. After the timeout has expired, the key will be automatically deleted. Read more
source§

fn expire_at<R, K>( + &self, + key: K, + timestamp: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on a key based on a UNIX timestamp. Read more
source§

fn pexpire<R, K>( + &self, + key: K, + milliseconds: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command works exactly like EXPIRE but the time to live of the key is specified in milliseconds instead of +seconds. Read more
source§

fn pexpire_at<R, K>( + &self, + key: K, + timestamp: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

PEXPIREAT has the same effect and semantic as EXPIREAT, but the Unix time at which the key will expire is +specified in milliseconds instead of seconds. Read more
source§

fn exists<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns number of keys that exist from the keys arguments. Read more
source§

fn lcs<R, K1, K2>( + &self, + key1: K1, + key2: K2, + len: bool, + idx: bool, + minmatchlen: Option<i64>, + withmatchlen: bool, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis, + K1: Into<RedisKey>, + K2: Into<RedisKey>,

Runs the longest common subsequence algorithm on two keys. Read more
source§

impl ListInterface for RedisClient

Available on crate feature i-lists only.
source§

fn blmpop<R, K>( + &self, + timeout: f64, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::lmpop. Read more
source§

fn blpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BLPOP is a blocking list pop primitive. It is the blocking version of LPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the head of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BRPOP is a blocking list pop primitive. It is the blocking version of RPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the tail of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpoplpush<R, S, D>( + &self, + source: S, + destination: D, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::rpoplpush. Read more
source§

fn blmove<R, S, D>( + &self, + source: S, + destination: D, + source_direction: LMoveDirection, + destination_direction: LMoveDirection, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::lmove. Read more
source§

fn lmpop<R, K>( + &self, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements from the first non-empty list key from the list of provided key names. Read more
source§

fn lindex<R, K>( + &self, + key: K, + index: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the element at index in the list stored at key. Read more
source§

fn linsert<R, K, P, V>( + &self, + key: K, + location: ListLocation, + pivot: P, + element: V, +) -> impl Future<Output = RedisResult<R>>

Inserts element in the list stored at key either before or after the reference value pivot. Read more
source§

fn llen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the list stored at key. Read more
source§

fn lpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the first elements of the list stored at key. Read more
source§

fn lpos<R, K, V>( + &self, + key: K, + element: V, + rank: Option<i64>, + count: Option<i64>, + maxlen: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The command returns the index of matching elements inside a Redis list. Read more
source§

fn lpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the head of the list stored at key. Read more
source§

fn lpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the head of the list stored at key, only if key already exists and holds a list. Read more
source§

fn lrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified elements of the list stored at key. Read more
source§

fn lrem<R, K, V>( + &self, + key: K, + count: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Removes the first count occurrences of elements equal to element from the list stored at key. Read more
source§

fn lset<R, K, V>( + &self, + key: K, + index: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets the list element at index to element. Read more
source§

fn ltrim<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Trim an existing list so that it will contain only the specified range of elements specified. Read more
source§

fn rpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the last elements of the list stored at key. Read more
source§

fn rpoplpush<R, S, D>( + &self, + source: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the last element (tail) of the list stored at source, and pushes the element at +the first element (head) of the list stored at destination. Read more
source§

fn lmove<R, S, D>( + &self, + source: S, + dest: D, + source_direction: LMoveDirection, + dest_direction: LMoveDirection, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the first/last element (head/tail depending on the source direction argument) of +the list stored at source, and pushes the element at the first/last element (head/tail depending on the +destination direction argument) of the list stored at destination. Read more
source§

fn rpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the tail of the list stored at key. Read more
source§

fn rpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the tail of the list stored at key, only if key already exists and holds a list. Read more
source§

fn sort<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, + store: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Returns or stores the elements contained in the list, set or sorted set at key. Read more
source§

fn sort_ro<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Read-only variant of the SORT command. It is exactly like the original SORT but refuses the STORE option and can +safely be used in read-only replicas. Read more
source§

impl LuaInterface for RedisClient

Available on crate feature i-scripts only.
source§

fn script_load<R, S>(&self, script: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a script into the scripts cache, without executing it. After the specified command is loaded into the +script cache it will be callable using EVALSHA with the correct SHA1 digest of the script. Read more
source§

fn script_load_cluster<R, S>( + &self, + script: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Available on crate feature sha-1 only.
A clustered variant of script_load that loads the script on all primary nodes in a cluster. Read more
source§

fn script_kill(&self) -> impl Future<Output = RedisResult<()>>

Kills the currently executing Lua script, assuming no write operation was yet performed by the script. Read more
source§

fn script_kill_cluster(&self) -> impl Future<Output = RedisResult<()>>

A clustered variant of the script_kill command that issues the command to all primary nodes +in the cluster.
source§

fn script_flush(&self, async: bool) -> impl Future<Output = RedisResult<()>>

Flush the Lua scripts cache. Read more
source§

fn script_flush_cluster( + &self, + async: bool, +) -> impl Future<Output = RedisResult<()>>

A clustered variant of script_flush that flushes the script cache on all primary nodes in +the cluster.
source§

fn script_exists<R, H>(&self, hashes: H) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + H: Into<MultipleStrings>,

Returns information about the existence of the scripts in the script cache. Read more
source§

fn script_debug( + &self, + flag: ScriptDebugFlag, +) -> impl Future<Output = RedisResult<()>>

Set the debug mode for subsequent scripts executed with EVAL. Read more
source§

fn evalsha<R, S, K, V>( + &self, + hash: S, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Evaluates a script cached on the server side by its SHA1 digest. Read more
source§

fn eval<R, S, K, V>( + &self, + script: S, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Evaluate a Lua script on the server. Read more
source§

impl MemoryInterface for RedisClient

Available on crate feature i-memory only.
source§

fn memory_doctor<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY DOCTOR command reports about different memory-related issues that the Redis server experiences, and +advises about possible remedies. Read more
source§

fn memory_malloc_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY MALLOC-STATS command provides an internal statistics report from the memory allocator. Read more
source§

fn memory_purge(&self) -> impl Future<Output = RedisResult<()>>

The MEMORY PURGE command attempts to purge dirty pages so these can be reclaimed by the allocator. Read more
source§

fn memory_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY STATS command returns an Array reply about the memory usage of the server. Read more
source§

fn memory_usage<R, K>( + &self, + key: K, + samples: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

The MEMORY USAGE command reports the number of bytes that a key and its value require to be stored in RAM. Read more
source§

impl MetricsInterface for RedisClient

source§

fn read_redelivery_count(&self) -> usize

Read the number of request redeliveries. Read more
source§

fn take_redelivery_count(&self) -> usize

Read and reset the number of request redeliveries.
source§

fn command_queue_len(&self) -> usize

Read the number of buffered commands that have not yet been sent to the server.
source§

fn read_latency_metrics(&self) -> Stats

Available on crate feature metrics only.
Read latency metrics across all commands. Read more
source§

fn take_latency_metrics(&self) -> Stats

Available on crate feature metrics only.
Read and consume latency metrics, resetting their values afterwards.
source§

fn read_network_latency_metrics(&self) -> Stats

Available on crate feature metrics only.
Read network latency metrics across all commands. Read more
source§

fn take_network_latency_metrics(&self) -> Stats

Available on crate feature metrics only.
Read and consume network latency metrics, resetting their values afterwards.
source§

fn read_req_size_metrics(&self) -> Stats

Available on crate feature metrics only.
Read request payload size metrics across all commands.
source§

fn take_req_size_metrics(&self) -> Stats

Available on crate feature metrics only.
Read and consume request payload size metrics, resetting their values afterwards.
source§

fn read_res_size_metrics(&self) -> Stats

Available on crate feature metrics only.
Read response payload size metrics across all commands.
source§

fn take_res_size_metrics(&self) -> Stats

Available on crate feature metrics only.
Read and consume response payload size metrics, resetting their values afterwards.
source§

impl PubsubInterface for RedisClient

Available on crate feature i-pubsub only.
source§

fn subscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribe to a channel on the publish-subscribe interface. Read more
source§

fn unsubscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribe from a channel on the PubSub interface. Read more
source§

fn psubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribes the client to the given patterns. Read more
source§

fn punsubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribes the client from the given patterns, or from all of them if none is given. Read more
source§

fn publish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Publish a message on the PubSub interface, returning the number of clients that received the message. Read more
source§

fn ssubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Subscribes the client to the specified shard channels. Read more
source§

fn sunsubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Unsubscribes the client from the given shard channels, or from all of them if none is given. Read more
source§

fn spublish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Posts a message to the given shard channel. Read more
source§

fn pubsub_channels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active channels. Read more
source§

fn pubsub_numpat<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of unique patterns that are subscribed to by clients. Read more
source§

fn pubsub_numsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers (exclusive of clients subscribed to patterns) for the specified channels. Read more
source§

fn pubsub_shardchannels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active shard channels. Read more
source§

fn pubsub_shardnumsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers for the specified shard channels. Read more
source§

impl RediSearchInterface for RedisClient

Available on crate feature i-redisearch only.
source§

fn ft_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns a list of all existing indexes. Read more
source§

fn ft_aggregate<R, I, Q>( + &self, + index: I, + query: Q, + options: FtAggregateOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Run a search query on an index, and perform aggregate transformations on the results. Read more
Search the index with a textual query, returning either documents or just ids. Read more
source§

fn ft_create<R, I>( + &self, + index: I, + options: FtCreateOptions, + schema: Vec<SearchSchema>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Create an index with the given specification. Read more
source§

fn ft_alter<R, I>( + &self, + index: I, + options: FtAlterOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Add a new attribute to the index. Read more
source§

fn ft_aliasadd<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. Read more
source§

fn ft_aliasdel<R, A>(&self, alias: A) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>,

Remove an alias from an index. Read more
source§

fn ft_aliasupdate<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. If the alias is already associated with another index, FT.ALIASUPDATE removes the +alias association with the previous index. Read more
source§

fn ft_config_get<R, S>(&self, option: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Retrieve configuration options. Read more
source§

fn ft_config_set<R, S, V>( + &self, + option: S, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set the value of a RediSearch configuration parameter. Read more
source§

fn ft_cursor_del<R, I, C>( + &self, + index: I, + cursor: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Delete a cursor. Read more
source§

fn ft_cursor_read<R, I, C>( + &self, + index: I, + cursor: C, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Read next results from an existing cursor. Read more
source§

fn ft_dictadd<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Add terms to a dictionary. Read more
source§

fn ft_dictdel<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Remove terms from a dictionary. Read more
source§

fn ft_dictdump<R, D>(&self, dict: D) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>,

Dump all terms in the given dictionary. Read more
source§

fn ft_dropindex<R, I>( + &self, + index: I, + dd: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Delete an index. Read more
source§

fn ft_explain<R, I, Q>( + &self, + index: I, + query: Q, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Return the execution plan for a complex query. Read more
source§

fn ft_info<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Return information and statistics on the index. Read more
source§

fn ft_spellcheck<R, I, Q>( + &self, + index: I, + query: Q, + distance: Option<u8>, + terms: Option<SpellcheckTerms>, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Perform spelling correction on a query, returning suggestions for misspelled terms. Read more
source§

fn ft_sugadd<R, K, S>( + &self, + key: K, + string: S, + score: f64, + incr: bool, + payload: Option<Bytes>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Add a suggestion string to an auto-complete suggestion dictionary. Read more
source§

fn ft_sugdel<R, K, S>( + &self, + key: K, + string: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Delete a string from a suggestion index. Read more
source§

fn ft_sugget<R, K, P>( + &self, + key: K, + prefix: P, + fuzzy: bool, + withscores: bool, + withpayloads: bool, + max: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Get completion suggestions for a prefix. Read more
source§

fn ft_suglen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the size of an auto-complete suggestion dictionary. Read more
source§

fn ft_syndump<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Dump the contents of a synonym group. Read more
source§

fn ft_synupdate<R, I, S, T>( + &self, + index: I, + synonym_group_id: S, + skipinitialscan: bool, + terms: T, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + S: Into<Str>, + T: Into<MultipleStrings>,

Update a synonym group. Read more
source§

fn ft_tagvals<R, I, F>( + &self, + index: I, + field_name: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + F: Into<Str>,

Return a distinct set of values indexed in a Tag field. Read more
source§

impl RedisJsonInterface for RedisClient

Available on crate feature i-redis-json only.
source§

fn json_arrappend<R, K, P, V>( + &self, + key: K, + path: P, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json values into the array at path after the last element in it. Read more
source§

fn json_arrindex<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + start: Option<i64>, + stop: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Search for the first occurrence of a JSON value in an array. Read more
source§

fn json_arrinsert<R, K, P, V>( + &self, + key: K, + path: P, + index: i64, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Insert the json values into the array at path before the index (shifts to the right). Read more
source§

fn json_arrlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON array at path in key. Read more
source§

fn json_arrpop<R, K, P>( + &self, + key: K, + path: Option<P>, + index: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Remove and return an element from the index in the array Read more
source§

fn json_arrtrim<R, K, P>( + &self, + key: K, + path: P, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Trim an array so that it contains only the specified inclusive range of elements Read more
source§

fn json_clear<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Clear container values (arrays/objects) and set numeric values to 0 Read more
source§

fn json_debug_memory<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report a value’s memory usage in bytes Read more
source§

fn json_del<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Delete a value. Read more
source§

fn json_get<R, K, I, N, S, P>( + &self, + key: K, + indent: Option<I>, + newline: Option<N>, + space: Option<S>, + paths: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<Str>, + N: Into<Str>, + S: Into<Str>, + P: Into<MultipleStrings>,

Return the value at path in JSON serialized form. Read more
source§

fn json_merge<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Merge a given JSON value into matching paths. Read more
source§

fn json_mget<R, K, P>( + &self, + keys: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + P: Into<Str>,

Return the values at path from multiple key arguments. Read more
source§

fn json_mset<R, K, P, V>( + &self, + values: Vec<(K, P, V)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set or update one or more JSON values according to the specified key-path-value triplets. Read more
source§

fn json_numincrby<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Increment the number value stored at path by number Read more
source§

fn json_objkeys<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the keys in the object that’s referenced by path. Read more
source§

fn json_objlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the number of keys in the JSON object at path in key. Read more
source§

fn json_resp<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the JSON in key in Redis serialization protocol specification form. Read more
source§

fn json_set<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + options: Option<SetOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set the JSON value at path in key. Read more
source§

fn json_strappend<R, K, P, V>( + &self, + key: K, + path: Option<P>, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json-string values to the string at path. Read more
source§

fn json_strlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON String at path in key. Read more
source§

fn json_toggle<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Toggle a Boolean value stored at path. Read more
source§

fn json_type<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the type of JSON value at path. Read more
source§

impl ServerInterface for RedisClient

Available on crate feature i-server only.
source§

fn bgrewriteaof<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Instruct Redis to start an Append Only File rewrite process. Read more
source§

fn bgsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Save the DB in background. Read more
source§

fn dbsize<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the number of keys in the selected database. Read more
source§

fn select(&self, db: u8) -> impl Future<Output = RedisResult<()>>

Select the database this client should use. Read more
source§

fn failover( + &self, + to: Option<(String, u16)>, + force: bool, + abort: bool, + timeout: Option<u32>, +) -> impl Future<Output = RedisResult<()>>

This command will start a coordinated failover between the currently-connected-to master and one of its +replicas. Read more
source§

fn lastsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the UNIX TIME of the last DB save executed with success. Read more
source§

fn wait<R>( + &self, + numreplicas: i64, + timeout: i64, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis,

This command blocks the current client until all the previous write commands are successfully transferred and +acknowledged by at least the specified number of replicas. If the timeout, specified in milliseconds, is +reached, the command returns even if the specified number of replicas were not yet reached. Read more
source§

fn sentinel_primary(&self) -> Option<Server>

Read the primary Redis server identifier returned from the sentinel nodes.
source§

fn sentinel_nodes(&self) -> Option<Vec<Server>>

Read the set of known sentinel nodes.
source§

impl SetsInterface for RedisClient

Available on crate feature i-sets only.
source§

fn sadd<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Add the specified members to the set stored at key. Read more
source§

fn scard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the set cardinality (number of elements) of the set stored at key. Read more
source§

fn sdiff<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the difference between the first set and all the successive sets. Read more
source§

fn sdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SDIFF, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sinter<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the intersection of all the given sets. Read more
source§

fn sinterstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SINTER, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sismember<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns if member is a member of the set stored at key. Read more
source§

fn smismember<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns whether each member is a member of the set stored at key. Read more
source§

fn smembers<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all the members of the set value stored at key. Read more
source§

fn smove<R, S, D, V>( + &self, + source: S, + dest: D, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Move member from the set at source to the set at destination. Read more
source§

fn spop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns one or more random members from the set value store at key. Read more
source§

fn srandmember<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the set value stored at key. Read more
source§

fn srem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Remove the specified members from the set stored at key. Read more
source§

fn sunion<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the union of all the given sets. Read more
source§

fn sunionstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SUNION, but instead of returning the resulting set, it is stored in destination. Read more
source§

impl SlowlogInterface for RedisClient

Available on crate feature i-slowlog only.
source§

fn slowlog_get<R>( + &self, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read the slow queries log. Read more
source§

fn slowlog_length<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read length of the slow queries log. Read more
source§

fn slowlog_reset(&self) -> impl Future<Output = RedisResult<()>>

This command is used to reset the slow queries log. Read more
source§

impl SortedSetsInterface for RedisClient

Available on crate feature i-sorted-sets only.
source§

fn bzmpop<R, K>( + &self, + timeout: f64, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zmpop. Read more
source§

fn bzpopmin<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmin. Read more
source§

fn bzpopmax<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmax. Read more
source§

fn zadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + ordering: Option<Ordering>, + changed: bool, + incr: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>

Adds all the specified members with the specified scores to the sorted set stored at key. Read more
source§

fn zcard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the sorted set cardinality (number of elements) of the sorted set stored at key. Read more
source§

fn zcount<R, K>( + &self, + key: K, + min: f64, + max: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of elements in the sorted set at key with a score between min and max. Read more
source§

fn zdiff<R, K>( + &self, + keys: K, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

This command is similar to ZDIFFSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

Computes the difference between the first and all successive input sorted sets and stores the result in +destination. Read more
source§

fn zincrby<R, K, V>( + &self, + key: K, + increment: f64, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Increments the score of member in the sorted set stored at key by increment. Read more
source§

fn zinter<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZINTERSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zinterstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the intersection of the sorted sets given by the specified keys, and stores the result in +destination. Read more
source§

fn zlexcount<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns the number of elements in the sorted set at key with a value between min and +max. Read more
source§

fn zpopmax<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the highest scores in the sorted set stored at key. Read more
source§

fn zpopmin<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the lowest scores in the sorted set stored at key. Read more
source§

fn zmpop<R, K>( + &self, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements, that are member-score pairs, from the first non-empty sorted set in the provided list +of key names. Read more
source§

fn zrandmember<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the sorted set value stored at key. Read more
source§

fn zrangestore<R, D, S, M, N>( + &self, + dest: D, + source: S, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

This command is like ZRANGE, but stores the result in the destination key. Read more
source§

fn zrange<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between min and max. Read more
source§

fn zrevrangebylex<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between max and min. Read more
source§

fn zrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between min and max (including elements +with score equal to min or max). Read more
source§

fn zrevrangebyscore<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between max and min (including +elements with score equal to max or min). Read more
source§

fn zrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from low to high. Read more
source§

fn zrem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Removes the specified members from the sorted set stored at key. Non existing members are ignored. Read more
source§

fn zremrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command removes all elements in the sorted set stored at key between the lexicographical range +specified by min and max. Read more
source§

fn zremrangebyrank<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes all elements in the sorted set stored at key with rank between start and stop. Read more
source§

fn zremrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Removes all elements in the sorted set stored at key with a score between min and max. Read more
source§

fn zrevrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrevrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from high to low. Read more
source§

fn zscore<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the score of member in the sorted set at key. Read more
source§

fn zunion<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZUNIONSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zunionstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the union of the sorted sets given by the specified keys, and stores the result in destination. Read more
source§

fn zmscore<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns the scores associated with the specified members in the sorted set stored at key. Read more
source§

impl StreamsInterface for RedisClient

Available on crate feature i-streams only.
source§

fn xinfo_consumers<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

This command returns the list of consumers that belong to the groupname consumer group of the stream stored at +key. Read more
source§

fn xinfo_groups<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns the list of all consumers groups of the stream stored at key. Read more
source§

fn xinfo_stream<R, K>( + &self, + key: K, + full: bool, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns information about the stream stored at key. Read more
source§

fn xadd<R, K, C, I, F>( + &self, + key: K, + nomkstream: bool, + cap: C, + id: I, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<XID>, + F: TryInto<MultipleOrderedPairs>, + F::Error: Into<RedisError>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Appends the specified stream entry to the stream at the specified key. If the key does not exist, as a side +effect of running this command the key is created with a stream value. The creation of stream’s key can be +disabled with the NOMKSTREAM option. Read more
source§

fn xtrim<R, K, C>(&self, key: K, cap: C) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Trims the stream by evicting older entries (entries with lower IDs) if needed. Read more
source§

fn xdel<R, K, S>(&self, key: K, ids: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Removes the specified entries from a stream, and returns the number of entries deleted. Read more
source§

fn xrange_values<Ri, Rk, Rv, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Return the stream entries matching the provided range of IDs, automatically converting to a less verbose type +definition. Read more
source§

fn xrange<R, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

The command returns the stream entries matching a given range of IDs. The range is specified by a minimum +and maximum ID. All the entries having an ID between the two specified or exactly one of the two IDs specified +(closed interval) are returned. Read more
source§

fn xrevrange_values<Ri, Rk, Rv, K, E, S>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Similar to XRANGE, but with the results returned in reverse order. The results will be automatically converted +to a less verbose type definition. Read more
source§

fn xrevrange<R, K, S, E>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

Similar to XRANGE, but with the results returned in reverse order. Read more
source§

fn xlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of entries inside a stream. Read more
source§

fn xread_map<Rk1, Rk2, Rk3, Rv, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xread<R, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xgroup_create<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, + mkstream: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

This command creates a new consumer group uniquely identified by groupname for the stream stored at key. Read more
source§

fn xgroup_createconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Create a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_delconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Delete a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_destroy<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Completely destroy a consumer group. Read more
source§

fn xgroup_setid<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

Set the last delivered ID for a consumer group. Read more
source§

fn xreadgroup_map<Rk1, Rk2, Rk3, Rv, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xreadgroup<R, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xack<R, K, G, I>( + &self, + key: K, + group: G, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + I: Into<MultipleIDs>,

Remove one or more messages from the Pending Entries List (PEL) of a stream consumer group. Read more
source§

fn xclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

A variation of xclaim with a less verbose return type.
source§

fn xclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

In the context of a stream consumer group, this command changes the ownership of a pending message, +so that the new owner is the consumer specified as the command argument. Read more
source§

fn xautoclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<(String, Vec<XReadValue<Ri, Rk, Rv>>)>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. It also converts +the response type to a less verbose type declaration and handles potential differences between RESP2 and RESP3. Read more
source§

fn xautoclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. Read more
source§

fn xpending<R, K, G, A>( + &self, + key: K, + group: G, + args: A, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + A: Into<XPendingArgs>,

Inspect the list of pending messages in a consumer group. Read more
source§

impl TimeSeriesInterface for RedisClient

Available on crate feature i-time-series only.
source§

fn ts_add<R, K, T, L>( + &self, + key: K, + timestamp: T, + value: f64, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + on_duplicate: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + T: TryInto<Timestamp>, + T::Error: Into<RedisError>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Append a sample to a time series. Read more
source§

fn ts_alter<R, K, L>( + &self, + key: K, + retention: Option<u64>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Update the retention, chunk size, duplicate policy, and labels of an existing time series. Read more
source§

fn ts_create<R, K, L>( + &self, + key: K, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Create a new time series. Read more
source§

fn ts_createrule<R, S, D>( + &self, + src: S, + dest: D, + aggregation: (Aggregator, u64), + align_timestamp: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Create a compaction rule. Read more
source§

fn ts_decrby<R, K, L>( + &self, + key: K, + subtrahend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given decrement. Read more
source§

fn ts_del<R, K>( + &self, + key: K, + from: i64, + to: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Delete all samples between two timestamps for a given time series. Read more
source§

fn ts_deleterule<R, S, D>( + &self, + src: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Delete a compaction rule. Read more
source§

fn ts_get<R, K>( + &self, + key: K, + latest: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the sample with the highest timestamp from a given time series. Read more
source§

fn ts_incrby<R, K, L>( + &self, + key: K, + addend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given increment. Read more
source§

fn ts_info<R, K>( + &self, + key: K, + debug: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return information and statistics for a time series. Read more
source§

fn ts_madd<R, K, I>(&self, samples: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: IntoIterator<Item = (K, Timestamp, f64)>,

Append new samples to one or more time series. Read more
source§

fn ts_mget<R, L, S, I>( + &self, + latest: bool, + labels: Option<L>, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + L: Into<GetLabels>, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get the sample with the highest timestamp from each time series matching a specific filter. Read more
source§

fn ts_mrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the forward direction. Read more
source§

fn ts_mrevrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the reverse direction. Read more
source§

fn ts_queryindex<R, S, I>( + &self, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get all time series keys matching a filter list. Read more
source§

fn ts_range<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in forward direction. Read more
source§

fn ts_revrange<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in reverse direction. Read more
source§

impl TrackingInterface for RedisClient

Available on crate feature i-tracking only.
source§

fn start_tracking<P>( + &self, + prefixes: P, + bcast: bool, + optin: bool, + optout: bool, + noloop: bool, +) -> impl Future<Output = RedisResult<()>>
where + P: Into<MultipleStrings>,

Send the CLIENT TRACKING command to all connected servers, subscribing to invalidation messages on the same connection. Read more
source§

fn stop_tracking(&self) -> impl Future<Output = RedisResult<()>>

Disable client tracking on all connections.
source§

fn on_invalidation<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Invalidation) -> RedisResult<()> + 'static,

Spawn a task that processes invalidation messages from the server. Read more
source§

fn invalidation_rx(&self) -> BroadcastReceiver<Invalidation>

Subscribe to invalidation messages from the server(s).
source§

impl TransactionInterface for RedisClient

Available on crate feature transactions only.
source§

fn multi(&self) -> Transaction

Enter a MULTI block, executing subsequent commands as a transaction. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/clients/struct.RedisPool.html b/doc/fred/clients/struct.RedisPool.html new file mode 100644 index 00000000..b2126cc6 --- /dev/null +++ b/doc/fred/clients/struct.RedisPool.html @@ -0,0 +1,2247 @@ +RedisPool in fred::clients - Rust

Struct fred::clients::RedisPool

source ·
pub struct RedisPool { /* private fields */ }
Expand description

A cheaply cloneable round-robin client pool.

+

§Restrictions

+

The following interfaces are not implemented on RedisPool:

+ +

In many cases, such as publish, callers can work around this by +adding a call to next, but in some scenarios this may not work. As a general rule, any commands +that change or depend on local connection state will not be implemented directly on RedisPool. Callers can use +clients, next, or last to operate on individual clients if needed.

+

Implementations§

source§

impl RedisPool

source

pub fn from_clients(clients: Vec<RedisClient>) -> Result<Self, RedisError>

Create a new pool from an existing set of clients.

+
source

pub fn new( + config: RedisConfig, + perf: Option<PerformanceConfig>, + connection: Option<ConnectionConfig>, + policy: Option<ReconnectPolicy>, + size: usize, +) -> Result<Self, RedisError>

Create a new pool without connecting to the server.

+

See the builder interface for more information.

+
source

pub fn prefer_connected(&self, val: bool) -> bool

Set whether the client will use next_connected or next when routing +commands among the pooled clients.

+
source

pub fn clients(&self) -> &[RedisClient]

Read the individual clients in the pool.

+
source

pub fn connect_pool(&self) -> Vec<ConnectHandle>

Connect each client to the server, returning the task driving each connection.

+

Use the base connect function to return one handle that drives all connections via join.

+
source

pub fn size(&self) -> usize

Read the size of the pool.

+
source

pub fn next_connected(&self) -> &RedisClient

Read the next connected client that should run the next command.

+
source

pub fn next(&self) -> &RedisClient

Read the client that should run the next command.

+
source

pub fn last(&self) -> &RedisClient

Read the client that ran the last command.

+
source

pub fn replicas(&self) -> Replicas

Available on crate feature replicas only.

Create a client that interacts with the replica nodes associated with the next client.

+

Trait Implementations§

source§

impl AclInterface for RedisPool

Available on crate feature i-acl only.
source§

fn acl_setuser<S, V>( + &self, + username: S, + rules: V, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Create an ACL user with the specified rules or modify the rules of an existing user. Read more
source§

fn acl_load(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will reload +the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. Read more
source§

fn acl_save(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will save the +currently defined ACLs from the server memory to the ACL file. Read more
source§

fn acl_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the currently active ACL rules in the Redis server. Read more
source§

fn acl_users<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows a list of all the usernames of the currently configured users in the Redis ACL system. Read more
source§

fn acl_getuser<R, S>(&self, username: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns all the rules defined for an existing ACL user. Read more
source§

fn acl_deluser<R, S>( + &self, + usernames: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Delete all the specified ACL users and terminate all the connections that are authenticated with such users. Read more
source§

fn acl_cat<R>( + &self, + category: Option<Str>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the available ACL categories if called without arguments. If a category name is given, +the command shows all the Redis commands in the specified category. Read more
source§

fn acl_genpass<R>( + &self, + bits: Option<u16>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Generate a password with length bits, returning the password. Read more
source§

fn acl_whoami<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the username the current connection is authenticated with. New connections are authenticated +with the “default” user. Read more
source§

fn acl_log_count<R>( + &self, + count: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read count recent ACL security events. Read more
source§

fn acl_log_reset(&self) -> impl Future<Output = RedisResult<()>>

Clear the ACL security events logs. Read more
source§

impl ClientInterface for RedisPool

Available on crate feature i-client only.
source§

fn client_id<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the ID of the current connection. Read more
source§

fn connection_ids(&self) -> impl Future<Output = HashMap<Server, i64>>

Read the connection IDs for the active connections to each server. Read more
source§

fn client_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns information and statistics about the current client connection in a mostly human readable +format. Read more
source§

fn client_kill<R>( + &self, + filters: Vec<ClientKillFilter>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Close a given connection or set of connections. Read more
source§

fn client_list<R, I>( + &self, + type: Option<ClientKillType>, + ids: Option<Vec<String>>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT LIST command returns information and statistics about the client connections server in a mostly human +readable format. Read more
source§

fn client_getname<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT GETNAME returns the name of the current connection as set by CLIENT SETNAME. Read more
source§

fn client_setname<S>(&self, name: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Assign a name to the current connection. Read more
source§

fn client_pause( + &self, + timeout: i64, + mode: Option<ClientPauseKind>, +) -> impl Future<Output = RedisResult<()>>

CLIENT PAUSE is a connections control command able to suspend all the Redis clients for the specified amount of +time (in milliseconds). Read more
source§

fn client_unpause(&self) -> impl Future<Output = RedisResult<()>>

CLIENT UNPAUSE is used to resume command processing for all clients that were paused by CLIENT PAUSE. Read more
source§

fn client_reply( + &self, + flag: ClientReplyFlag, +) -> impl Future<Output = RedisResult<()>>

The CLIENT REPLY command controls whether the server will reply the client’s commands. The following modes are +available: Read more
source§

fn client_unblock<R, S>( + &self, + id: S, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisValue>,

This command can unblock, from a different connection, a client blocked in a blocking operation, such as for +instance BRPOP or XREAD or WAIT. Read more
source§

fn unblock_self( + &self, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<()>>

A convenience function to unblock any blocked connection on this client.
source§

fn client_tracking<R, T, P>( + &self, + toggle: T, + redirect: Option<i64>, + prefixes: P, + bcast: bool, + optin: bool, + optout: bool, + noloop: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<Toggle>, + T::Error: Into<RedisError>, + P: Into<MultipleStrings>,

Available on crate feature i-tracking only.
This command enables the tracking feature of the Redis server that is used for server assisted client side +caching. Read more
source§

fn client_trackinginfo<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
The command returns information about the current client connection’s use of the server assisted client side +caching feature. Read more
source§

fn client_getredir<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command returns the client ID we are redirecting our tracking notifications to. Read more
source§

fn client_caching<R>( + &self, + enabled: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command controls the tracking of the keys in the next command executed by the connection, when tracking is +enabled in OPTIN or OPTOUT mode. Read more
source§

impl ClientLike for RedisPool

source§

fn update_perf_config(&self, config: PerformanceConfig)

Update the internal PerformanceConfig on each client in place with new +values.

+
source§

fn active_connections( + &self, +) -> impl Future<Output = Result<Vec<Server>, RedisError>>

Read the set of active connections across all clients in the pool.

+
source§

fn set_resolver(&self, resolver: Rc<dyn Resolve>) -> impl Future

Available on crate feature dns only.

Override the DNS resolution logic for all clients in the pool.

+
source§

fn connect(&self) -> ConnectHandle

Connect each client to the server.

+

This function returns a JoinHandle to a task that drives all connections via join.

+

See connect_pool for a variation of this function that separates the +connection tasks.

+

See init for an alternative shorthand.

+
source§

fn force_reconnection(&self) -> impl Future<Output = RedisResult<()>>

Force a reconnection to the server(s) for each client.

+

When running against a cluster this function will also refresh the cached cluster routing table.

+
source§

fn wait_for_connect(&self) -> impl Future<Output = RedisResult<()>>

Wait for all the clients to connect to the server.

+
source§

fn init(&self) -> impl Future<Output = RedisResult<ConnectHandle>>

Initialize a new routing and connection task for each client and wait for them to connect successfully.

+

The returned ConnectHandle refers to the task that drives the routing and +connection layer for each client via join. It will not finish until the max reconnection count is reached.

+

Callers can also use connect and wait_for_connect separately if +needed.

+ +
use fred::prelude::*;
+
+#[tokio::main]
+async fn main() -> Result<(), RedisError> {
+  let pool = Builder::default_centralized().build_pool(5)?;
+  let connection_task = pool.init().await?;
+
+  // ...
+
+  pool.quit().await?;
+  connection_task.await?
+}
+
source§

fn quit(&self) -> impl Future<Output = RedisResult<()>>

Close the connection to the Redis server for each client. The returned future resolves when the command has been +written to the socket, not when the connection has been fully closed. Some time after this future resolves the +future returned by connect will resolve which indicates that the connection has been fully +closed.

+

This function will also close all error, pubsub message, and reconnection event streams on all clients in the +pool.

+
source§

fn id(&self) -> &str

The unique ID identifying this client and underlying connections.
source§

fn client_config(&self) -> RedisConfig

Read the config used to initialize the client.
source§

fn client_reconnect_policy(&self) -> Option<ReconnectPolicy>

Read the reconnect policy used to initialize the client.
source§

fn connection_config(&self) -> &ConnectionConfig

Read the connection config used to initialize the client.
source§

fn protocol_version(&self) -> RespVersion

Read the RESP version used by the client when communicating with the server.
source§

fn has_reconnect_policy(&self) -> bool

Whether the client has a reconnection policy.
source§

fn is_pipelined(&self) -> bool

Whether the client will automatically pipeline commands.
source§

fn is_clustered(&self) -> bool

Whether the client is connected to a cluster.
source§

fn uses_sentinels(&self) -> bool

Whether the client uses the sentinel interface.
source§

fn perf_config(&self) -> PerformanceConfig

Read the PerformanceConfig associated with this client.
source§

fn state(&self) -> ClientState

Read the state of the underlying connection(s). Read more
source§

fn is_connected(&self) -> bool

Whether all underlying connections are healthy.
source§

fn server_version(&self) -> Option<Version>

Read the server version, if known.
source§

fn shutdown( + &self, + flags: Option<ShutdownFlags>, +) -> impl Future<Output = RedisResult<()>>

Available on crate feature i-server only.
Shut down the server and quit the client. Read more
source§

fn flushall<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Delete the keys in all databases. Read more
source§

fn flushall_cluster(&self) -> impl Future<Output = RedisResult<()>>

Delete the keys on all nodes in the cluster. This is a special function that does not map directly to the Redis +interface.
source§

fn ping<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Ping the Redis server. Read more
source§

fn info<R>( + &self, + section: Option<InfoKind>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read info about the server. Read more
source§

fn custom<R, T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command that is not yet supported via another interface on this client. This is most useful when +interacting with third party modules or extensions. Read more
source§

fn custom_raw<T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<Resp3Frame>>
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command similar to custom, but return the response frame directly without any +parsing. Read more
source§

fn with_options(&self, options: &Options) -> WithOptions<Self>

Customize various configuration options on commands.
source§

impl Clone for RedisPool

source§

fn clone(&self) -> RedisPool

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl ClusterInterface for RedisPool

Available on crate feature i-cluster only.
source§

fn cached_cluster_state(&self) -> Option<ClusterRouting>

Read the cached cluster state used for routing commands to the correct cluster nodes.
source§

fn num_primary_cluster_nodes(&self) -> usize

Read the number of known primary cluster nodes, or 0 if the cluster state is not known.
source§

fn sync_cluster(&self) -> impl Future<Output = Result<(), RedisError>>

Update the cached cluster state and add or remove any changed cluster node connections.
source§

fn cluster_bumpepoch<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Advances the cluster config epoch. Read more
source§

fn cluster_flushslots(&self) -> impl Future<Output = RedisResult<()>>

Deletes all slots from a node. Read more
source§

fn cluster_myid<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the node’s id. Read more
source§

fn cluster_nodes<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read the current cluster node configuration. Read more
source§

fn cluster_saveconfig(&self) -> impl Future<Output = RedisResult<()>>

Forces a node to save the nodes.conf configuration on disk. Read more
source§

fn cluster_slots<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER SLOTS returns details about which cluster slots map to which Redis instances. Read more
source§

fn cluster_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER INFO provides INFO style information about Redis Cluster vital parameters. Read more
source§

fn cluster_add_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

This command is useful in order to modify a node’s view of the cluster configuration. Specifically it assigns a +set of hash slots to the node receiving the command. Read more
source§

fn cluster_count_failure_reports<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns the number of failure reports for the specified node. Read more
source§

fn cluster_count_keys_in_slot<R>( + &self, + slot: u16, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of keys in the specified Redis Cluster hash slot. Read more
source§

fn cluster_del_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

The CLUSTER DELSLOTS command asks a particular Redis Cluster node to forget which master is serving the hash +slots specified as arguments. Read more
source§

fn cluster_failover( + &self, + flag: Option<ClusterFailoverFlag>, +) -> impl Future<Output = RedisResult<()>>

This command, that can only be sent to a Redis Cluster replica node, forces the replica to start a manual +failover of its master instance. Read more
source§

fn cluster_forget<S>(&self, node_id: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command is used in order to remove a node, specified via its node ID, from the set of known nodes of the +Redis Cluster node receiving the command. In other words the specified node is removed from the nodes table of +the node receiving the command. Read more
source§

fn cluster_get_keys_in_slot<R>( + &self, + slot: u16, + count: u64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns an array of keys names stored in the contacted node and hashing to the specified hash slot. Read more
source§

fn cluster_keyslot<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns an integer identifying the hash slot the specified key hashes to. Read more
source§

fn cluster_meet<S>( + &self, + ip: S, + port: u16, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

CLUSTER MEET is used in order to connect different Redis nodes with cluster support enabled, into a working +cluster. Read more
source§

fn cluster_replicate<S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command reconfigures a node as a replica of the specified master. If the node receiving the command is an +empty master, as a side effect of the command, the node role is changed from master to replica. Read more
source§

fn cluster_replicas<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command provides a list of replica nodes replicating from the specified master node. Read more
source§

fn cluster_reset( + &self, + mode: Option<ClusterResetFlag>, +) -> impl Future<Output = RedisResult<()>>

Reset a Redis Cluster node, in a more or less drastic way depending on the reset type, that can be hard or soft. +Note that this command does not work for masters if they hold one or more keys, in that case to completely +reset a master node keys must be removed first, e.g. by using FLUSHALL first, and then CLUSTER RESET. Read more
source§

fn cluster_set_config_epoch( + &self, + epoch: u64, +) -> impl Future<Output = RedisResult<()>>

This command sets a specific config epoch in a fresh node. Read more
source§

fn cluster_setslot( + &self, + slot: u16, + state: ClusterSetSlotState, +) -> impl Future<Output = RedisResult<()>>

CLUSTER SETSLOT is responsible for changing the state of a hash slot in the receiving node in different ways. Read more
source§

impl ConfigInterface for RedisPool

Available on crate feature i-config only.
source§

fn config_resetstat(&self) -> impl Future<Output = RedisResult<()>>

Resets the statistics reported by Redis using the INFO command. Read more
source§

fn config_rewrite(&self) -> impl Future<Output = RedisResult<()>>

The CONFIG REWRITE command rewrites the redis.conf file the server was started with, applying the minimal +changes needed to make it reflect the configuration currently used by the server, which may be different +compared to the original one because of the use of the CONFIG SET command. Read more
source§

fn config_get<R, S>(&self, parameter: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The CONFIG GET command is used to read the configuration parameters of a running Redis server. Read more
source§

fn config_set<P, V>( + &self, + parameter: P, + value: V, +) -> impl Future<Output = RedisResult<()>>
where + P: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The CONFIG SET command is used in order to reconfigure the server at run time without the need to restart Redis. Read more
source§

impl Debug for RedisPool

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl FunctionInterface for RedisPool

Available on crate feature i-scripts only.
source§

fn fcall<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Invoke a function. Read more
source§

fn fcall_ro<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

This is a read-only variant of the FCALL command that cannot execute commands that modify data. Read more
source§

fn function_delete<R, S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Delete a library and all its functions. Read more
source§

fn function_delete_cluster<S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Delete a library and all its functions from each cluster node concurrently. Read more
source§

fn function_dump<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the serialized payload of loaded libraries. Read more
source§

fn function_flush<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Deletes all the libraries. Read more
source§

fn function_flush_cluster( + &self, + async: bool, +) -> impl Future<Output = RedisResult<()>>

Deletes all the libraries on all cluster nodes concurrently. Read more
source§

fn function_kill<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Kill a function that is currently executing. Read more
source§

fn function_list<R, S>( + &self, + library_name: Option<S>, + withcode: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Return information about the functions and libraries. Read more
source§

fn function_load<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis. Read more
source§

fn function_load_cluster<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis on all cluster nodes concurrently. Read more
source§

fn function_restore<R, B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload. Read more
source§

fn function_restore_cluster<B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<()>>
where + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload on all cluster nodes concurrently. Read more
source§

fn function_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return information about the function that’s currently running and information about the available execution +engines. Read more
source§

impl GeoInterface for RedisPool

Available on crate feature i-geo only.
source§

fn geoadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + changed: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: Into<MultipleGeoValues>,

Adds the specified geospatial items (longitude, latitude, name) to the specified key. Read more
source§

fn geohash<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return valid Geohash strings representing the position of one or more elements in a sorted set value +representing a geospatial index (where elements were added using GEOADD). Read more
source§

fn geopos<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by +the sorted set at key. Read more
source§

fn geodist<R, K, S, D>( + &self, + key: K, + src: S, + dest: D, + unit: Option<GeoUnit>, +) -> impl Future<Output = RedisResult<R>>

Return the distance between two members in the geospatial index represented by the sorted set. Read more
source§

fn georadius<R, K, P>( + &self, + key: K, + position: P, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<GeoPosition>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified with the center location and the maximum distance from the center (the radius). Read more
source§

fn georadiusbymember<R, K, V>( + &self, + key: K, + member: V, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

This command is exactly like GEORADIUS with the sole difference that instead of taking, as the center of the +area to query, a longitude and latitude value, it takes the name of a member already existing inside the +geospatial index represented by the sorted set. Read more
source§

fn geosearch<R, K>( + &self, + key: K, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + withcoord: bool, + withdist: bool, + withhash: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified by a given shape. Read more
source§

fn geosearchstore<R, D, S>( + &self, + dest: D, + source: S, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + storedist: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>,

This command is like GEOSEARCH, but stores the result in destination key. Returns the number of members added to +the destination key. Read more
source§

impl HashesInterface for RedisPool

Available on crate feature i-hashes only.
source§

fn hgetall<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all fields and values of the hash stored at key. Read more
source§

fn hdel<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Removes the specified fields from the hash stored at key. Read more
source§

fn hexists<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns if field is an existing field in the hash stored at key. Read more
source§

fn hget<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the value associated with field in the hash stored at key. Read more
source§

fn hincrby<R, K, F>( + &self, + key: K, + field: F, + increment: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increments the number stored at field in the hash stored at key by increment. Read more
source§

fn hincrbyfloat<R, K, F>( + &self, + key: K, + field: F, + increment: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increment the specified field of a hash stored at key, and representing a floating point number, by the +specified increment. Read more
source§

fn hkeys<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all field names in the hash stored at key. Read more
source§

fn hlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of fields contained in the hash stored at key. Read more
source§

fn hmget<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Returns the values associated with the specified fields in the hash stored at key. Read more
source§

fn hmset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the specified fields to their respective values in the hash stored at key. Read more
source§

fn hset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets fields in the hash stored at key to their provided values. Read more
source§

fn hsetnx<R, K, F, V>( + &self, + key: K, + field: F, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets field in the hash stored at key to value, only if field does not yet exist. Read more
source§

fn hrandfield<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random field from the hash value stored at key. Read more
source§

fn hstrlen<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the string length of the value associated with field in the hash stored at key. Read more
source§

fn hvals<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all values in the hash stored at key. Read more
source§

impl HeartbeatInterface for RedisPool

source§

fn enable_heartbeat( + &self, + interval: Duration, + break_on_error: bool, +) -> impl Future<Output = RedisResult<()>>

Return a future that will ping the server on an interval.
source§

impl HyperloglogInterface for RedisPool

Available on crate feature i-hyperloglog only.
source§

fn pfadd<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Adds all the element arguments to the HyperLogLog data structure stored at the variable name specified as first +argument. Read more
source§

fn pfcount<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

When called with a single key, returns the approximated cardinality computed by the HyperLogLog data structure +stored at the specified variable, which is 0 if the variable does not exist. Read more
source§

fn pfmerge<R, D, S>( + &self, + dest: D, + sources: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<MultipleKeys>,

Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of the +observed sets of the source HyperLogLog structures. Read more
source§

impl KeysInterface for RedisPool

Available on crate feature i-keys only.
source§

fn watch<K>(&self, keys: K) -> impl Future<Output = RedisResult<()>>
where + K: Into<MultipleKeys>,

Marks the given keys to be watched for conditional execution of a transaction. Read more
source§

fn unwatch(&self) -> impl Future<Output = RedisResult<()>>

Flushes all the previously watched keys for a transaction. Read more
source§

fn randomkey<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return a random key from the currently selected database. Read more
source§

fn copy<R, S, D>( + &self, + source: S, + destination: D, + db: Option<u8>, + replace: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

This command copies the value stored at the source key to the destination key. Read more
source§

fn dump<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Serialize the value stored at key in a Redis-specific format and return it as bulk string. Read more
source§

fn restore<R, K>( + &self, + key: K, + ttl: i64, + serialized: RedisValue, + replace: bool, + absttl: bool, + idletime: Option<i64>, + frequency: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Create a key associated with a value that is obtained by deserializing the provided serialized value Read more
source§

fn set<R, K, V>( + &self, + key: K, + value: V, + expire: Option<Expiration>, + options: Option<SetOptions>, + get: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set a value with optional NX|XX, EX|PX|EXAT|PXAT|KEEPTTL, and GET arguments. Read more
source§

fn get<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Read a value from the server. Read more
source§

fn getrange<R, K>( + &self, + key: K, + start: usize, + end: usize, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the substring of the string value stored at key with offsets start and end (both inclusive). Read more
source§

fn setrange<R, K, V>( + &self, + key: K, + offset: u32, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Overwrites part of the string stored at key, starting at the specified offset, for the entire length of +value. Read more
source§

fn getset<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Atomically sets key to value and returns the old value stored at key. Read more
source§

fn getdel<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the value of key and delete the key. This command is similar to GET, except for the fact that it also +deletes the key on success (if and only if the key’s value type is a string). Read more
source§

fn strlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the string value stored at key. An error is returned when key holds a non-string value. Read more
source§

fn del<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Removes the specified keys. A key is ignored if it does not exist. Read more
Unlinks the specified keys. A key is ignored if it does not exist Read more
source§

fn rename<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination. Read more
source§

fn renamenx<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination if destination does not yet exist. Read more
source§

fn append<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Append value to key if it’s a string. Read more
source§

fn mget<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the +special value nil is returned. Read more
source§

fn mset<V>(&self, values: V) -> impl Future<Output = RedisResult<()>>
where + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. Read more
source§

fn msetnx<R, V>(&self, values: V) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. MSETNX will not perform any operation at all even if just a +single key already exists. Read more
source§

fn incr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by_float<R, K>( + &self, + key: K, + val: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increment the string representing a floating point number stored at key by val. If the key does not exist, it +is set to 0 before performing the operation. Read more
source§

fn decr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn decr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn ttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in seconds. Read more
source§

fn pttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in milliseconds. Read more
source§

fn persist<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Remove the existing timeout on a key, turning the key from volatile (a key with an expiration) +to persistent (a key that will never expire as no timeout is associated). Read more
source§

fn expire<R, K>( + &self, + key: K, + seconds: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on key. After the timeout has expired, the key will be automatically deleted. Read more
source§

fn expire_at<R, K>( + &self, + key: K, + timestamp: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on a key based on a UNIX timestamp. Read more
source§

fn pexpire<R, K>( + &self, + key: K, + milliseconds: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command works exactly like EXPIRE but the time to live of the key is specified in milliseconds instead of +seconds. Read more
source§

fn pexpire_at<R, K>( + &self, + key: K, + timestamp: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

PEXPIREAT has the same effect and semantic as EXPIREAT, but the Unix time at which the key will expire is +specified in milliseconds instead of seconds. Read more
source§

fn exists<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns number of keys that exist from the keys arguments. Read more
source§

fn lcs<R, K1, K2>( + &self, + key1: K1, + key2: K2, + len: bool, + idx: bool, + minmatchlen: Option<i64>, + withmatchlen: bool, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis, + K1: Into<RedisKey>, + K2: Into<RedisKey>,

Runs the longest common subsequence algorithm on two keys. Read more
source§

impl ListInterface for RedisPool

Available on crate feature i-lists only.
source§

fn blmpop<R, K>( + &self, + timeout: f64, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::lmpop. Read more
source§

fn blpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BLPOP is a blocking list pop primitive. It is the blocking version of LPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the head of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BRPOP is a blocking list pop primitive. It is the blocking version of RPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the tail of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpoplpush<R, S, D>( + &self, + source: S, + destination: D, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::rpoplpush. Read more
source§

fn blmove<R, S, D>( + &self, + source: S, + destination: D, + source_direction: LMoveDirection, + destination_direction: LMoveDirection, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::lmove. Read more
source§

fn lmpop<R, K>( + &self, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements from the first non-empty list key from the list of provided key names. Read more
source§

fn lindex<R, K>( + &self, + key: K, + index: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the element at index in the list stored at key. Read more
source§

fn linsert<R, K, P, V>( + &self, + key: K, + location: ListLocation, + pivot: P, + element: V, +) -> impl Future<Output = RedisResult<R>>

Inserts element in the list stored at key either before or after the reference value pivot. Read more
source§

fn llen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the list stored at key. Read more
source§

fn lpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the first elements of the list stored at key. Read more
source§

fn lpos<R, K, V>( + &self, + key: K, + element: V, + rank: Option<i64>, + count: Option<i64>, + maxlen: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The command returns the index of matching elements inside a Redis list. Read more
source§

fn lpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the head of the list stored at key. Read more
source§

fn lpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the head of the list stored at key, only if key already exists and holds a list. Read more
source§

fn lrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified elements of the list stored at key. Read more
source§

fn lrem<R, K, V>( + &self, + key: K, + count: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Removes the first count occurrences of elements equal to element from the list stored at key. Read more
source§

fn lset<R, K, V>( + &self, + key: K, + index: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets the list element at index to element. Read more
source§

fn ltrim<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Trim an existing list so that it will contain only the specified range of elements specified. Read more
source§

fn rpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the last elements of the list stored at key. Read more
source§

fn rpoplpush<R, S, D>( + &self, + source: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the last element (tail) of the list stored at source, and pushes the element at +the first element (head) of the list stored at destination. Read more
source§

fn lmove<R, S, D>( + &self, + source: S, + dest: D, + source_direction: LMoveDirection, + dest_direction: LMoveDirection, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the first/last element (head/tail depending on the source direction argument) of +the list stored at source, and pushes the element at the first/last element (head/tail depending on the +destination direction argument) of the list stored at destination. Read more
source§

fn rpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the tail of the list stored at key. Read more
source§

fn rpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the tail of the list stored at key, only if key already exists and holds a list. Read more
source§

fn sort<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, + store: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Returns or stores the elements contained in the list, set or sorted set at key. Read more
source§

fn sort_ro<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Read-only variant of the SORT command. It is exactly like the original SORT but refuses the STORE option and can +safely be used in read-only replicas. Read more
source§

impl LuaInterface for RedisPool

Available on crate feature i-scripts only.
source§

fn script_load<R, S>(&self, script: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a script into the scripts cache, without executing it. After the specified command is loaded into the +script cache it will be callable using EVALSHA with the correct SHA1 digest of the script. Read more
source§

fn script_load_cluster<R, S>( + &self, + script: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Available on crate feature sha-1 only.
A clustered variant of script_load that loads the script on all primary nodes in a cluster. Read more
source§

fn script_kill(&self) -> impl Future<Output = RedisResult<()>>

Kills the currently executing Lua script, assuming no write operation was yet performed by the script. Read more
source§

fn script_kill_cluster(&self) -> impl Future<Output = RedisResult<()>>

A clustered variant of the script_kill command that issues the command to all primary nodes +in the cluster.
source§

fn script_flush(&self, async: bool) -> impl Future<Output = RedisResult<()>>

Flush the Lua scripts cache. Read more
source§

fn script_flush_cluster( + &self, + async: bool, +) -> impl Future<Output = RedisResult<()>>

A clustered variant of script_flush that flushes the script cache on all primary nodes in +the cluster.
source§

fn script_exists<R, H>(&self, hashes: H) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + H: Into<MultipleStrings>,

Returns information about the existence of the scripts in the script cache. Read more
source§

fn script_debug( + &self, + flag: ScriptDebugFlag, +) -> impl Future<Output = RedisResult<()>>

Set the debug mode for subsequent scripts executed with EVAL. Read more
source§

fn evalsha<R, S, K, V>( + &self, + hash: S, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Evaluates a script cached on the server side by its SHA1 digest. Read more
source§

fn eval<R, S, K, V>( + &self, + script: S, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Evaluate a Lua script on the server. Read more
source§

impl MemoryInterface for RedisPool

Available on crate feature i-memory only.
source§

fn memory_doctor<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY DOCTOR command reports about different memory-related issues that the Redis server experiences, and +advises about possible remedies. Read more
source§

fn memory_malloc_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY MALLOC-STATS command provides an internal statistics report from the memory allocator. Read more
source§

fn memory_purge(&self) -> impl Future<Output = RedisResult<()>>

The MEMORY PURGE command attempts to purge dirty pages so these can be reclaimed by the allocator. Read more
source§

fn memory_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY STATS command returns an Array reply about the memory usage of the server. Read more
source§

fn memory_usage<R, K>( + &self, + key: K, + samples: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

The MEMORY USAGE command reports the number of bytes that a key and its value require to be stored in RAM. Read more
source§

impl RediSearchInterface for RedisPool

Available on crate feature i-redisearch only.
source§

fn ft_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns a list of all existing indexes. Read more
source§

fn ft_aggregate<R, I, Q>( + &self, + index: I, + query: Q, + options: FtAggregateOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Run a search query on an index, and perform aggregate transformations on the results. Read more
Search the index with a textual query, returning either documents or just ids. Read more
source§

fn ft_create<R, I>( + &self, + index: I, + options: FtCreateOptions, + schema: Vec<SearchSchema>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Create an index with the given specification. Read more
source§

fn ft_alter<R, I>( + &self, + index: I, + options: FtAlterOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Add a new attribute to the index. Read more
source§

fn ft_aliasadd<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. Read more
source§

fn ft_aliasdel<R, A>(&self, alias: A) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>,

Remove an alias from an index. Read more
source§

fn ft_aliasupdate<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. If the alias is already associated with another index, FT.ALIASUPDATE removes the +alias association with the previous index. Read more
source§

fn ft_config_get<R, S>(&self, option: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Retrieve configuration options. Read more
source§

fn ft_config_set<R, S, V>( + &self, + option: S, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set the value of a RediSearch configuration parameter. Read more
source§

fn ft_cursor_del<R, I, C>( + &self, + index: I, + cursor: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Delete a cursor. Read more
source§

fn ft_cursor_read<R, I, C>( + &self, + index: I, + cursor: C, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Read next results from an existing cursor. Read more
source§

fn ft_dictadd<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Add terms to a dictionary. Read more
source§

fn ft_dictdel<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Remove terms from a dictionary. Read more
source§

fn ft_dictdump<R, D>(&self, dict: D) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>,

Dump all terms in the given dictionary. Read more
source§

fn ft_dropindex<R, I>( + &self, + index: I, + dd: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Delete an index. Read more
source§

fn ft_explain<R, I, Q>( + &self, + index: I, + query: Q, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Return the execution plan for a complex query. Read more
source§

fn ft_info<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Return information and statistics on the index. Read more
source§

fn ft_spellcheck<R, I, Q>( + &self, + index: I, + query: Q, + distance: Option<u8>, + terms: Option<SpellcheckTerms>, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Perform spelling correction on a query, returning suggestions for misspelled terms. Read more
source§

fn ft_sugadd<R, K, S>( + &self, + key: K, + string: S, + score: f64, + incr: bool, + payload: Option<Bytes>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Add a suggestion string to an auto-complete suggestion dictionary. Read more
source§

fn ft_sugdel<R, K, S>( + &self, + key: K, + string: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Delete a string from a suggestion index. Read more
source§

fn ft_sugget<R, K, P>( + &self, + key: K, + prefix: P, + fuzzy: bool, + withscores: bool, + withpayloads: bool, + max: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Get completion suggestions for a prefix. Read more
source§

fn ft_suglen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the size of an auto-complete suggestion dictionary. Read more
source§

fn ft_syndump<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Dump the contents of a synonym group. Read more
source§

fn ft_synupdate<R, I, S, T>( + &self, + index: I, + synonym_group_id: S, + skipinitialscan: bool, + terms: T, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + S: Into<Str>, + T: Into<MultipleStrings>,

Update a synonym group. Read more
source§

fn ft_tagvals<R, I, F>( + &self, + index: I, + field_name: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + F: Into<Str>,

Return a distinct set of values indexed in a Tag field. Read more
source§

impl RedisJsonInterface for RedisPool

Available on crate feature i-redis-json only.
source§

fn json_arrappend<R, K, P, V>( + &self, + key: K, + path: P, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json values into the array at path after the last element in it. Read more
source§

fn json_arrindex<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + start: Option<i64>, + stop: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Search for the first occurrence of a JSON value in an array. Read more
source§

fn json_arrinsert<R, K, P, V>( + &self, + key: K, + path: P, + index: i64, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Insert the json values into the array at path before the index (shifts to the right). Read more
source§

fn json_arrlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON array at path in key. Read more
source§

fn json_arrpop<R, K, P>( + &self, + key: K, + path: Option<P>, + index: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Remove and return an element from the index in the array Read more
source§

fn json_arrtrim<R, K, P>( + &self, + key: K, + path: P, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Trim an array so that it contains only the specified inclusive range of elements Read more
source§

fn json_clear<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Clear container values (arrays/objects) and set numeric values to 0 Read more
source§

fn json_debug_memory<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report a value’s memory usage in bytes Read more
source§

fn json_del<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Delete a value. Read more
source§

fn json_get<R, K, I, N, S, P>( + &self, + key: K, + indent: Option<I>, + newline: Option<N>, + space: Option<S>, + paths: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<Str>, + N: Into<Str>, + S: Into<Str>, + P: Into<MultipleStrings>,

Return the value at path in JSON serialized form. Read more
source§

fn json_merge<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Merge a given JSON value into matching paths. Read more
source§

fn json_mget<R, K, P>( + &self, + keys: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + P: Into<Str>,

Return the values at path from multiple key arguments. Read more
source§

fn json_mset<R, K, P, V>( + &self, + values: Vec<(K, P, V)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set or update one or more JSON values according to the specified key-path-value triplets. Read more
source§

fn json_numincrby<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Increment the number value stored at path by number Read more
source§

fn json_objkeys<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the keys in the object that’s referenced by path. Read more
source§

fn json_objlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the number of keys in the JSON object at path in key. Read more
source§

fn json_resp<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the JSON in key in Redis serialization protocol specification form. Read more
source§

fn json_set<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + options: Option<SetOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set the JSON value at path in key. Read more
source§

fn json_strappend<R, K, P, V>( + &self, + key: K, + path: Option<P>, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json-string values to the string at path. Read more
source§

fn json_strlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON String at path in key. Read more
source§

fn json_toggle<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Toggle a Boolean value stored at path. Read more
source§

fn json_type<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the type of JSON value at path. Read more
source§

impl ServerInterface for RedisPool

Available on crate feature i-server only.
source§

fn bgrewriteaof<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Instruct Redis to start an Append Only File rewrite process. Read more
source§

fn bgsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Save the DB in background. Read more
source§

fn dbsize<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the number of keys in the selected database. Read more
source§

fn select(&self, db: u8) -> impl Future<Output = RedisResult<()>>

Select the database this client should use. Read more
source§

fn failover( + &self, + to: Option<(String, u16)>, + force: bool, + abort: bool, + timeout: Option<u32>, +) -> impl Future<Output = RedisResult<()>>

This command will start a coordinated failover between the currently-connected-to master and one of its +replicas. Read more
source§

fn lastsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the UNIX TIME of the last DB save executed with success. Read more
source§

fn wait<R>( + &self, + numreplicas: i64, + timeout: i64, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis,

This command blocks the current client until all the previous write commands are successfully transferred and +acknowledged by at least the specified number of replicas. If the timeout, specified in milliseconds, is +reached, the command returns even if the specified number of replicas were not yet reached. Read more
source§

fn sentinel_primary(&self) -> Option<Server>

Read the primary Redis server identifier returned from the sentinel nodes.
source§

fn sentinel_nodes(&self) -> Option<Vec<Server>>

Read the set of known sentinel nodes.
source§

impl SetsInterface for RedisPool

Available on crate feature i-sets only.
source§

fn sadd<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Add the specified members to the set stored at key. Read more
source§

fn scard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the set cardinality (number of elements) of the set stored at key. Read more
source§

fn sdiff<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the difference between the first set and all the successive sets. Read more
source§

fn sdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SDIFF, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sinter<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the intersection of all the given sets. Read more
source§

fn sinterstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SINTER, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sismember<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns if member is a member of the set stored at key. Read more
source§

fn smismember<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns whether each member is a member of the set stored at key. Read more
source§

fn smembers<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all the members of the set value stored at key. Read more
source§

fn smove<R, S, D, V>( + &self, + source: S, + dest: D, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Move member from the set at source to the set at destination. Read more
source§

fn spop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns one or more random members from the set value store at key. Read more
source§

fn srandmember<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the set value stored at key. Read more
source§

fn srem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Remove the specified members from the set stored at key. Read more
source§

fn sunion<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the union of all the given sets. Read more
source§

fn sunionstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SUNION, but instead of returning the resulting set, it is stored in destination. Read more
source§

impl SlowlogInterface for RedisPool

Available on crate feature i-slowlog only.
source§

fn slowlog_get<R>( + &self, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read the slow queries log. Read more
source§

fn slowlog_length<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read length of the slow queries log. Read more
source§

fn slowlog_reset(&self) -> impl Future<Output = RedisResult<()>>

This command is used to reset the slow queries log. Read more
source§

impl SortedSetsInterface for RedisPool

Available on crate feature i-sorted-sets only.
source§

fn bzmpop<R, K>( + &self, + timeout: f64, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zmpop. Read more
source§

fn bzpopmin<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmin. Read more
source§

fn bzpopmax<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmax. Read more
source§

fn zadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + ordering: Option<Ordering>, + changed: bool, + incr: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>

Adds all the specified members with the specified scores to the sorted set stored at key. Read more
source§

fn zcard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the sorted set cardinality (number of elements) of the sorted set stored at key. Read more
source§

fn zcount<R, K>( + &self, + key: K, + min: f64, + max: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of elements in the sorted set at key with a score between min and max. Read more
source§

fn zdiff<R, K>( + &self, + keys: K, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

This command is similar to ZDIFFSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

Computes the difference between the first and all successive input sorted sets and stores the result in +destination. Read more
source§

fn zincrby<R, K, V>( + &self, + key: K, + increment: f64, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Increments the score of member in the sorted set stored at key by increment. Read more
source§

fn zinter<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZINTERSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zinterstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the intersection of the sorted sets given by the specified keys, and stores the result in +destination. Read more
source§

fn zlexcount<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns the number of elements in the sorted set at key with a value between min and +max. Read more
source§

fn zpopmax<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the highest scores in the sorted set stored at key. Read more
source§

fn zpopmin<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the lowest scores in the sorted set stored at key. Read more
source§

fn zmpop<R, K>( + &self, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements, that are member-score pairs, from the first non-empty sorted set in the provided list +of key names. Read more
source§

fn zrandmember<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the sorted set value stored at key. Read more
source§

fn zrangestore<R, D, S, M, N>( + &self, + dest: D, + source: S, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

This command is like ZRANGE, but stores the result in the destination key. Read more
source§

fn zrange<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between min and max. Read more
source§

fn zrevrangebylex<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between max and min. Read more
source§

fn zrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between min and max (including elements +with score equal to min or max). Read more
source§

fn zrevrangebyscore<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between max and min (including +elements with score equal to max or min). Read more
source§

fn zrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from low to high. Read more
source§

fn zrem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Removes the specified members from the sorted set stored at key. Non existing members are ignored. Read more
source§

fn zremrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command removes all elements in the sorted set stored at key between the lexicographical range +specified by min and max. Read more
source§

fn zremrangebyrank<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes all elements in the sorted set stored at key with rank between start and stop. Read more
source§

fn zremrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Removes all elements in the sorted set stored at key with a score between min and max. Read more
source§

fn zrevrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrevrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from high to low. Read more
source§

fn zscore<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the score of member in the sorted set at key. Read more
source§

fn zunion<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZUNIONSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zunionstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the union of the sorted sets given by the specified keys, and stores the result in destination. Read more
source§

fn zmscore<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns the scores associated with the specified members in the sorted set stored at key. Read more
source§

impl StreamsInterface for RedisPool

Available on crate feature i-streams only.
source§

fn xinfo_consumers<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

This command returns the list of consumers that belong to the groupname consumer group of the stream stored at +key. Read more
source§

fn xinfo_groups<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns the list of all consumers groups of the stream stored at key. Read more
source§

fn xinfo_stream<R, K>( + &self, + key: K, + full: bool, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns information about the stream stored at key. Read more
source§

fn xadd<R, K, C, I, F>( + &self, + key: K, + nomkstream: bool, + cap: C, + id: I, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<XID>, + F: TryInto<MultipleOrderedPairs>, + F::Error: Into<RedisError>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Appends the specified stream entry to the stream at the specified key. If the key does not exist, as a side +effect of running this command the key is created with a stream value. The creation of stream’s key can be +disabled with the NOMKSTREAM option. Read more
source§

fn xtrim<R, K, C>(&self, key: K, cap: C) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Trims the stream by evicting older entries (entries with lower IDs) if needed. Read more
source§

fn xdel<R, K, S>(&self, key: K, ids: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Removes the specified entries from a stream, and returns the number of entries deleted. Read more
source§

fn xrange_values<Ri, Rk, Rv, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Return the stream entries matching the provided range of IDs, automatically converting to a less verbose type +definition. Read more
source§

fn xrange<R, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

The command returns the stream entries matching a given range of IDs. The range is specified by a minimum +and maximum ID. All the entries having an ID between the two specified or exactly one of the two IDs specified +(closed interval) are returned. Read more
source§

fn xrevrange_values<Ri, Rk, Rv, K, E, S>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Similar to XRANGE, but with the results returned in reverse order. The results will be automatically converted +to a less verbose type definition. Read more
source§

fn xrevrange<R, K, S, E>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

Similar to XRANGE, but with the results returned in reverse order. Read more
source§

fn xlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of entries inside a stream. Read more
source§

fn xread_map<Rk1, Rk2, Rk3, Rv, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xread<R, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xgroup_create<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, + mkstream: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

This command creates a new consumer group uniquely identified by groupname for the stream stored at key. Read more
source§

fn xgroup_createconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Create a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_delconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Delete a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_destroy<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Completely destroy a consumer group. Read more
source§

fn xgroup_setid<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

Set the last delivered ID for a consumer group. Read more
source§

fn xreadgroup_map<Rk1, Rk2, Rk3, Rv, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xreadgroup<R, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xack<R, K, G, I>( + &self, + key: K, + group: G, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + I: Into<MultipleIDs>,

Remove one or more messages from the Pending Entries List (PEL) of a stream consumer group. Read more
source§

fn xclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

A variation of xclaim with a less verbose return type.
source§

fn xclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

In the context of a stream consumer group, this command changes the ownership of a pending message, +so that the new owner is the consumer specified as the command argument. Read more
source§

fn xautoclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<(String, Vec<XReadValue<Ri, Rk, Rv>>)>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. It also converts +the response type to a less verbose type declaration and handles potential differences between RESP2 and RESP3. Read more
source§

fn xautoclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. Read more
source§

fn xpending<R, K, G, A>( + &self, + key: K, + group: G, + args: A, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + A: Into<XPendingArgs>,

Inspect the list of pending messages in a consumer group. Read more
source§

impl TimeSeriesInterface for RedisPool

Available on crate feature i-time-series only.
source§

fn ts_add<R, K, T, L>( + &self, + key: K, + timestamp: T, + value: f64, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + on_duplicate: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + T: TryInto<Timestamp>, + T::Error: Into<RedisError>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Append a sample to a time series. Read more
source§

fn ts_alter<R, K, L>( + &self, + key: K, + retention: Option<u64>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Update the retention, chunk size, duplicate policy, and labels of an existing time series. Read more
source§

fn ts_create<R, K, L>( + &self, + key: K, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Create a new time series. Read more
source§

fn ts_createrule<R, S, D>( + &self, + src: S, + dest: D, + aggregation: (Aggregator, u64), + align_timestamp: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Create a compaction rule. Read more
source§

fn ts_decrby<R, K, L>( + &self, + key: K, + subtrahend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given decrement. Read more
source§

fn ts_del<R, K>( + &self, + key: K, + from: i64, + to: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Delete all samples between two timestamps for a given time series. Read more
source§

fn ts_deleterule<R, S, D>( + &self, + src: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Delete a compaction rule. Read more
source§

fn ts_get<R, K>( + &self, + key: K, + latest: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the sample with the highest timestamp from a given time series. Read more
source§

fn ts_incrby<R, K, L>( + &self, + key: K, + addend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given increment. Read more
source§

fn ts_info<R, K>( + &self, + key: K, + debug: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return information and statistics for a time series. Read more
source§

fn ts_madd<R, K, I>(&self, samples: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: IntoIterator<Item = (K, Timestamp, f64)>,

Append new samples to one or more time series. Read more
source§

fn ts_mget<R, L, S, I>( + &self, + latest: bool, + labels: Option<L>, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + L: Into<GetLabels>, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get the sample with the highest timestamp from each time series matching a specific filter. Read more
source§

fn ts_mrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the forward direction. Read more
source§

fn ts_mrevrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the reverse direction. Read more
source§

fn ts_queryindex<R, S, I>( + &self, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get all time series keys matching a filter list. Read more
source§

fn ts_range<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in forward direction. Read more
source§

fn ts_revrange<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in reverse direction. Read more
source§

impl TransactionInterface for RedisPool

Available on crate feature transactions only.
source§

fn multi(&self) -> Transaction

Enter a MULTI block, executing subsequent commands as a transaction. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/clients/struct.Replicas.html b/doc/fred/clients/struct.Replicas.html new file mode 100644 index 00000000..b52a8cee --- /dev/null +++ b/doc/fred/clients/struct.Replicas.html @@ -0,0 +1,2099 @@ +Replicas in fred::clients - Rust

Struct fred::clients::Replicas

source ·
pub struct Replicas { /* private fields */ }
Available on crate feature replicas only.
Expand description

A struct for interacting with cluster replica nodes.

+

All commands sent via this interface will use a replica node, if possible. The underlying connections are shared +with the main client in order to maintain an up-to-date view of the system in the event that replicas change or +are promoted. The cached replica routing table will be updated on the client when following cluster redirections +or when any connection closes.

+

Redis replication is asynchronous.

+

Implementations§

source§

impl Replicas

source

pub fn nodes(&self) -> HashMap<Server, Server>

Read a mapping of replica server IDs to primary server IDs.

+
source

pub fn pipeline(&self) -> Pipeline<Replicas>

Send a series of commands in a pipeline.

+
source

pub fn client(&self) -> RedisClient

Read the underlying RedisClient that interacts with primary nodes.

+
source

pub async fn sync(&self, reset: bool) -> Result<(), RedisError>

Sync the cached replica routing table with the server(s).

+

If reset: true the client will forcefully disconnect from replicas even if the connections could otherwise be +reused.

+

Trait Implementations§

source§

impl ClientLike for Replicas

source§

fn id(&self) -> &str

The unique ID identifying this client and underlying connections.
source§

fn client_config(&self) -> RedisConfig

Read the config used to initialize the client.
source§

fn client_reconnect_policy(&self) -> Option<ReconnectPolicy>

Read the reconnect policy used to initialize the client.
source§

fn connection_config(&self) -> &ConnectionConfig

Read the connection config used to initialize the client.
source§

fn protocol_version(&self) -> RespVersion

Read the RESP version used by the client when communicating with the server.
source§

fn has_reconnect_policy(&self) -> bool

Whether the client has a reconnection policy.
source§

fn is_pipelined(&self) -> bool

Whether the client will automatically pipeline commands.
source§

fn is_clustered(&self) -> bool

Whether the client is connected to a cluster.
source§

fn uses_sentinels(&self) -> bool

Whether the client uses the sentinel interface.
source§

fn update_perf_config(&self, config: PerformanceConfig)

Update the internal PerformanceConfig in place with new values.
source§

fn perf_config(&self) -> PerformanceConfig

Read the PerformanceConfig associated with this client.
source§

fn state(&self) -> ClientState

Read the state of the underlying connection(s). Read more
source§

fn is_connected(&self) -> bool

Whether all underlying connections are healthy.
source§

fn active_connections( + &self, +) -> impl Future<Output = Result<Vec<Server>, RedisError>>

Read the set of active connections managed by the client.
source§

fn server_version(&self) -> Option<Version>

Read the server version, if known.
source§

fn set_resolver(&self, resolver: Rc<dyn Resolve>) -> impl Future

Available on crate feature dns only.
Override the DNS resolution logic for the client.
source§

fn connect(&self) -> ConnectHandle

Connect to the server. Read more
source§

fn force_reconnection(&self) -> impl Future<Output = RedisResult<()>>

Force a reconnection to the server(s). Read more
source§

fn wait_for_connect(&self) -> impl Future<Output = RedisResult<()>>

Wait for the result of the next connection attempt. Read more
source§

fn init(&self) -> impl Future<Output = RedisResult<ConnectHandle>>

Initialize a new routing and connection task and wait for it to connect successfully. Read more
source§

fn quit(&self) -> impl Future<Output = RedisResult<()>>

Close the connection to the Redis server. The returned future resolves when the command has been written to the +socket, not when the connection has been fully closed. Some time after this future resolves the future +returned by connect will resolve which indicates that the connection has been fully closed. Read more
source§

fn shutdown( + &self, + flags: Option<ShutdownFlags>, +) -> impl Future<Output = RedisResult<()>>

Available on crate feature i-server only.
Shut down the server and quit the client. Read more
source§

fn flushall<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Delete the keys in all databases. Read more
source§

fn flushall_cluster(&self) -> impl Future<Output = RedisResult<()>>

Delete the keys on all nodes in the cluster. This is a special function that does not map directly to the Redis +interface.
source§

fn ping<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Ping the Redis server. Read more
source§

fn info<R>( + &self, + section: Option<InfoKind>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read info about the server. Read more
source§

fn custom<R, T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command that is not yet supported via another interface on this client. This is most useful when +interacting with third party modules or extensions. Read more
source§

fn custom_raw<T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<Resp3Frame>>
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command similar to custom, but return the response frame directly without any +parsing. Read more
source§

fn with_options(&self, options: &Options) -> WithOptions<Self>

Customize various configuration options on commands.
source§

impl Clone for Replicas

source§

fn clone(&self) -> Replicas

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl ClusterInterface for Replicas

Available on crate feature i-cluster only.
source§

fn cached_cluster_state(&self) -> Option<ClusterRouting>

Read the cached cluster state used for routing commands to the correct cluster nodes.
source§

fn num_primary_cluster_nodes(&self) -> usize

Read the number of known primary cluster nodes, or 0 if the cluster state is not known.
source§

fn sync_cluster(&self) -> impl Future<Output = Result<(), RedisError>>

Update the cached cluster state and add or remove any changed cluster node connections.
source§

fn cluster_bumpepoch<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Advances the cluster config epoch. Read more
source§

fn cluster_flushslots(&self) -> impl Future<Output = RedisResult<()>>

Deletes all slots from a node. Read more
source§

fn cluster_myid<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the node’s id. Read more
source§

fn cluster_nodes<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read the current cluster node configuration. Read more
source§

fn cluster_saveconfig(&self) -> impl Future<Output = RedisResult<()>>

Forces a node to save the nodes.conf configuration on disk. Read more
source§

fn cluster_slots<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER SLOTS returns details about which cluster slots map to which Redis instances. Read more
source§

fn cluster_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER INFO provides INFO style information about Redis Cluster vital parameters. Read more
source§

fn cluster_add_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

This command is useful in order to modify a node’s view of the cluster configuration. Specifically it assigns a +set of hash slots to the node receiving the command. Read more
source§

fn cluster_count_failure_reports<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns the number of failure reports for the specified node. Read more
source§

fn cluster_count_keys_in_slot<R>( + &self, + slot: u16, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of keys in the specified Redis Cluster hash slot. Read more
source§

fn cluster_del_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

The CLUSTER DELSLOTS command asks a particular Redis Cluster node to forget which master is serving the hash +slots specified as arguments. Read more
source§

fn cluster_failover( + &self, + flag: Option<ClusterFailoverFlag>, +) -> impl Future<Output = RedisResult<()>>

This command, that can only be sent to a Redis Cluster replica node, forces the replica to start a manual +failover of its master instance. Read more
source§

fn cluster_forget<S>(&self, node_id: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command is used in order to remove a node, specified via its node ID, from the set of known nodes of the +Redis Cluster node receiving the command. In other words the specified node is removed from the nodes table of +the node receiving the command. Read more
source§

fn cluster_get_keys_in_slot<R>( + &self, + slot: u16, + count: u64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns an array of keys names stored in the contacted node and hashing to the specified hash slot. Read more
source§

fn cluster_keyslot<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns an integer identifying the hash slot the specified key hashes to. Read more
source§

fn cluster_meet<S>( + &self, + ip: S, + port: u16, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

CLUSTER MEET is used in order to connect different Redis nodes with cluster support enabled, into a working +cluster. Read more
source§

fn cluster_replicate<S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command reconfigures a node as a replica of the specified master. If the node receiving the command is an +empty master, as a side effect of the command, the node role is changed from master to replica. Read more
source§

fn cluster_replicas<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command provides a list of replica nodes replicating from the specified master node. Read more
source§

fn cluster_reset( + &self, + mode: Option<ClusterResetFlag>, +) -> impl Future<Output = RedisResult<()>>

Reset a Redis Cluster node, in a more or less drastic way depending on the reset type, that can be hard or soft. +Note that this command does not work for masters if they hold one or more keys, in that case to completely +reset a master node keys must be removed first, e.g. by using FLUSHALL first, and then CLUSTER RESET. Read more
source§

fn cluster_set_config_epoch( + &self, + epoch: u64, +) -> impl Future<Output = RedisResult<()>>

This command sets a specific config epoch in a fresh node. Read more
source§

fn cluster_setslot( + &self, + slot: u16, + state: ClusterSetSlotState, +) -> impl Future<Output = RedisResult<()>>

CLUSTER SETSLOT is responsible for changing the state of a hash slot in the receiving node in different ways. Read more
source§

impl ConfigInterface for Replicas

Available on crate feature i-config only.
source§

fn config_resetstat(&self) -> impl Future<Output = RedisResult<()>>

Resets the statistics reported by Redis using the INFO command. Read more
source§

fn config_rewrite(&self) -> impl Future<Output = RedisResult<()>>

The CONFIG REWRITE command rewrites the redis.conf file the server was started with, applying the minimal +changes needed to make it reflect the configuration currently used by the server, which may be different +compared to the original one because of the use of the CONFIG SET command. Read more
source§

fn config_get<R, S>(&self, parameter: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The CONFIG GET command is used to read the configuration parameters of a running Redis server. Read more
source§

fn config_set<P, V>( + &self, + parameter: P, + value: V, +) -> impl Future<Output = RedisResult<()>>
where + P: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The CONFIG SET command is used in order to reconfigure the server at run time without the need to restart Redis. Read more
source§

impl Debug for Replicas

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl FunctionInterface for Replicas

Available on crate feature i-scripts only.
source§

fn fcall<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Invoke a function. Read more
source§

fn fcall_ro<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

This is a read-only variant of the FCALL command that cannot execute commands that modify data. Read more
source§

fn function_delete<R, S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Delete a library and all its functions. Read more
source§

fn function_delete_cluster<S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Delete a library and all its functions from each cluster node concurrently. Read more
source§

fn function_dump<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the serialized payload of loaded libraries. Read more
source§

fn function_flush<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Deletes all the libraries. Read more
source§

fn function_flush_cluster( + &self, + async: bool, +) -> impl Future<Output = RedisResult<()>>

Deletes all the libraries on all cluster nodes concurrently. Read more
source§

fn function_kill<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Kill a function that is currently executing. Read more
source§

fn function_list<R, S>( + &self, + library_name: Option<S>, + withcode: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Return information about the functions and libraries. Read more
source§

fn function_load<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis. Read more
source§

fn function_load_cluster<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis on all cluster nodes concurrently. Read more
source§

fn function_restore<R, B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload. Read more
source§

fn function_restore_cluster<B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<()>>
where + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload on all cluster nodes concurrently. Read more
source§

fn function_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return information about the function that’s currently running and information about the available execution +engines. Read more
source§

impl GeoInterface for Replicas

Available on crate feature i-geo only.
source§

fn geoadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + changed: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: Into<MultipleGeoValues>,

Adds the specified geospatial items (longitude, latitude, name) to the specified key. Read more
source§

fn geohash<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return valid Geohash strings representing the position of one or more elements in a sorted set value +representing a geospatial index (where elements were added using GEOADD). Read more
source§

fn geopos<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by +the sorted set at key. Read more
source§

fn geodist<R, K, S, D>( + &self, + key: K, + src: S, + dest: D, + unit: Option<GeoUnit>, +) -> impl Future<Output = RedisResult<R>>

Return the distance between two members in the geospatial index represented by the sorted set. Read more
source§

fn georadius<R, K, P>( + &self, + key: K, + position: P, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<GeoPosition>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified with the center location and the maximum distance from the center (the radius). Read more
source§

fn georadiusbymember<R, K, V>( + &self, + key: K, + member: V, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

This command is exactly like GEORADIUS with the sole difference that instead of taking, as the center of the +area to query, a longitude and latitude value, it takes the name of a member already existing inside the +geospatial index represented by the sorted set. Read more
source§

fn geosearch<R, K>( + &self, + key: K, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + withcoord: bool, + withdist: bool, + withhash: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified by a given shape. Read more
source§

fn geosearchstore<R, D, S>( + &self, + dest: D, + source: S, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + storedist: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>,

This command is like GEOSEARCH, but stores the result in destination key. Returns the number of members added to +the destination key. Read more
source§

impl HashesInterface for Replicas

Available on crate feature i-hashes only.
source§

fn hgetall<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all fields and values of the hash stored at key. Read more
source§

fn hdel<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Removes the specified fields from the hash stored at key. Read more
source§

fn hexists<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns if field is an existing field in the hash stored at key. Read more
source§

fn hget<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the value associated with field in the hash stored at key. Read more
source§

fn hincrby<R, K, F>( + &self, + key: K, + field: F, + increment: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increments the number stored at field in the hash stored at key by increment. Read more
source§

fn hincrbyfloat<R, K, F>( + &self, + key: K, + field: F, + increment: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increment the specified field of a hash stored at key, and representing a floating point number, by the +specified increment. Read more
source§

fn hkeys<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all field names in the hash stored at key. Read more
source§

fn hlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of fields contained in the hash stored at key. Read more
source§

fn hmget<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Returns the values associated with the specified fields in the hash stored at key. Read more
source§

fn hmset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the specified fields to their respective values in the hash stored at key. Read more
source§

fn hset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets fields in the hash stored at key to their provided values. Read more
source§

fn hsetnx<R, K, F, V>( + &self, + key: K, + field: F, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets field in the hash stored at key to value, only if field does not yet exist. Read more
source§

fn hrandfield<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random field from the hash value stored at key. Read more
source§

fn hstrlen<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the string length of the value associated with field in the hash stored at key. Read more
source§

fn hvals<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all values in the hash stored at key. Read more
source§

impl HyperloglogInterface for Replicas

Available on crate feature i-hyperloglog only.
source§

fn pfadd<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Adds all the element arguments to the HyperLogLog data structure stored at the variable name specified as first +argument. Read more
source§

fn pfcount<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

When called with a single key, returns the approximated cardinality computed by the HyperLogLog data structure +stored at the specified variable, which is 0 if the variable does not exist. Read more
source§

fn pfmerge<R, D, S>( + &self, + dest: D, + sources: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<MultipleKeys>,

Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of the +observed sets of the source HyperLogLog structures. Read more
source§

impl KeysInterface for Replicas

Available on crate feature i-keys only.
source§

fn watch<K>(&self, keys: K) -> impl Future<Output = RedisResult<()>>
where + K: Into<MultipleKeys>,

Marks the given keys to be watched for conditional execution of a transaction. Read more
source§

fn unwatch(&self) -> impl Future<Output = RedisResult<()>>

Flushes all the previously watched keys for a transaction. Read more
source§

fn randomkey<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return a random key from the currently selected database. Read more
source§

fn copy<R, S, D>( + &self, + source: S, + destination: D, + db: Option<u8>, + replace: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

This command copies the value stored at the source key to the destination key. Read more
source§

fn dump<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Serialize the value stored at key in a Redis-specific format and return it as bulk string. Read more
source§

fn restore<R, K>( + &self, + key: K, + ttl: i64, + serialized: RedisValue, + replace: bool, + absttl: bool, + idletime: Option<i64>, + frequency: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Create a key associated with a value that is obtained by deserializing the provided serialized value Read more
source§

fn set<R, K, V>( + &self, + key: K, + value: V, + expire: Option<Expiration>, + options: Option<SetOptions>, + get: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set a value with optional NX|XX, EX|PX|EXAT|PXAT|KEEPTTL, and GET arguments. Read more
source§

fn get<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Read a value from the server. Read more
source§

fn getrange<R, K>( + &self, + key: K, + start: usize, + end: usize, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the substring of the string value stored at key with offsets start and end (both inclusive). Read more
source§

fn setrange<R, K, V>( + &self, + key: K, + offset: u32, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Overwrites part of the string stored at key, starting at the specified offset, for the entire length of +value. Read more
source§

fn getset<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Atomically sets key to value and returns the old value stored at key. Read more
source§

fn getdel<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the value of key and delete the key. This command is similar to GET, except for the fact that it also +deletes the key on success (if and only if the key’s value type is a string). Read more
source§

fn strlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the string value stored at key. An error is returned when key holds a non-string value. Read more
source§

fn del<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Removes the specified keys. A key is ignored if it does not exist. Read more
Unlinks the specified keys. A key is ignored if it does not exist Read more
source§

fn rename<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination. Read more
source§

fn renamenx<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination if destination does not yet exist. Read more
source§

fn append<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Append value to key if it’s a string. Read more
source§

fn mget<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the +special value nil is returned. Read more
source§

fn mset<V>(&self, values: V) -> impl Future<Output = RedisResult<()>>
where + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. Read more
source§

fn msetnx<R, V>(&self, values: V) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. MSETNX will not perform any operation at all even if just a +single key already exists. Read more
source§

fn incr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by_float<R, K>( + &self, + key: K, + val: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increment the string representing a floating point number stored at key by val. If the key does not exist, it +is set to 0 before performing the operation. Read more
source§

fn decr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn decr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn ttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in seconds. Read more
source§

fn pttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in milliseconds. Read more
source§

fn persist<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Remove the existing timeout on a key, turning the key from volatile (a key with an expiration) +to persistent (a key that will never expire as no timeout is associated). Read more
source§

fn expire<R, K>( + &self, + key: K, + seconds: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on key. After the timeout has expired, the key will be automatically deleted. Read more
source§

fn expire_at<R, K>( + &self, + key: K, + timestamp: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on a key based on a UNIX timestamp. Read more
source§

fn pexpire<R, K>( + &self, + key: K, + milliseconds: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command works exactly like EXPIRE but the time to live of the key is specified in milliseconds instead of +seconds. Read more
source§

fn pexpire_at<R, K>( + &self, + key: K, + timestamp: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

PEXPIREAT has the same effect and semantic as EXPIREAT, but the Unix time at which the key will expire is +specified in milliseconds instead of seconds. Read more
source§

fn exists<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns number of keys that exist from the keys arguments. Read more
source§

fn lcs<R, K1, K2>( + &self, + key1: K1, + key2: K2, + len: bool, + idx: bool, + minmatchlen: Option<i64>, + withmatchlen: bool, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis, + K1: Into<RedisKey>, + K2: Into<RedisKey>,

Runs the longest common subsequence algorithm on two keys. Read more
source§

impl ListInterface for Replicas

Available on crate feature i-lists only.
source§

fn blmpop<R, K>( + &self, + timeout: f64, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::lmpop. Read more
source§

fn blpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BLPOP is a blocking list pop primitive. It is the blocking version of LPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the head of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BRPOP is a blocking list pop primitive. It is the blocking version of RPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the tail of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpoplpush<R, S, D>( + &self, + source: S, + destination: D, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::rpoplpush. Read more
source§

fn blmove<R, S, D>( + &self, + source: S, + destination: D, + source_direction: LMoveDirection, + destination_direction: LMoveDirection, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::lmove. Read more
source§

fn lmpop<R, K>( + &self, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements from the first non-empty list key from the list of provided key names. Read more
source§

fn lindex<R, K>( + &self, + key: K, + index: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the element at index in the list stored at key. Read more
source§

fn linsert<R, K, P, V>( + &self, + key: K, + location: ListLocation, + pivot: P, + element: V, +) -> impl Future<Output = RedisResult<R>>

Inserts element in the list stored at key either before or after the reference value pivot. Read more
source§

fn llen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the list stored at key. Read more
source§

fn lpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the first elements of the list stored at key. Read more
source§

fn lpos<R, K, V>( + &self, + key: K, + element: V, + rank: Option<i64>, + count: Option<i64>, + maxlen: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The command returns the index of matching elements inside a Redis list. Read more
source§

fn lpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the head of the list stored at key. Read more
source§

fn lpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the head of the list stored at key, only if key already exists and holds a list. Read more
source§

fn lrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified elements of the list stored at key. Read more
source§

fn lrem<R, K, V>( + &self, + key: K, + count: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Removes the first count occurrences of elements equal to element from the list stored at key. Read more
source§

fn lset<R, K, V>( + &self, + key: K, + index: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets the list element at index to element. Read more
source§

fn ltrim<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Trim an existing list so that it will contain only the specified range of elements specified. Read more
source§

fn rpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the last elements of the list stored at key. Read more
source§

fn rpoplpush<R, S, D>( + &self, + source: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the last element (tail) of the list stored at source, and pushes the element at +the first element (head) of the list stored at destination. Read more
source§

fn lmove<R, S, D>( + &self, + source: S, + dest: D, + source_direction: LMoveDirection, + dest_direction: LMoveDirection, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the first/last element (head/tail depending on the source direction argument) of +the list stored at source, and pushes the element at the first/last element (head/tail depending on the +destination direction argument) of the list stored at destination. Read more
source§

fn rpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the tail of the list stored at key. Read more
source§

fn rpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the tail of the list stored at key, only if key already exists and holds a list. Read more
source§

fn sort<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, + store: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Returns or stores the elements contained in the list, set or sorted set at key. Read more
source§

fn sort_ro<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Read-only variant of the SORT command. It is exactly like the original SORT but refuses the STORE option and can +safely be used in read-only replicas. Read more
source§

impl LuaInterface for Replicas

Available on crate feature i-scripts only.
source§

fn script_load<R, S>(&self, script: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a script into the scripts cache, without executing it. After the specified command is loaded into the +script cache it will be callable using EVALSHA with the correct SHA1 digest of the script. Read more
source§

fn script_load_cluster<R, S>( + &self, + script: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Available on crate feature sha-1 only.
A clustered variant of script_load that loads the script on all primary nodes in a cluster. Read more
source§

fn script_kill(&self) -> impl Future<Output = RedisResult<()>>

Kills the currently executing Lua script, assuming no write operation was yet performed by the script. Read more
source§

fn script_kill_cluster(&self) -> impl Future<Output = RedisResult<()>>

A clustered variant of the script_kill command that issues the command to all primary nodes +in the cluster.
source§

fn script_flush(&self, async: bool) -> impl Future<Output = RedisResult<()>>

Flush the Lua scripts cache. Read more
source§

fn script_flush_cluster( + &self, + async: bool, +) -> impl Future<Output = RedisResult<()>>

A clustered variant of script_flush that flushes the script cache on all primary nodes in +the cluster.
source§

fn script_exists<R, H>(&self, hashes: H) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + H: Into<MultipleStrings>,

Returns information about the existence of the scripts in the script cache. Read more
source§

fn script_debug( + &self, + flag: ScriptDebugFlag, +) -> impl Future<Output = RedisResult<()>>

Set the debug mode for subsequent scripts executed with EVAL. Read more
source§

fn evalsha<R, S, K, V>( + &self, + hash: S, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Evaluates a script cached on the server side by its SHA1 digest. Read more
source§

fn eval<R, S, K, V>( + &self, + script: S, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Evaluate a Lua script on the server. Read more
source§

impl MemoryInterface for Replicas

Available on crate feature i-memory only.
source§

fn memory_doctor<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY DOCTOR command reports about different memory-related issues that the Redis server experiences, and +advises about possible remedies. Read more
source§

fn memory_malloc_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY MALLOC-STATS command provides an internal statistics report from the memory allocator. Read more
source§

fn memory_purge(&self) -> impl Future<Output = RedisResult<()>>

The MEMORY PURGE command attempts to purge dirty pages so these can be reclaimed by the allocator. Read more
source§

fn memory_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY STATS command returns an Array reply about the memory usage of the server. Read more
source§

fn memory_usage<R, K>( + &self, + key: K, + samples: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

The MEMORY USAGE command reports the number of bytes that a key and its value require to be stored in RAM. Read more
source§

impl RediSearchInterface for Replicas

Available on crate feature i-redisearch only.
source§

fn ft_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns a list of all existing indexes. Read more
source§

fn ft_aggregate<R, I, Q>( + &self, + index: I, + query: Q, + options: FtAggregateOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Run a search query on an index, and perform aggregate transformations on the results. Read more
Search the index with a textual query, returning either documents or just ids. Read more
source§

fn ft_create<R, I>( + &self, + index: I, + options: FtCreateOptions, + schema: Vec<SearchSchema>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Create an index with the given specification. Read more
source§

fn ft_alter<R, I>( + &self, + index: I, + options: FtAlterOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Add a new attribute to the index. Read more
source§

fn ft_aliasadd<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. Read more
source§

fn ft_aliasdel<R, A>(&self, alias: A) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>,

Remove an alias from an index. Read more
source§

fn ft_aliasupdate<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. If the alias is already associated with another index, FT.ALIASUPDATE removes the +alias association with the previous index. Read more
source§

fn ft_config_get<R, S>(&self, option: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Retrieve configuration options. Read more
source§

fn ft_config_set<R, S, V>( + &self, + option: S, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set the value of a RediSearch configuration parameter. Read more
source§

fn ft_cursor_del<R, I, C>( + &self, + index: I, + cursor: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Delete a cursor. Read more
source§

fn ft_cursor_read<R, I, C>( + &self, + index: I, + cursor: C, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Read next results from an existing cursor. Read more
source§

fn ft_dictadd<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Add terms to a dictionary. Read more
source§

fn ft_dictdel<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Remove terms from a dictionary. Read more
source§

fn ft_dictdump<R, D>(&self, dict: D) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>,

Dump all terms in the given dictionary. Read more
source§

fn ft_dropindex<R, I>( + &self, + index: I, + dd: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Delete an index. Read more
source§

fn ft_explain<R, I, Q>( + &self, + index: I, + query: Q, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Return the execution plan for a complex query. Read more
source§

fn ft_info<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Return information and statistics on the index. Read more
source§

fn ft_spellcheck<R, I, Q>( + &self, + index: I, + query: Q, + distance: Option<u8>, + terms: Option<SpellcheckTerms>, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Perform spelling correction on a query, returning suggestions for misspelled terms. Read more
source§

fn ft_sugadd<R, K, S>( + &self, + key: K, + string: S, + score: f64, + incr: bool, + payload: Option<Bytes>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Add a suggestion string to an auto-complete suggestion dictionary. Read more
source§

fn ft_sugdel<R, K, S>( + &self, + key: K, + string: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Delete a string from a suggestion index. Read more
source§

fn ft_sugget<R, K, P>( + &self, + key: K, + prefix: P, + fuzzy: bool, + withscores: bool, + withpayloads: bool, + max: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Get completion suggestions for a prefix. Read more
source§

fn ft_suglen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the size of an auto-complete suggestion dictionary. Read more
source§

fn ft_syndump<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Dump the contents of a synonym group. Read more
source§

fn ft_synupdate<R, I, S, T>( + &self, + index: I, + synonym_group_id: S, + skipinitialscan: bool, + terms: T, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + S: Into<Str>, + T: Into<MultipleStrings>,

Update a synonym group. Read more
source§

fn ft_tagvals<R, I, F>( + &self, + index: I, + field_name: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + F: Into<Str>,

Return a distinct set of values indexed in a Tag field. Read more
source§

impl RedisJsonInterface for Replicas

Available on crate feature i-redis-json only.
source§

fn json_arrappend<R, K, P, V>( + &self, + key: K, + path: P, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json values into the array at path after the last element in it. Read more
source§

fn json_arrindex<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + start: Option<i64>, + stop: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Search for the first occurrence of a JSON value in an array. Read more
source§

fn json_arrinsert<R, K, P, V>( + &self, + key: K, + path: P, + index: i64, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Insert the json values into the array at path before the index (shifts to the right). Read more
source§

fn json_arrlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON array at path in key. Read more
source§

fn json_arrpop<R, K, P>( + &self, + key: K, + path: Option<P>, + index: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Remove and return an element from the index in the array Read more
source§

fn json_arrtrim<R, K, P>( + &self, + key: K, + path: P, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Trim an array so that it contains only the specified inclusive range of elements Read more
source§

fn json_clear<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Clear container values (arrays/objects) and set numeric values to 0 Read more
source§

fn json_debug_memory<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report a value’s memory usage in bytes Read more
source§

fn json_del<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Delete a value. Read more
source§

fn json_get<R, K, I, N, S, P>( + &self, + key: K, + indent: Option<I>, + newline: Option<N>, + space: Option<S>, + paths: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<Str>, + N: Into<Str>, + S: Into<Str>, + P: Into<MultipleStrings>,

Return the value at path in JSON serialized form. Read more
source§

fn json_merge<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Merge a given JSON value into matching paths. Read more
source§

fn json_mget<R, K, P>( + &self, + keys: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + P: Into<Str>,

Return the values at path from multiple key arguments. Read more
source§

fn json_mset<R, K, P, V>( + &self, + values: Vec<(K, P, V)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set or update one or more JSON values according to the specified key-path-value triplets. Read more
source§

fn json_numincrby<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Increment the number value stored at path by number Read more
source§

fn json_objkeys<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the keys in the object that’s referenced by path. Read more
source§

fn json_objlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the number of keys in the JSON object at path in key. Read more
source§

fn json_resp<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the JSON in key in Redis serialization protocol specification form. Read more
source§

fn json_set<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + options: Option<SetOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set the JSON value at path in key. Read more
source§

fn json_strappend<R, K, P, V>( + &self, + key: K, + path: Option<P>, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json-string values to the string at path. Read more
source§

fn json_strlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON String at path in key. Read more
source§

fn json_toggle<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Toggle a Boolean value stored at path. Read more
source§

fn json_type<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the type of JSON value at path. Read more
source§

impl ServerInterface for Replicas

Available on crate feature i-server only.
source§

fn bgrewriteaof<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Instruct Redis to start an Append Only File rewrite process. Read more
source§

fn bgsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Save the DB in background. Read more
source§

fn dbsize<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the number of keys in the selected database. Read more
source§

fn select(&self, db: u8) -> impl Future<Output = RedisResult<()>>

Select the database this client should use. Read more
source§

fn failover( + &self, + to: Option<(String, u16)>, + force: bool, + abort: bool, + timeout: Option<u32>, +) -> impl Future<Output = RedisResult<()>>

This command will start a coordinated failover between the currently-connected-to master and one of its +replicas. Read more
source§

fn lastsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the UNIX TIME of the last DB save executed with success. Read more
source§

fn wait<R>( + &self, + numreplicas: i64, + timeout: i64, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis,

This command blocks the current client until all the previous write commands are successfully transferred and +acknowledged by at least the specified number of replicas. If the timeout, specified in milliseconds, is +reached, the command returns even if the specified number of replicas were not yet reached. Read more
source§

fn sentinel_primary(&self) -> Option<Server>

Read the primary Redis server identifier returned from the sentinel nodes.
source§

fn sentinel_nodes(&self) -> Option<Vec<Server>>

Read the set of known sentinel nodes.
source§

impl SetsInterface for Replicas

Available on crate feature i-sets only.
source§

fn sadd<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Add the specified members to the set stored at key. Read more
source§

fn scard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the set cardinality (number of elements) of the set stored at key. Read more
source§

fn sdiff<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the difference between the first set and all the successive sets. Read more
source§

fn sdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SDIFF, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sinter<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the intersection of all the given sets. Read more
source§

fn sinterstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SINTER, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sismember<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns if member is a member of the set stored at key. Read more
source§

fn smismember<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns whether each member is a member of the set stored at key. Read more
source§

fn smembers<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all the members of the set value stored at key. Read more
source§

fn smove<R, S, D, V>( + &self, + source: S, + dest: D, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Move member from the set at source to the set at destination. Read more
source§

fn spop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns one or more random members from the set value store at key. Read more
source§

fn srandmember<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the set value stored at key. Read more
source§

fn srem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Remove the specified members from the set stored at key. Read more
source§

fn sunion<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the union of all the given sets. Read more
source§

fn sunionstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SUNION, but instead of returning the resulting set, it is stored in destination. Read more
source§

impl SlowlogInterface for Replicas

Available on crate feature i-slowlog only.
source§

fn slowlog_get<R>( + &self, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read the slow queries log. Read more
source§

fn slowlog_length<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read length of the slow queries log. Read more
source§

fn slowlog_reset(&self) -> impl Future<Output = RedisResult<()>>

This command is used to reset the slow queries log. Read more
source§

impl SortedSetsInterface for Replicas

Available on crate feature i-sorted-sets only.
source§

fn bzmpop<R, K>( + &self, + timeout: f64, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zmpop. Read more
source§

fn bzpopmin<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmin. Read more
source§

fn bzpopmax<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmax. Read more
source§

fn zadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + ordering: Option<Ordering>, + changed: bool, + incr: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>

Adds all the specified members with the specified scores to the sorted set stored at key. Read more
source§

fn zcard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the sorted set cardinality (number of elements) of the sorted set stored at key. Read more
source§

fn zcount<R, K>( + &self, + key: K, + min: f64, + max: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of elements in the sorted set at key with a score between min and max. Read more
source§

fn zdiff<R, K>( + &self, + keys: K, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

This command is similar to ZDIFFSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

Computes the difference between the first and all successive input sorted sets and stores the result in +destination. Read more
source§

fn zincrby<R, K, V>( + &self, + key: K, + increment: f64, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Increments the score of member in the sorted set stored at key by increment. Read more
source§

fn zinter<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZINTERSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zinterstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the intersection of the sorted sets given by the specified keys, and stores the result in +destination. Read more
source§

fn zlexcount<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns the number of elements in the sorted set at key with a value between min and +max. Read more
source§

fn zpopmax<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the highest scores in the sorted set stored at key. Read more
source§

fn zpopmin<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the lowest scores in the sorted set stored at key. Read more
source§

fn zmpop<R, K>( + &self, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements, that are member-score pairs, from the first non-empty sorted set in the provided list +of key names. Read more
source§

fn zrandmember<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the sorted set value stored at key. Read more
source§

fn zrangestore<R, D, S, M, N>( + &self, + dest: D, + source: S, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

This command is like ZRANGE, but stores the result in the destination key. Read more
source§

fn zrange<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between min and max. Read more
source§

fn zrevrangebylex<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between max and min. Read more
source§

fn zrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between min and max (including elements +with score equal to min or max). Read more
source§

fn zrevrangebyscore<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between max and min (including +elements with score equal to max or min). Read more
source§

fn zrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from low to high. Read more
source§

fn zrem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Removes the specified members from the sorted set stored at key. Non existing members are ignored. Read more
source§

fn zremrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command removes all elements in the sorted set stored at key between the lexicographical range +specified by min and max. Read more
source§

fn zremrangebyrank<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes all elements in the sorted set stored at key with rank between start and stop. Read more
source§

fn zremrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Removes all elements in the sorted set stored at key with a score between min and max. Read more
source§

fn zrevrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrevrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from high to low. Read more
source§

fn zscore<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the score of member in the sorted set at key. Read more
source§

fn zunion<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZUNIONSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zunionstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the union of the sorted sets given by the specified keys, and stores the result in destination. Read more
source§

fn zmscore<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns the scores associated with the specified members in the sorted set stored at key. Read more
source§

impl StreamsInterface for Replicas

Available on crate feature i-streams only.
source§

fn xinfo_consumers<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

This command returns the list of consumers that belong to the groupname consumer group of the stream stored at +key. Read more
source§

fn xinfo_groups<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns the list of all consumers groups of the stream stored at key. Read more
source§

fn xinfo_stream<R, K>( + &self, + key: K, + full: bool, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns information about the stream stored at key. Read more
source§

fn xadd<R, K, C, I, F>( + &self, + key: K, + nomkstream: bool, + cap: C, + id: I, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<XID>, + F: TryInto<MultipleOrderedPairs>, + F::Error: Into<RedisError>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Appends the specified stream entry to the stream at the specified key. If the key does not exist, as a side +effect of running this command the key is created with a stream value. The creation of stream’s key can be +disabled with the NOMKSTREAM option. Read more
source§

fn xtrim<R, K, C>(&self, key: K, cap: C) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Trims the stream by evicting older entries (entries with lower IDs) if needed. Read more
source§

fn xdel<R, K, S>(&self, key: K, ids: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Removes the specified entries from a stream, and returns the number of entries deleted. Read more
source§

fn xrange_values<Ri, Rk, Rv, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Return the stream entries matching the provided range of IDs, automatically converting to a less verbose type +definition. Read more
source§

fn xrange<R, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

The command returns the stream entries matching a given range of IDs. The range is specified by a minimum +and maximum ID. All the entries having an ID between the two specified or exactly one of the two IDs specified +(closed interval) are returned. Read more
source§

fn xrevrange_values<Ri, Rk, Rv, K, E, S>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Similar to XRANGE, but with the results returned in reverse order. The results will be automatically converted +to a less verbose type definition. Read more
source§

fn xrevrange<R, K, S, E>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

Similar to XRANGE, but with the results returned in reverse order. Read more
source§

fn xlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of entries inside a stream. Read more
source§

fn xread_map<Rk1, Rk2, Rk3, Rv, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xread<R, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xgroup_create<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, + mkstream: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

This command creates a new consumer group uniquely identified by groupname for the stream stored at key. Read more
source§

fn xgroup_createconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Create a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_delconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Delete a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_destroy<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Completely destroy a consumer group. Read more
source§

fn xgroup_setid<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

Set the last delivered ID for a consumer group. Read more
source§

fn xreadgroup_map<Rk1, Rk2, Rk3, Rv, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xreadgroup<R, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xack<R, K, G, I>( + &self, + key: K, + group: G, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + I: Into<MultipleIDs>,

Remove one or more messages from the Pending Entries List (PEL) of a stream consumer group. Read more
source§

fn xclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

A variation of xclaim with a less verbose return type.
source§

fn xclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

In the context of a stream consumer group, this command changes the ownership of a pending message, +so that the new owner is the consumer specified as the command argument. Read more
source§

fn xautoclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<(String, Vec<XReadValue<Ri, Rk, Rv>>)>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. It also converts +the response type to a less verbose type declaration and handles potential differences between RESP2 and RESP3. Read more
source§

fn xautoclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. Read more
source§

fn xpending<R, K, G, A>( + &self, + key: K, + group: G, + args: A, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + A: Into<XPendingArgs>,

Inspect the list of pending messages in a consumer group. Read more
source§

impl TimeSeriesInterface for Replicas

Available on crate feature i-time-series only.
source§

fn ts_add<R, K, T, L>( + &self, + key: K, + timestamp: T, + value: f64, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + on_duplicate: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + T: TryInto<Timestamp>, + T::Error: Into<RedisError>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Append a sample to a time series. Read more
source§

fn ts_alter<R, K, L>( + &self, + key: K, + retention: Option<u64>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Update the retention, chunk size, duplicate policy, and labels of an existing time series. Read more
source§

fn ts_create<R, K, L>( + &self, + key: K, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Create a new time series. Read more
source§

fn ts_createrule<R, S, D>( + &self, + src: S, + dest: D, + aggregation: (Aggregator, u64), + align_timestamp: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Create a compaction rule. Read more
source§

fn ts_decrby<R, K, L>( + &self, + key: K, + subtrahend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given decrement. Read more
source§

fn ts_del<R, K>( + &self, + key: K, + from: i64, + to: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Delete all samples between two timestamps for a given time series. Read more
source§

fn ts_deleterule<R, S, D>( + &self, + src: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Delete a compaction rule. Read more
source§

fn ts_get<R, K>( + &self, + key: K, + latest: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the sample with the highest timestamp from a given time series. Read more
source§

fn ts_incrby<R, K, L>( + &self, + key: K, + addend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given increment. Read more
source§

fn ts_info<R, K>( + &self, + key: K, + debug: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return information and statistics for a time series. Read more
source§

fn ts_madd<R, K, I>(&self, samples: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: IntoIterator<Item = (K, Timestamp, f64)>,

Append new samples to one or more time series. Read more
source§

fn ts_mget<R, L, S, I>( + &self, + latest: bool, + labels: Option<L>, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + L: Into<GetLabels>, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get the sample with the highest timestamp from each time series matching a specific filter. Read more
source§

fn ts_mrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the forward direction. Read more
source§

fn ts_mrevrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the reverse direction. Read more
source§

fn ts_queryindex<R, S, I>( + &self, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get all time series keys matching a filter list. Read more
source§

fn ts_range<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in forward direction. Read more
source§

fn ts_revrange<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in reverse direction. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/clients/struct.SentinelClient.html b/doc/fred/clients/struct.SentinelClient.html new file mode 100644 index 00000000..16e1897f --- /dev/null +++ b/doc/fred/clients/struct.SentinelClient.html @@ -0,0 +1,286 @@ +SentinelClient in fred::clients - Rust

Struct fred::clients::SentinelClient

source ·
pub struct SentinelClient { /* private fields */ }
Available on crate feature sentinel-client only.
Expand description

A struct for interacting directly with Sentinel nodes.

+

This struct will not communicate with Redis servers behind the sentinel interface, but rather with the +sentinel nodes themselves. Callers should use the RedisClient interface with a +ServerConfig::Sentinel for interacting with Redis services behind a +sentinel layer.

+

See the sentinel API docs for more information.

+

Implementations§

source§

impl SentinelClient

source

pub fn new( + config: SentinelConfig, + perf: Option<PerformanceConfig>, + connection: Option<ConnectionConfig>, + policy: Option<ReconnectPolicy>, +) -> SentinelClient

Create a new client instance without connecting to the sentinel node.

+

See the builder interface for more information.

+

Trait Implementations§

source§

impl AclInterface for SentinelClient

Available on crate feature i-acl only.
source§

fn acl_setuser<S, V>( + &self, + username: S, + rules: V, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Create an ACL user with the specified rules or modify the rules of an existing user. Read more
source§

fn acl_load(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will reload +the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. Read more
source§

fn acl_save(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will save the +currently defined ACLs from the server memory to the ACL file. Read more
source§

fn acl_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the currently active ACL rules in the Redis server. Read more
source§

fn acl_users<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows a list of all the usernames of the currently configured users in the Redis ACL system. Read more
source§

fn acl_getuser<R, S>(&self, username: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns all the rules defined for an existing ACL user. Read more
source§

fn acl_deluser<R, S>( + &self, + usernames: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Delete all the specified ACL users and terminate all the connections that are authenticated with such users. Read more
source§

fn acl_cat<R>( + &self, + category: Option<Str>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the available ACL categories if called without arguments. If a category name is given, +the command shows all the Redis commands in the specified category. Read more
source§

fn acl_genpass<R>( + &self, + bits: Option<u16>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Generate a password with length bits, returning the password. Read more
source§

fn acl_whoami<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the username the current connection is authenticated with. New connections are authenticated +with the “default” user. Read more
source§

fn acl_log_count<R>( + &self, + count: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read count recent ACL security events. Read more
source§

fn acl_log_reset(&self) -> impl Future<Output = RedisResult<()>>

Clear the ACL security events logs. Read more
source§

impl AuthInterface for SentinelClient

source§

fn auth<S>( + &self, + username: Option<String>, + password: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Request for authentication in a password-protected Redis server. Returns ok if successful. Read more
source§

fn hello( + &self, + version: RespVersion, + auth: Option<(Str, Str)>, + setname: Option<Str>, +) -> impl Future<Output = RedisResult<()>>

Switch to a different protocol, optionally authenticating in the process. Read more
source§

impl ClientInterface for SentinelClient

Available on crate feature i-client only.
source§

fn client_id<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the ID of the current connection. Read more
source§

fn connection_ids(&self) -> impl Future<Output = HashMap<Server, i64>>

Read the connection IDs for the active connections to each server. Read more
source§

fn client_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns information and statistics about the current client connection in a mostly human readable +format. Read more
source§

fn client_kill<R>( + &self, + filters: Vec<ClientKillFilter>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Close a given connection or set of connections. Read more
source§

fn client_list<R, I>( + &self, + type: Option<ClientKillType>, + ids: Option<Vec<String>>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT LIST command returns information and statistics about the client connections server in a mostly human +readable format. Read more
source§

fn client_getname<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT GETNAME returns the name of the current connection as set by CLIENT SETNAME. Read more
source§

fn client_setname<S>(&self, name: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Assign a name to the current connection. Read more
source§

fn client_pause( + &self, + timeout: i64, + mode: Option<ClientPauseKind>, +) -> impl Future<Output = RedisResult<()>>

CLIENT PAUSE is a connections control command able to suspend all the Redis clients for the specified amount of +time (in milliseconds). Read more
source§

fn client_unpause(&self) -> impl Future<Output = RedisResult<()>>

CLIENT UNPAUSE is used to resume command processing for all clients that were paused by CLIENT PAUSE. Read more
source§

fn client_reply( + &self, + flag: ClientReplyFlag, +) -> impl Future<Output = RedisResult<()>>

The CLIENT REPLY command controls whether the server will reply the client’s commands. The following modes are +available: Read more
source§

fn client_unblock<R, S>( + &self, + id: S, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisValue>,

This command can unblock, from a different connection, a client blocked in a blocking operation, such as for +instance BRPOP or XREAD or WAIT. Read more
source§

fn unblock_self( + &self, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<()>>

A convenience function to unblock any blocked connection on this client.
source§

fn client_tracking<R, T, P>( + &self, + toggle: T, + redirect: Option<i64>, + prefixes: P, + bcast: bool, + optin: bool, + optout: bool, + noloop: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<Toggle>, + T::Error: Into<RedisError>, + P: Into<MultipleStrings>,

Available on crate feature i-tracking only.
This command enables the tracking feature of the Redis server that is used for server assisted client side +caching. Read more
source§

fn client_trackinginfo<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
The command returns information about the current client connection’s use of the server assisted client side +caching feature. Read more
source§

fn client_getredir<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command returns the client ID we are redirecting our tracking notifications to. Read more
source§

fn client_caching<R>( + &self, + enabled: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command controls the tracking of the keys in the next command executed by the connection, when tracking is +enabled in OPTIN or OPTOUT mode. Read more
source§

impl ClientLike for SentinelClient

source§

fn id(&self) -> &str

The unique ID identifying this client and underlying connections.
source§

fn client_config(&self) -> RedisConfig

Read the config used to initialize the client.
source§

fn client_reconnect_policy(&self) -> Option<ReconnectPolicy>

Read the reconnect policy used to initialize the client.
source§

fn connection_config(&self) -> &ConnectionConfig

Read the connection config used to initialize the client.
source§

fn protocol_version(&self) -> RespVersion

Read the RESP version used by the client when communicating with the server.
source§

fn has_reconnect_policy(&self) -> bool

Whether the client has a reconnection policy.
source§

fn is_pipelined(&self) -> bool

Whether the client will automatically pipeline commands.
source§

fn is_clustered(&self) -> bool

Whether the client is connected to a cluster.
source§

fn uses_sentinels(&self) -> bool

Whether the client uses the sentinel interface.
source§

fn update_perf_config(&self, config: PerformanceConfig)

Update the internal PerformanceConfig in place with new values.
source§

fn perf_config(&self) -> PerformanceConfig

Read the PerformanceConfig associated with this client.
source§

fn state(&self) -> ClientState

Read the state of the underlying connection(s). Read more
source§

fn is_connected(&self) -> bool

Whether all underlying connections are healthy.
source§

fn active_connections( + &self, +) -> impl Future<Output = Result<Vec<Server>, RedisError>>

Read the set of active connections managed by the client.
source§

fn server_version(&self) -> Option<Version>

Read the server version, if known.
source§

fn set_resolver(&self, resolver: Rc<dyn Resolve>) -> impl Future

Available on crate feature dns only.
Override the DNS resolution logic for the client.
source§

fn connect(&self) -> ConnectHandle

Connect to the server. Read more
source§

fn force_reconnection(&self) -> impl Future<Output = RedisResult<()>>

Force a reconnection to the server(s). Read more
source§

fn wait_for_connect(&self) -> impl Future<Output = RedisResult<()>>

Wait for the result of the next connection attempt. Read more
source§

fn init(&self) -> impl Future<Output = RedisResult<ConnectHandle>>

Initialize a new routing and connection task and wait for it to connect successfully. Read more
source§

fn quit(&self) -> impl Future<Output = RedisResult<()>>

Close the connection to the Redis server. The returned future resolves when the command has been written to the +socket, not when the connection has been fully closed. Some time after this future resolves the future +returned by connect will resolve which indicates that the connection has been fully closed. Read more
source§

fn shutdown( + &self, + flags: Option<ShutdownFlags>, +) -> impl Future<Output = RedisResult<()>>

Available on crate feature i-server only.
Shut down the server and quit the client. Read more
source§

fn flushall<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Delete the keys in all databases. Read more
source§

fn flushall_cluster(&self) -> impl Future<Output = RedisResult<()>>

Delete the keys on all nodes in the cluster. This is a special function that does not map directly to the Redis +interface.
source§

fn ping<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Ping the Redis server. Read more
source§

fn info<R>( + &self, + section: Option<InfoKind>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read info about the server. Read more
source§

fn custom<R, T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command that is not yet supported via another interface on this client. This is most useful when +interacting with third party modules or extensions. Read more
source§

fn custom_raw<T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<Resp3Frame>>
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command similar to custom, but return the response frame directly without any +parsing. Read more
source§

fn with_options(&self, options: &Options) -> WithOptions<Self>

Customize various configuration options on commands.
source§

impl Clone for SentinelClient

source§

fn clone(&self) -> SentinelClient

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SentinelClient

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl EventInterface for SentinelClient

source§

fn on_message<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Message) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each publish-subscribe message. Read more
source§

fn on_keyspace_event<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(KeyspaceEvent) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each keyspace event. Read more
source§

fn on_reconnect<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Server) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each reconnection event. Read more
source§

fn on_cluster_change<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Vec<ClusterStateChange>) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each cluster change event. Read more
source§

fn on_error<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(RedisError) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each connection error event. Read more
source§

fn on_unresponsive<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Server) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function whenever the client detects an unresponsive connection.
source§

fn on_any<Fe, Fr, Fc>( + &self, + error_fn: Fe, + reconnect_fn: Fr, + cluster_change_fn: Fc, +) -> JoinHandle<RedisResult<()>>
where + Fe: Fn(RedisError) -> RedisResult<()> + 'static, + Fr: Fn(Server) -> RedisResult<()> + 'static, + Fc: Fn(Vec<ClusterStateChange>) -> RedisResult<()> + 'static,

Spawn one task that listens for all connection management event types. Read more
source§

fn message_rx(&self) -> BroadcastReceiver<Message>

Listen for messages on the publish-subscribe interface. Read more
source§

fn keyspace_event_rx(&self) -> BroadcastReceiver<KeyspaceEvent>

Listen for keyspace and keyevent notifications on the publish-subscribe interface. Read more
source§

fn reconnect_rx(&self) -> BroadcastReceiver<Server>

Listen for reconnection notifications. Read more
source§

fn cluster_change_rx(&self) -> BroadcastReceiver<Vec<ClusterStateChange>>

Listen for notifications whenever the cluster state changes. Read more
source§

fn error_rx(&self) -> BroadcastReceiver<RedisError>

Listen for protocol and connection errors. This stream can be used to more intelligently handle errors that may +not appear in the request-response cycle, and so cannot be handled by response futures.
source§

fn unresponsive_rx(&self) -> BroadcastReceiver<Server>

Receive a message when the client initiates a reconnection after detecting an unresponsive connection.
source§

impl HeartbeatInterface for SentinelClient

Available on crate feature i-server only.
source§

fn enable_heartbeat( + &self, + interval: Duration, + break_on_error: bool, +) -> impl Future<Output = RedisResult<()>>

Return a future that will ping the server on an interval.
source§

impl MetricsInterface for SentinelClient

source§

fn read_redelivery_count(&self) -> usize

Read the number of request redeliveries. Read more
source§

fn take_redelivery_count(&self) -> usize

Read and reset the number of request redeliveries.
source§

fn command_queue_len(&self) -> usize

Read the number of buffered commands that have not yet been sent to the server.
source§

fn read_latency_metrics(&self) -> Stats

Available on crate feature metrics only.
Read latency metrics across all commands. Read more
source§

fn take_latency_metrics(&self) -> Stats

Available on crate feature metrics only.
Read and consume latency metrics, resetting their values afterwards.
source§

fn read_network_latency_metrics(&self) -> Stats

Available on crate feature metrics only.
Read network latency metrics across all commands. Read more
source§

fn take_network_latency_metrics(&self) -> Stats

Available on crate feature metrics only.
Read and consume network latency metrics, resetting their values afterwards.
source§

fn read_req_size_metrics(&self) -> Stats

Available on crate feature metrics only.
Read request payload size metrics across all commands.
source§

fn take_req_size_metrics(&self) -> Stats

Available on crate feature metrics only.
Read and consume request payload size metrics, resetting their values afterwards.
source§

fn read_res_size_metrics(&self) -> Stats

Available on crate feature metrics only.
Read response payload size metrics across all commands.
source§

fn take_res_size_metrics(&self) -> Stats

Available on crate feature metrics only.
Read and consume response payload size metrics, resetting their values afterwards.
source§

impl PubsubInterface for SentinelClient

Available on crate feature i-pubsub only.
source§

fn subscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribe to a channel on the publish-subscribe interface. Read more
source§

fn unsubscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribe from a channel on the PubSub interface. Read more
source§

fn psubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribes the client to the given patterns. Read more
source§

fn punsubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribes the client from the given patterns, or from all of them if none is given. Read more
source§

fn publish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Publish a message on the PubSub interface, returning the number of clients that received the message. Read more
source§

fn ssubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Subscribes the client to the specified shard channels. Read more
source§

fn sunsubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Unsubscribes the client from the given shard channels, or from all of them if none is given. Read more
source§

fn spublish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Posts a message to the given shard channel. Read more
source§

fn pubsub_channels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active channels. Read more
source§

fn pubsub_numpat<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of unique patterns that are subscribed to by clients. Read more
source§

fn pubsub_numsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers (exclusive of clients subscribed to patterns) for the specified channels. Read more
source§

fn pubsub_shardchannels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active shard channels. Read more
source§

fn pubsub_shardnumsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers for the specified shard channels. Read more
source§

impl SentinelInterface for SentinelClient

source§

fn ckquorum<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Check if the current Sentinel configuration is able to reach the quorum needed to failover a master, and the +majority needed to authorize the failover.
source§

fn flushconfig<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Force Sentinel to rewrite its configuration on disk, including the current Sentinel state.
source§

fn failover<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Force a failover as if the master was not reachable, and without asking for agreement to other Sentinels.
source§

fn get_master_addr_by_name<R, N>( + &self, + name: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Return the ip and port number of the master with that name.
source§

fn info_cache<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return cached INFO output from masters and replicas.
source§

fn master<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Show the state and info of the specified master.
source§

fn masters<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Show a list of monitored masters and their state.
source§

fn monitor<R, N>( + &self, + name: N, + ip: IpAddr, + port: u16, + quorum: u32, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Start Sentinel’s monitoring. Read more
source§

fn myid<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the ID of the Sentinel instance.
source§

fn pending_scripts<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command returns information about pending scripts.
source§

fn remove<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Stop Sentinel’s monitoring. Read more
source§

fn replicas<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Show a list of replicas for this master, and their state.
source§

fn sentinels<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Show a list of sentinel instances for this master, and their state.
source§

fn set<R, N, V>(&self, name: N, args: V) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Set Sentinel’s monitoring configuration. Read more
source§

fn simulate_failure<R>( + &self, + kind: SentinelFailureKind, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command simulates different Sentinel crash scenarios.
source§

fn reset<R, P>(&self, pattern: P) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + P: Into<Str>,

This command will reset all the masters with matching name.
source§

fn config_get<R, K>(&self, name: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<Str>,

Get the current value of a global Sentinel configuration parameter. The specified name may be a wildcard, +similar to the Redis CONFIG GET command.
source§

fn config_set<R, K, V>( + &self, + name: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set the value of a global Sentinel configuration parameter.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/clients/struct.SubscriberClient.html b/doc/fred/clients/struct.SubscriberClient.html new file mode 100644 index 00000000..035e74af --- /dev/null +++ b/doc/fred/clients/struct.SubscriberClient.html @@ -0,0 +1,2310 @@ +SubscriberClient in fred::clients - Rust

Struct fred::clients::SubscriberClient

source ·
pub struct SubscriberClient { /* private fields */ }
Available on crate feature subscriber-client only.
Expand description

A subscriber client that will manage subscription state to any pubsub channels or patterns for the caller.

+

If the connection to the server closes for any reason this struct can automatically re-subscribe to channels, +patterns, and sharded channels.

+

Note: most non-pubsub commands are only supported when using RESP3.

+ +
use fred::clients::SubscriberClient;
+use fred::prelude::*;
+
+async fn example() -> Result<(), RedisError> {
+  let subscriber = Builder::default_centralized().build_subscriber_client()?;
+  subscriber.init().await?;
+
+  // spawn a task that will re-subscribe to channels and patterns after reconnecting
+  let _ = subscriber.manage_subscriptions();
+
+  let mut message_rx = subscriber.message_rx();
+  let jh = tokio::spawn(async move {
+    while let Ok(message) = message_rx.recv().await {
+      println!("Recv message {:?} on channel {}", message.value, message.channel);
+    }
+  });
+
+  let _ = subscriber.subscribe("foo").await?;
+  let _ = subscriber.psubscribe("bar*").await?;
+  println!("Tracking channels: {:?}", subscriber.tracked_channels()); // foo
+  println!("Tracking patterns: {:?}", subscriber.tracked_patterns()); // bar*
+
+  // force a re-subscription
+  subscriber.resubscribe_all().await?;
+  // clear all the local state and unsubscribe
+  subscriber.unsubscribe_all().await?;
+  subscriber.quit().await?;
+  Ok(())
+}
+

Implementations§

source§

impl SubscriberClient

source

pub fn new( + config: RedisConfig, + perf: Option<PerformanceConfig>, + connection: Option<ConnectionConfig>, + policy: Option<ReconnectPolicy>, +) -> SubscriberClient

Create a new client instance without connecting to the server.

+

See the builder interface for more information.

+
source

pub fn clone_new(&self) -> Self

Create a new SubscriberClient from the config provided to this client.

+

The returned client will not be connected to the server, and it will use new connections after connecting. +However, it will manage the same channel subscriptions as the original client.

+
source

pub fn manage_subscriptions(&self) -> JoinHandle<()>

Spawn a task that will automatically re-subscribe to any channels or channel patterns used by the client.

+
source

pub fn tracked_channels(&self) -> BTreeSet<Str>

Read the set of channels that this client will manage.

+
source

pub fn tracked_patterns(&self) -> BTreeSet<Str>

Read the set of channel patterns that this client will manage.

+
source

pub fn tracked_shard_channels(&self) -> BTreeSet<Str>

Read the set of shard channels that this client will manage.

+
source

pub async fn resubscribe_all(&self) -> Result<(), RedisError>

Re-subscribe to any tracked channels and patterns.

+

This can be used to sync the client’s subscriptions with the server after calling QUIT, then connect, etc.

+
source

pub async fn unsubscribe_all(&self) -> Result<(), RedisError>

Unsubscribe from all tracked channels and patterns, and remove them from the client cache.

+
source

pub fn to_client(&self) -> RedisClient

Create a new RedisClient, reusing the existing connection(s).

+

Note: most non-pubsub commands are only supported when using RESP3.

+

Trait Implementations§

source§

impl AclInterface for SubscriberClient

Available on crate feature i-acl only.
source§

fn acl_setuser<S, V>( + &self, + username: S, + rules: V, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Create an ACL user with the specified rules or modify the rules of an existing user. Read more
source§

fn acl_load(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will reload +the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. Read more
source§

fn acl_save(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will save the +currently defined ACLs from the server memory to the ACL file. Read more
source§

fn acl_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the currently active ACL rules in the Redis server. Read more
source§

fn acl_users<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows a list of all the usernames of the currently configured users in the Redis ACL system. Read more
source§

fn acl_getuser<R, S>(&self, username: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns all the rules defined for an existing ACL user. Read more
source§

fn acl_deluser<R, S>( + &self, + usernames: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Delete all the specified ACL users and terminate all the connections that are authenticated with such users. Read more
source§

fn acl_cat<R>( + &self, + category: Option<Str>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the available ACL categories if called without arguments. If a category name is given, +the command shows all the Redis commands in the specified category. Read more
source§

fn acl_genpass<R>( + &self, + bits: Option<u16>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Generate a password with length bits, returning the password. Read more
source§

fn acl_whoami<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the username the current connection is authenticated with. New connections are authenticated +with the “default” user. Read more
source§

fn acl_log_count<R>( + &self, + count: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read count recent ACL security events. Read more
source§

fn acl_log_reset(&self) -> impl Future<Output = RedisResult<()>>

Clear the ACL security events logs. Read more
source§

impl AuthInterface for SubscriberClient

source§

fn auth<S>( + &self, + username: Option<String>, + password: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Request for authentication in a password-protected Redis server. Returns ok if successful. Read more
source§

fn hello( + &self, + version: RespVersion, + auth: Option<(Str, Str)>, + setname: Option<Str>, +) -> impl Future<Output = RedisResult<()>>

Switch to a different protocol, optionally authenticating in the process. Read more
source§

impl ClientInterface for SubscriberClient

Available on crate feature i-client only.
source§

fn client_id<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the ID of the current connection. Read more
source§

fn connection_ids(&self) -> impl Future<Output = HashMap<Server, i64>>

Read the connection IDs for the active connections to each server. Read more
source§

fn client_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns information and statistics about the current client connection in a mostly human readable +format. Read more
source§

fn client_kill<R>( + &self, + filters: Vec<ClientKillFilter>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Close a given connection or set of connections. Read more
source§

fn client_list<R, I>( + &self, + type: Option<ClientKillType>, + ids: Option<Vec<String>>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT LIST command returns information and statistics about the client connections server in a mostly human +readable format. Read more
source§

fn client_getname<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT GETNAME returns the name of the current connection as set by CLIENT SETNAME. Read more
source§

fn client_setname<S>(&self, name: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Assign a name to the current connection. Read more
source§

fn client_pause( + &self, + timeout: i64, + mode: Option<ClientPauseKind>, +) -> impl Future<Output = RedisResult<()>>

CLIENT PAUSE is a connections control command able to suspend all the Redis clients for the specified amount of +time (in milliseconds). Read more
source§

fn client_unpause(&self) -> impl Future<Output = RedisResult<()>>

CLIENT UNPAUSE is used to resume command processing for all clients that were paused by CLIENT PAUSE. Read more
source§

fn client_reply( + &self, + flag: ClientReplyFlag, +) -> impl Future<Output = RedisResult<()>>

The CLIENT REPLY command controls whether the server will reply the client’s commands. The following modes are +available: Read more
source§

fn client_unblock<R, S>( + &self, + id: S, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisValue>,

This command can unblock, from a different connection, a client blocked in a blocking operation, such as for +instance BRPOP or XREAD or WAIT. Read more
source§

fn unblock_self( + &self, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<()>>

A convenience function to unblock any blocked connection on this client.
source§

fn client_tracking<R, T, P>( + &self, + toggle: T, + redirect: Option<i64>, + prefixes: P, + bcast: bool, + optin: bool, + optout: bool, + noloop: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<Toggle>, + T::Error: Into<RedisError>, + P: Into<MultipleStrings>,

Available on crate feature i-tracking only.
This command enables the tracking feature of the Redis server that is used for server assisted client side +caching. Read more
source§

fn client_trackinginfo<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
The command returns information about the current client connection’s use of the server assisted client side +caching feature. Read more
source§

fn client_getredir<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command returns the client ID we are redirecting our tracking notifications to. Read more
source§

fn client_caching<R>( + &self, + enabled: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command controls the tracking of the keys in the next command executed by the connection, when tracking is +enabled in OPTIN or OPTOUT mode. Read more
source§

impl ClientLike for SubscriberClient

source§

fn id(&self) -> &str

The unique ID identifying this client and underlying connections.
source§

fn client_config(&self) -> RedisConfig

Read the config used to initialize the client.
source§

fn client_reconnect_policy(&self) -> Option<ReconnectPolicy>

Read the reconnect policy used to initialize the client.
source§

fn connection_config(&self) -> &ConnectionConfig

Read the connection config used to initialize the client.
source§

fn protocol_version(&self) -> RespVersion

Read the RESP version used by the client when communicating with the server.
source§

fn has_reconnect_policy(&self) -> bool

Whether the client has a reconnection policy.
source§

fn is_pipelined(&self) -> bool

Whether the client will automatically pipeline commands.
source§

fn is_clustered(&self) -> bool

Whether the client is connected to a cluster.
source§

fn uses_sentinels(&self) -> bool

Whether the client uses the sentinel interface.
source§

fn update_perf_config(&self, config: PerformanceConfig)

Update the internal PerformanceConfig in place with new values.
source§

fn perf_config(&self) -> PerformanceConfig

Read the PerformanceConfig associated with this client.
source§

fn state(&self) -> ClientState

Read the state of the underlying connection(s). Read more
source§

fn is_connected(&self) -> bool

Whether all underlying connections are healthy.
source§

fn active_connections( + &self, +) -> impl Future<Output = Result<Vec<Server>, RedisError>>

Read the set of active connections managed by the client.
source§

fn server_version(&self) -> Option<Version>

Read the server version, if known.
source§

fn set_resolver(&self, resolver: Rc<dyn Resolve>) -> impl Future

Available on crate feature dns only.
Override the DNS resolution logic for the client.
source§

fn connect(&self) -> ConnectHandle

Connect to the server. Read more
source§

fn force_reconnection(&self) -> impl Future<Output = RedisResult<()>>

Force a reconnection to the server(s). Read more
source§

fn wait_for_connect(&self) -> impl Future<Output = RedisResult<()>>

Wait for the result of the next connection attempt. Read more
source§

fn init(&self) -> impl Future<Output = RedisResult<ConnectHandle>>

Initialize a new routing and connection task and wait for it to connect successfully. Read more
source§

fn quit(&self) -> impl Future<Output = RedisResult<()>>

Close the connection to the Redis server. The returned future resolves when the command has been written to the +socket, not when the connection has been fully closed. Some time after this future resolves the future +returned by connect will resolve which indicates that the connection has been fully closed. Read more
source§

fn shutdown( + &self, + flags: Option<ShutdownFlags>, +) -> impl Future<Output = RedisResult<()>>

Available on crate feature i-server only.
Shut down the server and quit the client. Read more
source§

fn flushall<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Delete the keys in all databases. Read more
source§

fn flushall_cluster(&self) -> impl Future<Output = RedisResult<()>>

Delete the keys on all nodes in the cluster. This is a special function that does not map directly to the Redis +interface.
source§

fn ping<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Ping the Redis server. Read more
source§

fn info<R>( + &self, + section: Option<InfoKind>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read info about the server. Read more
source§

fn custom<R, T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command that is not yet supported via another interface on this client. This is most useful when +interacting with third party modules or extensions. Read more
source§

fn custom_raw<T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<Resp3Frame>>
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command similar to custom, but return the response frame directly without any +parsing. Read more
source§

fn with_options(&self, options: &Options) -> WithOptions<Self>

Customize various configuration options on commands.
source§

impl Clone for SubscriberClient

source§

fn clone(&self) -> SubscriberClient

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl ClusterInterface for SubscriberClient

Available on crate feature i-cluster only.
source§

fn cached_cluster_state(&self) -> Option<ClusterRouting>

Read the cached cluster state used for routing commands to the correct cluster nodes.
source§

fn num_primary_cluster_nodes(&self) -> usize

Read the number of known primary cluster nodes, or 0 if the cluster state is not known.
source§

fn sync_cluster(&self) -> impl Future<Output = Result<(), RedisError>>

Update the cached cluster state and add or remove any changed cluster node connections.
source§

fn cluster_bumpepoch<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Advances the cluster config epoch. Read more
source§

fn cluster_flushslots(&self) -> impl Future<Output = RedisResult<()>>

Deletes all slots from a node. Read more
source§

fn cluster_myid<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the node’s id. Read more
source§

fn cluster_nodes<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read the current cluster node configuration. Read more
source§

fn cluster_saveconfig(&self) -> impl Future<Output = RedisResult<()>>

Forces a node to save the nodes.conf configuration on disk. Read more
source§

fn cluster_slots<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER SLOTS returns details about which cluster slots map to which Redis instances. Read more
source§

fn cluster_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER INFO provides INFO style information about Redis Cluster vital parameters. Read more
source§

fn cluster_add_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

This command is useful in order to modify a node’s view of the cluster configuration. Specifically it assigns a +set of hash slots to the node receiving the command. Read more
source§

fn cluster_count_failure_reports<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns the number of failure reports for the specified node. Read more
source§

fn cluster_count_keys_in_slot<R>( + &self, + slot: u16, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of keys in the specified Redis Cluster hash slot. Read more
source§

fn cluster_del_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

The CLUSTER DELSLOTS command asks a particular Redis Cluster node to forget which master is serving the hash +slots specified as arguments. Read more
source§

fn cluster_failover( + &self, + flag: Option<ClusterFailoverFlag>, +) -> impl Future<Output = RedisResult<()>>

This command, that can only be sent to a Redis Cluster replica node, forces the replica to start a manual +failover of its master instance. Read more
source§

fn cluster_forget<S>(&self, node_id: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command is used in order to remove a node, specified via its node ID, from the set of known nodes of the +Redis Cluster node receiving the command. In other words the specified node is removed from the nodes table of +the node receiving the command. Read more
source§

fn cluster_get_keys_in_slot<R>( + &self, + slot: u16, + count: u64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns an array of keys names stored in the contacted node and hashing to the specified hash slot. Read more
source§

fn cluster_keyslot<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns an integer identifying the hash slot the specified key hashes to. Read more
source§

fn cluster_meet<S>( + &self, + ip: S, + port: u16, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

CLUSTER MEET is used in order to connect different Redis nodes with cluster support enabled, into a working +cluster. Read more
source§

fn cluster_replicate<S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command reconfigures a node as a replica of the specified master. If the node receiving the command is an +empty master, as a side effect of the command, the node role is changed from master to replica. Read more
source§

fn cluster_replicas<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command provides a list of replica nodes replicating from the specified master node. Read more
source§

fn cluster_reset( + &self, + mode: Option<ClusterResetFlag>, +) -> impl Future<Output = RedisResult<()>>

Reset a Redis Cluster node, in a more or less drastic way depending on the reset type, that can be hard or soft. +Note that this command does not work for masters if they hold one or more keys, in that case to completely +reset a master node keys must be removed first, e.g. by using FLUSHALL first, and then CLUSTER RESET. Read more
source§

fn cluster_set_config_epoch( + &self, + epoch: u64, +) -> impl Future<Output = RedisResult<()>>

This command sets a specific config epoch in a fresh node. Read more
source§

fn cluster_setslot( + &self, + slot: u16, + state: ClusterSetSlotState, +) -> impl Future<Output = RedisResult<()>>

CLUSTER SETSLOT is responsible for changing the state of a hash slot in the receiving node in different ways. Read more
source§

impl ConfigInterface for SubscriberClient

Available on crate feature i-config only.
source§

fn config_resetstat(&self) -> impl Future<Output = RedisResult<()>>

Resets the statistics reported by Redis using the INFO command. Read more
source§

fn config_rewrite(&self) -> impl Future<Output = RedisResult<()>>

The CONFIG REWRITE command rewrites the redis.conf file the server was started with, applying the minimal +changes needed to make it reflect the configuration currently used by the server, which may be different +compared to the original one because of the use of the CONFIG SET command. Read more
source§

fn config_get<R, S>(&self, parameter: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The CONFIG GET command is used to read the configuration parameters of a running Redis server. Read more
source§

fn config_set<P, V>( + &self, + parameter: P, + value: V, +) -> impl Future<Output = RedisResult<()>>
where + P: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The CONFIG SET command is used in order to reconfigure the server at run time without the need to restart Redis. Read more
source§

impl Debug for SubscriberClient

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl EventInterface for SubscriberClient

source§

fn on_message<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Message) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each publish-subscribe message. Read more
source§

fn on_keyspace_event<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(KeyspaceEvent) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each keyspace event. Read more
source§

fn on_reconnect<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Server) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each reconnection event. Read more
source§

fn on_cluster_change<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Vec<ClusterStateChange>) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each cluster change event. Read more
source§

fn on_error<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(RedisError) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each connection error event. Read more
source§

fn on_unresponsive<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Server) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function whenever the client detects an unresponsive connection.
source§

fn on_any<Fe, Fr, Fc>( + &self, + error_fn: Fe, + reconnect_fn: Fr, + cluster_change_fn: Fc, +) -> JoinHandle<RedisResult<()>>
where + Fe: Fn(RedisError) -> RedisResult<()> + 'static, + Fr: Fn(Server) -> RedisResult<()> + 'static, + Fc: Fn(Vec<ClusterStateChange>) -> RedisResult<()> + 'static,

Spawn one task that listens for all connection management event types. Read more
source§

fn message_rx(&self) -> BroadcastReceiver<Message>

Listen for messages on the publish-subscribe interface. Read more
source§

fn keyspace_event_rx(&self) -> BroadcastReceiver<KeyspaceEvent>

Listen for keyspace and keyevent notifications on the publish-subscribe interface. Read more
source§

fn reconnect_rx(&self) -> BroadcastReceiver<Server>

Listen for reconnection notifications. Read more
source§

fn cluster_change_rx(&self) -> BroadcastReceiver<Vec<ClusterStateChange>>

Listen for notifications whenever the cluster state changes. Read more
source§

fn error_rx(&self) -> BroadcastReceiver<RedisError>

Listen for protocol and connection errors. This stream can be used to more intelligently handle errors that may +not appear in the request-response cycle, and so cannot be handled by response futures.
source§

fn unresponsive_rx(&self) -> BroadcastReceiver<Server>

Receive a message when the client initiates a reconnection after detecting an unresponsive connection.
source§

impl FunctionInterface for SubscriberClient

Available on crate feature i-scripts only.
source§

fn fcall<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Invoke a function. Read more
source§

fn fcall_ro<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

This is a read-only variant of the FCALL command that cannot execute commands that modify data. Read more
source§

fn function_delete<R, S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Delete a library and all its functions. Read more
source§

fn function_delete_cluster<S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Delete a library and all its functions from each cluster node concurrently. Read more
source§

fn function_dump<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the serialized payload of loaded libraries. Read more
source§

fn function_flush<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Deletes all the libraries. Read more
source§

fn function_flush_cluster( + &self, + async: bool, +) -> impl Future<Output = RedisResult<()>>

Deletes all the libraries on all cluster nodes concurrently. Read more
source§

fn function_kill<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Kill a function that is currently executing. Read more
source§

fn function_list<R, S>( + &self, + library_name: Option<S>, + withcode: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Return information about the functions and libraries. Read more
source§

fn function_load<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis. Read more
source§

fn function_load_cluster<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis on all cluster nodes concurrently. Read more
source§

fn function_restore<R, B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload. Read more
source§

fn function_restore_cluster<B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<()>>
where + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload on all cluster nodes concurrently. Read more
source§

fn function_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return information about the function that’s currently running and information about the available execution +engines. Read more
source§

impl GeoInterface for SubscriberClient

Available on crate feature i-geo only.
source§

fn geoadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + changed: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: Into<MultipleGeoValues>,

Adds the specified geospatial items (longitude, latitude, name) to the specified key. Read more
source§

fn geohash<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return valid Geohash strings representing the position of one or more elements in a sorted set value +representing a geospatial index (where elements were added using GEOADD). Read more
source§

fn geopos<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by +the sorted set at key. Read more
source§

fn geodist<R, K, S, D>( + &self, + key: K, + src: S, + dest: D, + unit: Option<GeoUnit>, +) -> impl Future<Output = RedisResult<R>>

Return the distance between two members in the geospatial index represented by the sorted set. Read more
source§

fn georadius<R, K, P>( + &self, + key: K, + position: P, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<GeoPosition>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified with the center location and the maximum distance from the center (the radius). Read more
source§

fn georadiusbymember<R, K, V>( + &self, + key: K, + member: V, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

This command is exactly like GEORADIUS with the sole difference that instead of taking, as the center of the +area to query, a longitude and latitude value, it takes the name of a member already existing inside the +geospatial index represented by the sorted set. Read more
source§

fn geosearch<R, K>( + &self, + key: K, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + withcoord: bool, + withdist: bool, + withhash: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified by a given shape. Read more
source§

fn geosearchstore<R, D, S>( + &self, + dest: D, + source: S, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + storedist: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>,

This command is like GEOSEARCH, but stores the result in destination key. Returns the number of members added to +the destination key. Read more
source§

impl HashesInterface for SubscriberClient

Available on crate feature i-hashes only.
source§

fn hgetall<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all fields and values of the hash stored at key. Read more
source§

fn hdel<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Removes the specified fields from the hash stored at key. Read more
source§

fn hexists<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns if field is an existing field in the hash stored at key. Read more
source§

fn hget<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the value associated with field in the hash stored at key. Read more
source§

fn hincrby<R, K, F>( + &self, + key: K, + field: F, + increment: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increments the number stored at field in the hash stored at key by increment. Read more
source§

fn hincrbyfloat<R, K, F>( + &self, + key: K, + field: F, + increment: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increment the specified field of a hash stored at key, and representing a floating point number, by the +specified increment. Read more
source§

fn hkeys<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all field names in the hash stored at key. Read more
source§

fn hlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of fields contained in the hash stored at key. Read more
source§

fn hmget<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Returns the values associated with the specified fields in the hash stored at key. Read more
source§

fn hmset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the specified fields to their respective values in the hash stored at key. Read more
source§

fn hset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets fields in the hash stored at key to their provided values. Read more
source§

fn hsetnx<R, K, F, V>( + &self, + key: K, + field: F, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets field in the hash stored at key to value, only if field does not yet exist. Read more
source§

fn hrandfield<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random field from the hash value stored at key. Read more
source§

fn hstrlen<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the string length of the value associated with field in the hash stored at key. Read more
source§

fn hvals<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all values in the hash stored at key. Read more
source§

impl HeartbeatInterface for SubscriberClient

Available on crate feature i-server only.
source§

fn enable_heartbeat( + &self, + interval: Duration, + break_on_error: bool, +) -> impl Future<Output = RedisResult<()>>

Return a future that will ping the server on an interval.
source§

impl HyperloglogInterface for SubscriberClient

Available on crate feature i-hyperloglog only.
source§

fn pfadd<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Adds all the element arguments to the HyperLogLog data structure stored at the variable name specified as first +argument. Read more
source§

fn pfcount<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

When called with a single key, returns the approximated cardinality computed by the HyperLogLog data structure +stored at the specified variable, which is 0 if the variable does not exist. Read more
source§

fn pfmerge<R, D, S>( + &self, + dest: D, + sources: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<MultipleKeys>,

Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of the +observed sets of the source HyperLogLog structures. Read more
source§

impl KeysInterface for SubscriberClient

Available on crate feature i-keys only.
source§

fn watch<K>(&self, keys: K) -> impl Future<Output = RedisResult<()>>
where + K: Into<MultipleKeys>,

Marks the given keys to be watched for conditional execution of a transaction. Read more
source§

fn unwatch(&self) -> impl Future<Output = RedisResult<()>>

Flushes all the previously watched keys for a transaction. Read more
source§

fn randomkey<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return a random key from the currently selected database. Read more
source§

fn copy<R, S, D>( + &self, + source: S, + destination: D, + db: Option<u8>, + replace: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

This command copies the value stored at the source key to the destination key. Read more
source§

fn dump<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Serialize the value stored at key in a Redis-specific format and return it as bulk string. Read more
source§

fn restore<R, K>( + &self, + key: K, + ttl: i64, + serialized: RedisValue, + replace: bool, + absttl: bool, + idletime: Option<i64>, + frequency: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Create a key associated with a value that is obtained by deserializing the provided serialized value Read more
source§

fn set<R, K, V>( + &self, + key: K, + value: V, + expire: Option<Expiration>, + options: Option<SetOptions>, + get: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set a value with optional NX|XX, EX|PX|EXAT|PXAT|KEEPTTL, and GET arguments. Read more
source§

fn get<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Read a value from the server. Read more
source§

fn getrange<R, K>( + &self, + key: K, + start: usize, + end: usize, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the substring of the string value stored at key with offsets start and end (both inclusive). Read more
source§

fn setrange<R, K, V>( + &self, + key: K, + offset: u32, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Overwrites part of the string stored at key, starting at the specified offset, for the entire length of +value. Read more
source§

fn getset<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Atomically sets key to value and returns the old value stored at key. Read more
source§

fn getdel<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the value of key and delete the key. This command is similar to GET, except for the fact that it also +deletes the key on success (if and only if the key’s value type is a string). Read more
source§

fn strlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the string value stored at key. An error is returned when key holds a non-string value. Read more
source§

fn del<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Removes the specified keys. A key is ignored if it does not exist. Read more
Unlinks the specified keys. A key is ignored if it does not exist Read more
source§

fn rename<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination. Read more
source§

fn renamenx<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination if destination does not yet exist. Read more
source§

fn append<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Append value to key if it’s a string. Read more
source§

fn mget<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the +special value nil is returned. Read more
source§

fn mset<V>(&self, values: V) -> impl Future<Output = RedisResult<()>>
where + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. Read more
source§

fn msetnx<R, V>(&self, values: V) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. MSETNX will not perform any operation at all even if just a +single key already exists. Read more
source§

fn incr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by_float<R, K>( + &self, + key: K, + val: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increment the string representing a floating point number stored at key by val. If the key does not exist, it +is set to 0 before performing the operation. Read more
source§

fn decr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn decr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn ttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in seconds. Read more
source§

fn pttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in milliseconds. Read more
source§

fn persist<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Remove the existing timeout on a key, turning the key from volatile (a key with an expiration) +to persistent (a key that will never expire as no timeout is associated). Read more
source§

fn expire<R, K>( + &self, + key: K, + seconds: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on key. After the timeout has expired, the key will be automatically deleted. Read more
source§

fn expire_at<R, K>( + &self, + key: K, + timestamp: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on a key based on a UNIX timestamp. Read more
source§

fn pexpire<R, K>( + &self, + key: K, + milliseconds: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command works exactly like EXPIRE but the time to live of the key is specified in milliseconds instead of +seconds. Read more
source§

fn pexpire_at<R, K>( + &self, + key: K, + timestamp: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

PEXPIREAT has the same effect and semantic as EXPIREAT, but the Unix time at which the key will expire is +specified in milliseconds instead of seconds. Read more
source§

fn exists<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns number of keys that exist from the keys arguments. Read more
source§

fn lcs<R, K1, K2>( + &self, + key1: K1, + key2: K2, + len: bool, + idx: bool, + minmatchlen: Option<i64>, + withmatchlen: bool, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis, + K1: Into<RedisKey>, + K2: Into<RedisKey>,

Runs the longest common subsequence algorithm on two keys. Read more
source§

impl ListInterface for SubscriberClient

Available on crate feature i-lists only.
source§

fn blmpop<R, K>( + &self, + timeout: f64, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::lmpop. Read more
source§

fn blpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BLPOP is a blocking list pop primitive. It is the blocking version of LPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the head of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BRPOP is a blocking list pop primitive. It is the blocking version of RPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the tail of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpoplpush<R, S, D>( + &self, + source: S, + destination: D, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::rpoplpush. Read more
source§

fn blmove<R, S, D>( + &self, + source: S, + destination: D, + source_direction: LMoveDirection, + destination_direction: LMoveDirection, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::lmove. Read more
source§

fn lmpop<R, K>( + &self, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements from the first non-empty list key from the list of provided key names. Read more
source§

fn lindex<R, K>( + &self, + key: K, + index: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the element at index in the list stored at key. Read more
source§

fn linsert<R, K, P, V>( + &self, + key: K, + location: ListLocation, + pivot: P, + element: V, +) -> impl Future<Output = RedisResult<R>>

Inserts element in the list stored at key either before or after the reference value pivot. Read more
source§

fn llen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the list stored at key. Read more
source§

fn lpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the first elements of the list stored at key. Read more
source§

fn lpos<R, K, V>( + &self, + key: K, + element: V, + rank: Option<i64>, + count: Option<i64>, + maxlen: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The command returns the index of matching elements inside a Redis list. Read more
source§

fn lpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the head of the list stored at key. Read more
source§

fn lpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the head of the list stored at key, only if key already exists and holds a list. Read more
source§

fn lrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified elements of the list stored at key. Read more
source§

fn lrem<R, K, V>( + &self, + key: K, + count: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Removes the first count occurrences of elements equal to element from the list stored at key. Read more
source§

fn lset<R, K, V>( + &self, + key: K, + index: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets the list element at index to element. Read more
source§

fn ltrim<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Trim an existing list so that it will contain only the specified range of elements specified. Read more
source§

fn rpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the last elements of the list stored at key. Read more
source§

fn rpoplpush<R, S, D>( + &self, + source: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the last element (tail) of the list stored at source, and pushes the element at +the first element (head) of the list stored at destination. Read more
source§

fn lmove<R, S, D>( + &self, + source: S, + dest: D, + source_direction: LMoveDirection, + dest_direction: LMoveDirection, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the first/last element (head/tail depending on the source direction argument) of +the list stored at source, and pushes the element at the first/last element (head/tail depending on the +destination direction argument) of the list stored at destination. Read more
source§

fn rpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the tail of the list stored at key. Read more
source§

fn rpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the tail of the list stored at key, only if key already exists and holds a list. Read more
source§

fn sort<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, + store: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Returns or stores the elements contained in the list, set or sorted set at key. Read more
source§

fn sort_ro<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Read-only variant of the SORT command. It is exactly like the original SORT but refuses the STORE option and can +safely be used in read-only replicas. Read more
source§

impl LuaInterface for SubscriberClient

Available on crate feature i-scripts only.
source§

fn script_load<R, S>(&self, script: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a script into the scripts cache, without executing it. After the specified command is loaded into the +script cache it will be callable using EVALSHA with the correct SHA1 digest of the script. Read more
source§

fn script_load_cluster<R, S>( + &self, + script: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Available on crate feature sha-1 only.
A clustered variant of script_load that loads the script on all primary nodes in a cluster. Read more
source§

fn script_kill(&self) -> impl Future<Output = RedisResult<()>>

Kills the currently executing Lua script, assuming no write operation was yet performed by the script. Read more
source§

fn script_kill_cluster(&self) -> impl Future<Output = RedisResult<()>>

A clustered variant of the script_kill command that issues the command to all primary nodes +in the cluster.
source§

fn script_flush(&self, async: bool) -> impl Future<Output = RedisResult<()>>

Flush the Lua scripts cache. Read more
source§

fn script_flush_cluster( + &self, + async: bool, +) -> impl Future<Output = RedisResult<()>>

A clustered variant of script_flush that flushes the script cache on all primary nodes in +the cluster.
source§

fn script_exists<R, H>(&self, hashes: H) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + H: Into<MultipleStrings>,

Returns information about the existence of the scripts in the script cache. Read more
source§

fn script_debug( + &self, + flag: ScriptDebugFlag, +) -> impl Future<Output = RedisResult<()>>

Set the debug mode for subsequent scripts executed with EVAL. Read more
source§

fn evalsha<R, S, K, V>( + &self, + hash: S, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Evaluates a script cached on the server side by its SHA1 digest. Read more
source§

fn eval<R, S, K, V>( + &self, + script: S, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Evaluate a Lua script on the server. Read more
source§

impl MemoryInterface for SubscriberClient

Available on crate feature i-memory only.
source§

fn memory_doctor<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY DOCTOR command reports about different memory-related issues that the Redis server experiences, and +advises about possible remedies. Read more
source§

fn memory_malloc_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY MALLOC-STATS command provides an internal statistics report from the memory allocator. Read more
source§

fn memory_purge(&self) -> impl Future<Output = RedisResult<()>>

The MEMORY PURGE command attempts to purge dirty pages so these can be reclaimed by the allocator. Read more
source§

fn memory_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY STATS command returns an Array reply about the memory usage of the server. Read more
source§

fn memory_usage<R, K>( + &self, + key: K, + samples: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

The MEMORY USAGE command reports the number of bytes that a key and its value require to be stored in RAM. Read more
source§

impl MetricsInterface for SubscriberClient

source§

fn read_redelivery_count(&self) -> usize

Read the number of request redeliveries. Read more
source§

fn take_redelivery_count(&self) -> usize

Read and reset the number of request redeliveries.
source§

fn command_queue_len(&self) -> usize

Read the number of buffered commands that have not yet been sent to the server.
source§

fn read_latency_metrics(&self) -> Stats

Available on crate feature metrics only.
Read latency metrics across all commands. Read more
source§

fn take_latency_metrics(&self) -> Stats

Available on crate feature metrics only.
Read and consume latency metrics, resetting their values afterwards.
source§

fn read_network_latency_metrics(&self) -> Stats

Available on crate feature metrics only.
Read network latency metrics across all commands. Read more
source§

fn take_network_latency_metrics(&self) -> Stats

Available on crate feature metrics only.
Read and consume network latency metrics, resetting their values afterwards.
source§

fn read_req_size_metrics(&self) -> Stats

Available on crate feature metrics only.
Read request payload size metrics across all commands.
source§

fn take_req_size_metrics(&self) -> Stats

Available on crate feature metrics only.
Read and consume request payload size metrics, resetting their values afterwards.
source§

fn read_res_size_metrics(&self) -> Stats

Available on crate feature metrics only.
Read response payload size metrics across all commands.
source§

fn take_res_size_metrics(&self) -> Stats

Available on crate feature metrics only.
Read and consume response payload size metrics, resetting their values afterwards.
source§

impl PubsubInterface for SubscriberClient

Available on crate feature i-pubsub only.
source§

fn subscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribe to a channel on the publish-subscribe interface. Read more
source§

fn unsubscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribe from a channel on the PubSub interface. Read more
source§

fn psubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribes the client to the given patterns. Read more
source§

fn punsubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribes the client from the given patterns, or from all of them if none is given. Read more
source§

fn ssubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Subscribes the client to the specified shard channels. Read more
source§

fn sunsubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Unsubscribes the client from the given shard channels, or from all of them if none is given. Read more
source§

fn publish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Publish a message on the PubSub interface, returning the number of clients that received the message. Read more
source§

fn spublish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Posts a message to the given shard channel. Read more
source§

fn pubsub_channels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active channels. Read more
source§

fn pubsub_numpat<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of unique patterns that are subscribed to by clients. Read more
source§

fn pubsub_numsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers (exclusive of clients subscribed to patterns) for the specified channels. Read more
source§

fn pubsub_shardchannels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active shard channels. Read more
source§

fn pubsub_shardnumsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers for the specified shard channels. Read more
source§

impl RediSearchInterface for SubscriberClient

Available on crate feature i-redisearch only.
source§

fn ft_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns a list of all existing indexes. Read more
source§

fn ft_aggregate<R, I, Q>( + &self, + index: I, + query: Q, + options: FtAggregateOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Run a search query on an index, and perform aggregate transformations on the results. Read more
Search the index with a textual query, returning either documents or just ids. Read more
source§

fn ft_create<R, I>( + &self, + index: I, + options: FtCreateOptions, + schema: Vec<SearchSchema>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Create an index with the given specification. Read more
source§

fn ft_alter<R, I>( + &self, + index: I, + options: FtAlterOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Add a new attribute to the index. Read more
source§

fn ft_aliasadd<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. Read more
source§

fn ft_aliasdel<R, A>(&self, alias: A) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>,

Remove an alias from an index. Read more
source§

fn ft_aliasupdate<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. If the alias is already associated with another index, FT.ALIASUPDATE removes the +alias association with the previous index. Read more
source§

fn ft_config_get<R, S>(&self, option: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Retrieve configuration options. Read more
source§

fn ft_config_set<R, S, V>( + &self, + option: S, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set the value of a RediSearch configuration parameter. Read more
source§

fn ft_cursor_del<R, I, C>( + &self, + index: I, + cursor: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Delete a cursor. Read more
source§

fn ft_cursor_read<R, I, C>( + &self, + index: I, + cursor: C, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Read next results from an existing cursor. Read more
source§

fn ft_dictadd<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Add terms to a dictionary. Read more
source§

fn ft_dictdel<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Remove terms from a dictionary. Read more
source§

fn ft_dictdump<R, D>(&self, dict: D) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>,

Dump all terms in the given dictionary. Read more
source§

fn ft_dropindex<R, I>( + &self, + index: I, + dd: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Delete an index. Read more
source§

fn ft_explain<R, I, Q>( + &self, + index: I, + query: Q, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Return the execution plan for a complex query. Read more
source§

fn ft_info<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Return information and statistics on the index. Read more
source§

fn ft_spellcheck<R, I, Q>( + &self, + index: I, + query: Q, + distance: Option<u8>, + terms: Option<SpellcheckTerms>, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Perform spelling correction on a query, returning suggestions for misspelled terms. Read more
source§

fn ft_sugadd<R, K, S>( + &self, + key: K, + string: S, + score: f64, + incr: bool, + payload: Option<Bytes>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Add a suggestion string to an auto-complete suggestion dictionary. Read more
source§

fn ft_sugdel<R, K, S>( + &self, + key: K, + string: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Delete a string from a suggestion index. Read more
source§

fn ft_sugget<R, K, P>( + &self, + key: K, + prefix: P, + fuzzy: bool, + withscores: bool, + withpayloads: bool, + max: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Get completion suggestions for a prefix. Read more
source§

fn ft_suglen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the size of an auto-complete suggestion dictionary. Read more
source§

fn ft_syndump<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Dump the contents of a synonym group. Read more
source§

fn ft_synupdate<R, I, S, T>( + &self, + index: I, + synonym_group_id: S, + skipinitialscan: bool, + terms: T, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + S: Into<Str>, + T: Into<MultipleStrings>,

Update a synonym group. Read more
source§

fn ft_tagvals<R, I, F>( + &self, + index: I, + field_name: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + F: Into<Str>,

Return a distinct set of values indexed in a Tag field. Read more
source§

impl RedisJsonInterface for SubscriberClient

Available on crate feature i-redis-json only.
source§

fn json_arrappend<R, K, P, V>( + &self, + key: K, + path: P, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json values into the array at path after the last element in it. Read more
source§

fn json_arrindex<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + start: Option<i64>, + stop: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Search for the first occurrence of a JSON value in an array. Read more
source§

fn json_arrinsert<R, K, P, V>( + &self, + key: K, + path: P, + index: i64, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Insert the json values into the array at path before the index (shifts to the right). Read more
source§

fn json_arrlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON array at path in key. Read more
source§

fn json_arrpop<R, K, P>( + &self, + key: K, + path: Option<P>, + index: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Remove and return an element from the index in the array Read more
source§

fn json_arrtrim<R, K, P>( + &self, + key: K, + path: P, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Trim an array so that it contains only the specified inclusive range of elements Read more
source§

fn json_clear<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Clear container values (arrays/objects) and set numeric values to 0 Read more
source§

fn json_debug_memory<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report a value’s memory usage in bytes Read more
source§

fn json_del<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Delete a value. Read more
source§

fn json_get<R, K, I, N, S, P>( + &self, + key: K, + indent: Option<I>, + newline: Option<N>, + space: Option<S>, + paths: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<Str>, + N: Into<Str>, + S: Into<Str>, + P: Into<MultipleStrings>,

Return the value at path in JSON serialized form. Read more
source§

fn json_merge<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Merge a given JSON value into matching paths. Read more
source§

fn json_mget<R, K, P>( + &self, + keys: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + P: Into<Str>,

Return the values at path from multiple key arguments. Read more
source§

fn json_mset<R, K, P, V>( + &self, + values: Vec<(K, P, V)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set or update one or more JSON values according to the specified key-path-value triplets. Read more
source§

fn json_numincrby<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Increment the number value stored at path by number Read more
source§

fn json_objkeys<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the keys in the object that’s referenced by path. Read more
source§

fn json_objlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the number of keys in the JSON object at path in key. Read more
source§

fn json_resp<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the JSON in key in Redis serialization protocol specification form. Read more
source§

fn json_set<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + options: Option<SetOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set the JSON value at path in key. Read more
source§

fn json_strappend<R, K, P, V>( + &self, + key: K, + path: Option<P>, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json-string values to the string at path. Read more
source§

fn json_strlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON String at path in key. Read more
source§

fn json_toggle<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Toggle a Boolean value stored at path. Read more
source§

fn json_type<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the type of JSON value at path. Read more
source§

impl ServerInterface for SubscriberClient

Available on crate feature i-server only.
source§

fn bgrewriteaof<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Instruct Redis to start an Append Only File rewrite process. Read more
source§

fn bgsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Save the DB in background. Read more
source§

fn dbsize<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the number of keys in the selected database. Read more
source§

fn select(&self, db: u8) -> impl Future<Output = RedisResult<()>>

Select the database this client should use. Read more
source§

fn failover( + &self, + to: Option<(String, u16)>, + force: bool, + abort: bool, + timeout: Option<u32>, +) -> impl Future<Output = RedisResult<()>>

This command will start a coordinated failover between the currently-connected-to master and one of its +replicas. Read more
source§

fn lastsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the UNIX TIME of the last DB save executed with success. Read more
source§

fn wait<R>( + &self, + numreplicas: i64, + timeout: i64, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis,

This command blocks the current client until all the previous write commands are successfully transferred and +acknowledged by at least the specified number of replicas. If the timeout, specified in milliseconds, is +reached, the command returns even if the specified number of replicas were not yet reached. Read more
source§

fn sentinel_primary(&self) -> Option<Server>

Read the primary Redis server identifier returned from the sentinel nodes.
source§

fn sentinel_nodes(&self) -> Option<Vec<Server>>

Read the set of known sentinel nodes.
source§

impl SetsInterface for SubscriberClient

Available on crate feature i-sets only.
source§

fn sadd<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Add the specified members to the set stored at key. Read more
source§

fn scard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the set cardinality (number of elements) of the set stored at key. Read more
source§

fn sdiff<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the difference between the first set and all the successive sets. Read more
source§

fn sdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SDIFF, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sinter<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the intersection of all the given sets. Read more
source§

fn sinterstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SINTER, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sismember<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns if member is a member of the set stored at key. Read more
source§

fn smismember<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns whether each member is a member of the set stored at key. Read more
source§

fn smembers<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all the members of the set value stored at key. Read more
source§

fn smove<R, S, D, V>( + &self, + source: S, + dest: D, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Move member from the set at source to the set at destination. Read more
source§

fn spop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns one or more random members from the set value store at key. Read more
source§

fn srandmember<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the set value stored at key. Read more
source§

fn srem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Remove the specified members from the set stored at key. Read more
source§

fn sunion<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the union of all the given sets. Read more
source§

fn sunionstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SUNION, but instead of returning the resulting set, it is stored in destination. Read more
source§

impl SlowlogInterface for SubscriberClient

Available on crate feature i-slowlog only.
source§

fn slowlog_get<R>( + &self, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read the slow queries log. Read more
source§

fn slowlog_length<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read length of the slow queries log. Read more
source§

fn slowlog_reset(&self) -> impl Future<Output = RedisResult<()>>

This command is used to reset the slow queries log. Read more
source§

impl SortedSetsInterface for SubscriberClient

Available on crate feature i-sorted-sets only.
source§

fn bzmpop<R, K>( + &self, + timeout: f64, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zmpop. Read more
source§

fn bzpopmin<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmin. Read more
source§

fn bzpopmax<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmax. Read more
source§

fn zadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + ordering: Option<Ordering>, + changed: bool, + incr: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>

Adds all the specified members with the specified scores to the sorted set stored at key. Read more
source§

fn zcard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the sorted set cardinality (number of elements) of the sorted set stored at key. Read more
source§

fn zcount<R, K>( + &self, + key: K, + min: f64, + max: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of elements in the sorted set at key with a score between min and max. Read more
source§

fn zdiff<R, K>( + &self, + keys: K, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

This command is similar to ZDIFFSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

Computes the difference between the first and all successive input sorted sets and stores the result in +destination. Read more
source§

fn zincrby<R, K, V>( + &self, + key: K, + increment: f64, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Increments the score of member in the sorted set stored at key by increment. Read more
source§

fn zinter<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZINTERSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zinterstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the intersection of the sorted sets given by the specified keys, and stores the result in +destination. Read more
source§

fn zlexcount<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns the number of elements in the sorted set at key with a value between min and +max. Read more
source§

fn zpopmax<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the highest scores in the sorted set stored at key. Read more
source§

fn zpopmin<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the lowest scores in the sorted set stored at key. Read more
source§

fn zmpop<R, K>( + &self, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements, that are member-score pairs, from the first non-empty sorted set in the provided list +of key names. Read more
source§

fn zrandmember<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the sorted set value stored at key. Read more
source§

fn zrangestore<R, D, S, M, N>( + &self, + dest: D, + source: S, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

This command is like ZRANGE, but stores the result in the destination key. Read more
source§

fn zrange<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between min and max. Read more
source§

fn zrevrangebylex<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between max and min. Read more
source§

fn zrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between min and max (including elements +with score equal to min or max). Read more
source§

fn zrevrangebyscore<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between max and min (including +elements with score equal to max or min). Read more
source§

fn zrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from low to high. Read more
source§

fn zrem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Removes the specified members from the sorted set stored at key. Non existing members are ignored. Read more
source§

fn zremrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command removes all elements in the sorted set stored at key between the lexicographical range +specified by min and max. Read more
source§

fn zremrangebyrank<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes all elements in the sorted set stored at key with rank between start and stop. Read more
source§

fn zremrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Removes all elements in the sorted set stored at key with a score between min and max. Read more
source§

fn zrevrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrevrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from high to low. Read more
source§

fn zscore<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the score of member in the sorted set at key. Read more
source§

fn zunion<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZUNIONSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zunionstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the union of the sorted sets given by the specified keys, and stores the result in destination. Read more
source§

fn zmscore<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns the scores associated with the specified members in the sorted set stored at key. Read more
source§

impl StreamsInterface for SubscriberClient

Available on crate feature i-streams only.
source§

fn xinfo_consumers<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

This command returns the list of consumers that belong to the groupname consumer group of the stream stored at +key. Read more
source§

fn xinfo_groups<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns the list of all consumers groups of the stream stored at key. Read more
source§

fn xinfo_stream<R, K>( + &self, + key: K, + full: bool, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns information about the stream stored at key. Read more
source§

fn xadd<R, K, C, I, F>( + &self, + key: K, + nomkstream: bool, + cap: C, + id: I, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<XID>, + F: TryInto<MultipleOrderedPairs>, + F::Error: Into<RedisError>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Appends the specified stream entry to the stream at the specified key. If the key does not exist, as a side +effect of running this command the key is created with a stream value. The creation of stream’s key can be +disabled with the NOMKSTREAM option. Read more
source§

fn xtrim<R, K, C>(&self, key: K, cap: C) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Trims the stream by evicting older entries (entries with lower IDs) if needed. Read more
source§

fn xdel<R, K, S>(&self, key: K, ids: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Removes the specified entries from a stream, and returns the number of entries deleted. Read more
source§

fn xrange_values<Ri, Rk, Rv, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Return the stream entries matching the provided range of IDs, automatically converting to a less verbose type +definition. Read more
source§

fn xrange<R, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

The command returns the stream entries matching a given range of IDs. The range is specified by a minimum +and maximum ID. All the entries having an ID between the two specified or exactly one of the two IDs specified +(closed interval) are returned. Read more
source§

fn xrevrange_values<Ri, Rk, Rv, K, E, S>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Similar to XRANGE, but with the results returned in reverse order. The results will be automatically converted +to a less verbose type definition. Read more
source§

fn xrevrange<R, K, S, E>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

Similar to XRANGE, but with the results returned in reverse order. Read more
source§

fn xlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of entries inside a stream. Read more
source§

fn xread_map<Rk1, Rk2, Rk3, Rv, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xread<R, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xgroup_create<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, + mkstream: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

This command creates a new consumer group uniquely identified by groupname for the stream stored at key. Read more
source§

fn xgroup_createconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Create a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_delconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Delete a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_destroy<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Completely destroy a consumer group. Read more
source§

fn xgroup_setid<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

Set the last delivered ID for a consumer group. Read more
source§

fn xreadgroup_map<Rk1, Rk2, Rk3, Rv, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xreadgroup<R, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xack<R, K, G, I>( + &self, + key: K, + group: G, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + I: Into<MultipleIDs>,

Remove one or more messages from the Pending Entries List (PEL) of a stream consumer group. Read more
source§

fn xclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

A variation of xclaim with a less verbose return type.
source§

fn xclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

In the context of a stream consumer group, this command changes the ownership of a pending message, +so that the new owner is the consumer specified as the command argument. Read more
source§

fn xautoclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<(String, Vec<XReadValue<Ri, Rk, Rv>>)>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. It also converts +the response type to a less verbose type declaration and handles potential differences between RESP2 and RESP3. Read more
source§

fn xautoclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. Read more
source§

fn xpending<R, K, G, A>( + &self, + key: K, + group: G, + args: A, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + A: Into<XPendingArgs>,

Inspect the list of pending messages in a consumer group. Read more
source§

impl TimeSeriesInterface for SubscriberClient

Available on crate feature i-time-series only.
source§

fn ts_add<R, K, T, L>( + &self, + key: K, + timestamp: T, + value: f64, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + on_duplicate: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + T: TryInto<Timestamp>, + T::Error: Into<RedisError>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Append a sample to a time series. Read more
source§

fn ts_alter<R, K, L>( + &self, + key: K, + retention: Option<u64>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Update the retention, chunk size, duplicate policy, and labels of an existing time series. Read more
source§

fn ts_create<R, K, L>( + &self, + key: K, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Create a new time series. Read more
source§

fn ts_createrule<R, S, D>( + &self, + src: S, + dest: D, + aggregation: (Aggregator, u64), + align_timestamp: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Create a compaction rule. Read more
source§

fn ts_decrby<R, K, L>( + &self, + key: K, + subtrahend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given decrement. Read more
source§

fn ts_del<R, K>( + &self, + key: K, + from: i64, + to: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Delete all samples between two timestamps for a given time series. Read more
source§

fn ts_deleterule<R, S, D>( + &self, + src: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Delete a compaction rule. Read more
source§

fn ts_get<R, K>( + &self, + key: K, + latest: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the sample with the highest timestamp from a given time series. Read more
source§

fn ts_incrby<R, K, L>( + &self, + key: K, + addend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given increment. Read more
source§

fn ts_info<R, K>( + &self, + key: K, + debug: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return information and statistics for a time series. Read more
source§

fn ts_madd<R, K, I>(&self, samples: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: IntoIterator<Item = (K, Timestamp, f64)>,

Append new samples to one or more time series. Read more
source§

fn ts_mget<R, L, S, I>( + &self, + latest: bool, + labels: Option<L>, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + L: Into<GetLabels>, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get the sample with the highest timestamp from each time series matching a specific filter. Read more
source§

fn ts_mrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the forward direction. Read more
source§

fn ts_mrevrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the reverse direction. Read more
source§

fn ts_queryindex<R, S, I>( + &self, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get all time series keys matching a filter list. Read more
source§

fn ts_range<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in forward direction. Read more
source§

fn ts_revrange<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in reverse direction. Read more
source§

impl TrackingInterface for SubscriberClient

Available on crate feature i-tracking only.
source§

fn start_tracking<P>( + &self, + prefixes: P, + bcast: bool, + optin: bool, + optout: bool, + noloop: bool, +) -> impl Future<Output = RedisResult<()>>
where + P: Into<MultipleStrings>,

Send the CLIENT TRACKING command to all connected servers, subscribing to invalidation messages on the same connection. Read more
source§

fn stop_tracking(&self) -> impl Future<Output = RedisResult<()>>

Disable client tracking on all connections.
source§

fn on_invalidation<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Invalidation) -> RedisResult<()> + 'static,

Spawn a task that processes invalidation messages from the server. Read more
source§

fn invalidation_rx(&self) -> BroadcastReceiver<Invalidation>

Subscribe to invalidation messages from the server(s).
source§

impl TransactionInterface for SubscriberClient

Available on crate feature transactions only.
source§

fn multi(&self) -> Transaction

Enter a MULTI block, executing subsequent commands as a transaction. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/clients/struct.Transaction.html b/doc/fred/clients/struct.Transaction.html new file mode 100644 index 00000000..04e00bd8 --- /dev/null +++ b/doc/fred/clients/struct.Transaction.html @@ -0,0 +1,2149 @@ +Transaction in fred::clients - Rust

Struct fred::clients::Transaction

source ·
pub struct Transaction { /* private fields */ }
Available on crate feature transactions only.
Expand description

A cheaply cloneable transaction block.

+

Implementations§

source§

impl Transaction

source

pub fn id(&self) -> u64

An ID identifying the underlying transaction state.

+
source

pub fn reset(&self)

Clear the internal command buffer and watched keys.

+
source

pub fn len(&self) -> usize

Read the number of commands queued to run.

+
source

pub fn watched_len(&self) -> usize

Read the number of keys to WATCH before the starting the transaction.

+
source

pub async fn exec<R>(&self, abort_on_error: bool) -> Result<R, RedisError>
where + R: FromRedis,

Executes all previously queued commands in a transaction.

+

If abort_on_error is true the client will automatically send DISCARD if an error is received from +any of the commands prior to EXEC. This does not apply to MOVED or ASK errors, which wll be followed +automatically.

+

https://redis.io/commands/exec

+ +

+async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  let _ = client.mset(vec![("foo", 1), ("bar", 2)]).await?;
+
+  let trx = client.multi();
+  let _: () = trx.get("foo").await?; // returns QUEUED
+  let _: () = trx.get("bar").await?; // returns QUEUED
+
+  let (foo, bar): (i64, i64) = trx.exec(false).await?;
+  assert_eq!((foo, bar), (1, 2));
+  Ok(())
+}
+
source

pub fn watch_before<K>(&self, keys: K)
where + K: Into<MultipleKeys>,

Send the WATCH command with the provided keys before starting the transaction.

+
source

pub fn hash_slot(&self) -> Option<u16>

Read the hash slot against which this transaction will run, if known.

+
source

pub fn cluster_node(&self) -> Option<Server>

Read the server ID against which this transaction will run, if known.

+

Trait Implementations§

source§

impl AclInterface for Transaction

Available on crate feature i-acl only.
source§

fn acl_setuser<S, V>( + &self, + username: S, + rules: V, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Create an ACL user with the specified rules or modify the rules of an existing user. Read more
source§

fn acl_load(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will reload +the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. Read more
source§

fn acl_save(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will save the +currently defined ACLs from the server memory to the ACL file. Read more
source§

fn acl_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the currently active ACL rules in the Redis server. Read more
source§

fn acl_users<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows a list of all the usernames of the currently configured users in the Redis ACL system. Read more
source§

fn acl_getuser<R, S>(&self, username: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns all the rules defined for an existing ACL user. Read more
source§

fn acl_deluser<R, S>( + &self, + usernames: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Delete all the specified ACL users and terminate all the connections that are authenticated with such users. Read more
source§

fn acl_cat<R>( + &self, + category: Option<Str>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the available ACL categories if called without arguments. If a category name is given, +the command shows all the Redis commands in the specified category. Read more
source§

fn acl_genpass<R>( + &self, + bits: Option<u16>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Generate a password with length bits, returning the password. Read more
source§

fn acl_whoami<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the username the current connection is authenticated with. New connections are authenticated +with the “default” user. Read more
source§

fn acl_log_count<R>( + &self, + count: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read count recent ACL security events. Read more
source§

fn acl_log_reset(&self) -> impl Future<Output = RedisResult<()>>

Clear the ACL security events logs. Read more
source§

impl AuthInterface for Transaction

source§

fn auth<S>( + &self, + username: Option<String>, + password: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Request for authentication in a password-protected Redis server. Returns ok if successful. Read more
source§

fn hello( + &self, + version: RespVersion, + auth: Option<(Str, Str)>, + setname: Option<Str>, +) -> impl Future<Output = RedisResult<()>>

Switch to a different protocol, optionally authenticating in the process. Read more
source§

impl ClientInterface for Transaction

Available on crate feature i-client only.
source§

fn client_id<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the ID of the current connection. Read more
source§

fn connection_ids(&self) -> impl Future<Output = HashMap<Server, i64>>

Read the connection IDs for the active connections to each server. Read more
source§

fn client_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns information and statistics about the current client connection in a mostly human readable +format. Read more
source§

fn client_kill<R>( + &self, + filters: Vec<ClientKillFilter>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Close a given connection or set of connections. Read more
source§

fn client_list<R, I>( + &self, + type: Option<ClientKillType>, + ids: Option<Vec<String>>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT LIST command returns information and statistics about the client connections server in a mostly human +readable format. Read more
source§

fn client_getname<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT GETNAME returns the name of the current connection as set by CLIENT SETNAME. Read more
source§

fn client_setname<S>(&self, name: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Assign a name to the current connection. Read more
source§

fn client_pause( + &self, + timeout: i64, + mode: Option<ClientPauseKind>, +) -> impl Future<Output = RedisResult<()>>

CLIENT PAUSE is a connections control command able to suspend all the Redis clients for the specified amount of +time (in milliseconds). Read more
source§

fn client_unpause(&self) -> impl Future<Output = RedisResult<()>>

CLIENT UNPAUSE is used to resume command processing for all clients that were paused by CLIENT PAUSE. Read more
source§

fn client_reply( + &self, + flag: ClientReplyFlag, +) -> impl Future<Output = RedisResult<()>>

The CLIENT REPLY command controls whether the server will reply the client’s commands. The following modes are +available: Read more
source§

fn client_unblock<R, S>( + &self, + id: S, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisValue>,

This command can unblock, from a different connection, a client blocked in a blocking operation, such as for +instance BRPOP or XREAD or WAIT. Read more
source§

fn unblock_self( + &self, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<()>>

A convenience function to unblock any blocked connection on this client.
source§

fn client_tracking<R, T, P>( + &self, + toggle: T, + redirect: Option<i64>, + prefixes: P, + bcast: bool, + optin: bool, + optout: bool, + noloop: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<Toggle>, + T::Error: Into<RedisError>, + P: Into<MultipleStrings>,

Available on crate feature i-tracking only.
This command enables the tracking feature of the Redis server that is used for server assisted client side +caching. Read more
source§

fn client_trackinginfo<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
The command returns information about the current client connection’s use of the server assisted client side +caching feature. Read more
source§

fn client_getredir<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command returns the client ID we are redirecting our tracking notifications to. Read more
source§

fn client_caching<R>( + &self, + enabled: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command controls the tracking of the keys in the next command executed by the connection, when tracking is +enabled in OPTIN or OPTOUT mode. Read more
source§

impl ClientLike for Transaction

source§

fn id(&self) -> &str

The unique ID identifying this client and underlying connections.
source§

fn client_config(&self) -> RedisConfig

Read the config used to initialize the client.
source§

fn client_reconnect_policy(&self) -> Option<ReconnectPolicy>

Read the reconnect policy used to initialize the client.
source§

fn connection_config(&self) -> &ConnectionConfig

Read the connection config used to initialize the client.
source§

fn protocol_version(&self) -> RespVersion

Read the RESP version used by the client when communicating with the server.
source§

fn has_reconnect_policy(&self) -> bool

Whether the client has a reconnection policy.
source§

fn is_pipelined(&self) -> bool

Whether the client will automatically pipeline commands.
source§

fn is_clustered(&self) -> bool

Whether the client is connected to a cluster.
source§

fn uses_sentinels(&self) -> bool

Whether the client uses the sentinel interface.
source§

fn update_perf_config(&self, config: PerformanceConfig)

Update the internal PerformanceConfig in place with new values.
source§

fn perf_config(&self) -> PerformanceConfig

Read the PerformanceConfig associated with this client.
source§

fn state(&self) -> ClientState

Read the state of the underlying connection(s). Read more
source§

fn is_connected(&self) -> bool

Whether all underlying connections are healthy.
source§

fn active_connections( + &self, +) -> impl Future<Output = Result<Vec<Server>, RedisError>>

Read the set of active connections managed by the client.
source§

fn server_version(&self) -> Option<Version>

Read the server version, if known.
source§

fn set_resolver(&self, resolver: Rc<dyn Resolve>) -> impl Future

Available on crate feature dns only.
Override the DNS resolution logic for the client.
source§

fn connect(&self) -> ConnectHandle

Connect to the server. Read more
source§

fn force_reconnection(&self) -> impl Future<Output = RedisResult<()>>

Force a reconnection to the server(s). Read more
source§

fn wait_for_connect(&self) -> impl Future<Output = RedisResult<()>>

Wait for the result of the next connection attempt. Read more
source§

fn init(&self) -> impl Future<Output = RedisResult<ConnectHandle>>

Initialize a new routing and connection task and wait for it to connect successfully. Read more
source§

fn quit(&self) -> impl Future<Output = RedisResult<()>>

Close the connection to the Redis server. The returned future resolves when the command has been written to the +socket, not when the connection has been fully closed. Some time after this future resolves the future +returned by connect will resolve which indicates that the connection has been fully closed. Read more
source§

fn shutdown( + &self, + flags: Option<ShutdownFlags>, +) -> impl Future<Output = RedisResult<()>>

Available on crate feature i-server only.
Shut down the server and quit the client. Read more
source§

fn flushall<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Delete the keys in all databases. Read more
source§

fn flushall_cluster(&self) -> impl Future<Output = RedisResult<()>>

Delete the keys on all nodes in the cluster. This is a special function that does not map directly to the Redis +interface.
source§

fn ping<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Ping the Redis server. Read more
source§

fn info<R>( + &self, + section: Option<InfoKind>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read info about the server. Read more
source§

fn custom<R, T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command that is not yet supported via another interface on this client. This is most useful when +interacting with third party modules or extensions. Read more
source§

fn custom_raw<T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<Resp3Frame>>
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command similar to custom, but return the response frame directly without any +parsing. Read more
source§

fn with_options(&self, options: &Options) -> WithOptions<Self>

Customize various configuration options on commands.
source§

impl Clone for Transaction

source§

fn clone(&self) -> Transaction

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl ConfigInterface for Transaction

Available on crate feature i-config only.
source§

fn config_resetstat(&self) -> impl Future<Output = RedisResult<()>>

Resets the statistics reported by Redis using the INFO command. Read more
source§

fn config_rewrite(&self) -> impl Future<Output = RedisResult<()>>

The CONFIG REWRITE command rewrites the redis.conf file the server was started with, applying the minimal +changes needed to make it reflect the configuration currently used by the server, which may be different +compared to the original one because of the use of the CONFIG SET command. Read more
source§

fn config_get<R, S>(&self, parameter: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The CONFIG GET command is used to read the configuration parameters of a running Redis server. Read more
source§

fn config_set<P, V>( + &self, + parameter: P, + value: V, +) -> impl Future<Output = RedisResult<()>>
where + P: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The CONFIG SET command is used in order to reconfigure the server at run time without the need to restart Redis. Read more
source§

impl Debug for Transaction

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl FunctionInterface for Transaction

Available on crate feature i-scripts only.
source§

fn fcall<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Invoke a function. Read more
source§

fn fcall_ro<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

This is a read-only variant of the FCALL command that cannot execute commands that modify data. Read more
source§

fn function_delete<R, S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Delete a library and all its functions. Read more
source§

fn function_delete_cluster<S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Delete a library and all its functions from each cluster node concurrently. Read more
source§

fn function_dump<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the serialized payload of loaded libraries. Read more
source§

fn function_flush<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Deletes all the libraries. Read more
source§

fn function_flush_cluster( + &self, + async: bool, +) -> impl Future<Output = RedisResult<()>>

Deletes all the libraries on all cluster nodes concurrently. Read more
source§

fn function_kill<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Kill a function that is currently executing. Read more
source§

fn function_list<R, S>( + &self, + library_name: Option<S>, + withcode: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Return information about the functions and libraries. Read more
source§

fn function_load<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis. Read more
source§

fn function_load_cluster<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis on all cluster nodes concurrently. Read more
source§

fn function_restore<R, B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload. Read more
source§

fn function_restore_cluster<B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<()>>
where + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload on all cluster nodes concurrently. Read more
source§

fn function_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return information about the function that’s currently running and information about the available execution +engines. Read more
source§

impl GeoInterface for Transaction

Available on crate feature i-geo only.
source§

fn geoadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + changed: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: Into<MultipleGeoValues>,

Adds the specified geospatial items (longitude, latitude, name) to the specified key. Read more
source§

fn geohash<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return valid Geohash strings representing the position of one or more elements in a sorted set value +representing a geospatial index (where elements were added using GEOADD). Read more
source§

fn geopos<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by +the sorted set at key. Read more
source§

fn geodist<R, K, S, D>( + &self, + key: K, + src: S, + dest: D, + unit: Option<GeoUnit>, +) -> impl Future<Output = RedisResult<R>>

Return the distance between two members in the geospatial index represented by the sorted set. Read more
source§

fn georadius<R, K, P>( + &self, + key: K, + position: P, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<GeoPosition>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified with the center location and the maximum distance from the center (the radius). Read more
source§

fn georadiusbymember<R, K, V>( + &self, + key: K, + member: V, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

This command is exactly like GEORADIUS with the sole difference that instead of taking, as the center of the +area to query, a longitude and latitude value, it takes the name of a member already existing inside the +geospatial index represented by the sorted set. Read more
source§

fn geosearch<R, K>( + &self, + key: K, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + withcoord: bool, + withdist: bool, + withhash: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified by a given shape. Read more
source§

fn geosearchstore<R, D, S>( + &self, + dest: D, + source: S, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + storedist: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>,

This command is like GEOSEARCH, but stores the result in destination key. Returns the number of members added to +the destination key. Read more
source§

impl HashesInterface for Transaction

Available on crate feature i-hashes only.
source§

fn hgetall<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all fields and values of the hash stored at key. Read more
source§

fn hdel<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Removes the specified fields from the hash stored at key. Read more
source§

fn hexists<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns if field is an existing field in the hash stored at key. Read more
source§

fn hget<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the value associated with field in the hash stored at key. Read more
source§

fn hincrby<R, K, F>( + &self, + key: K, + field: F, + increment: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increments the number stored at field in the hash stored at key by increment. Read more
source§

fn hincrbyfloat<R, K, F>( + &self, + key: K, + field: F, + increment: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increment the specified field of a hash stored at key, and representing a floating point number, by the +specified increment. Read more
source§

fn hkeys<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all field names in the hash stored at key. Read more
source§

fn hlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of fields contained in the hash stored at key. Read more
source§

fn hmget<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Returns the values associated with the specified fields in the hash stored at key. Read more
source§

fn hmset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the specified fields to their respective values in the hash stored at key. Read more
source§

fn hset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets fields in the hash stored at key to their provided values. Read more
source§

fn hsetnx<R, K, F, V>( + &self, + key: K, + field: F, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets field in the hash stored at key to value, only if field does not yet exist. Read more
source§

fn hrandfield<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random field from the hash value stored at key. Read more
source§

fn hstrlen<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the string length of the value associated with field in the hash stored at key. Read more
source§

fn hvals<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all values in the hash stored at key. Read more
source§

impl HyperloglogInterface for Transaction

Available on crate feature i-hyperloglog only.
source§

fn pfadd<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Adds all the element arguments to the HyperLogLog data structure stored at the variable name specified as first +argument. Read more
source§

fn pfcount<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

When called with a single key, returns the approximated cardinality computed by the HyperLogLog data structure +stored at the specified variable, which is 0 if the variable does not exist. Read more
source§

fn pfmerge<R, D, S>( + &self, + dest: D, + sources: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<MultipleKeys>,

Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of the +observed sets of the source HyperLogLog structures. Read more
source§

impl KeysInterface for Transaction

Available on crate feature i-keys only.
source§

fn watch<K>(&self, keys: K) -> impl Future<Output = RedisResult<()>>
where + K: Into<MultipleKeys>,

Marks the given keys to be watched for conditional execution of a transaction. Read more
source§

fn unwatch(&self) -> impl Future<Output = RedisResult<()>>

Flushes all the previously watched keys for a transaction. Read more
source§

fn randomkey<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return a random key from the currently selected database. Read more
source§

fn copy<R, S, D>( + &self, + source: S, + destination: D, + db: Option<u8>, + replace: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

This command copies the value stored at the source key to the destination key. Read more
source§

fn dump<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Serialize the value stored at key in a Redis-specific format and return it as bulk string. Read more
source§

fn restore<R, K>( + &self, + key: K, + ttl: i64, + serialized: RedisValue, + replace: bool, + absttl: bool, + idletime: Option<i64>, + frequency: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Create a key associated with a value that is obtained by deserializing the provided serialized value Read more
source§

fn set<R, K, V>( + &self, + key: K, + value: V, + expire: Option<Expiration>, + options: Option<SetOptions>, + get: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set a value with optional NX|XX, EX|PX|EXAT|PXAT|KEEPTTL, and GET arguments. Read more
source§

fn get<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Read a value from the server. Read more
source§

fn getrange<R, K>( + &self, + key: K, + start: usize, + end: usize, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the substring of the string value stored at key with offsets start and end (both inclusive). Read more
source§

fn setrange<R, K, V>( + &self, + key: K, + offset: u32, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Overwrites part of the string stored at key, starting at the specified offset, for the entire length of +value. Read more
source§

fn getset<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Atomically sets key to value and returns the old value stored at key. Read more
source§

fn getdel<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the value of key and delete the key. This command is similar to GET, except for the fact that it also +deletes the key on success (if and only if the key’s value type is a string). Read more
source§

fn strlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the string value stored at key. An error is returned when key holds a non-string value. Read more
source§

fn del<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Removes the specified keys. A key is ignored if it does not exist. Read more
Unlinks the specified keys. A key is ignored if it does not exist Read more
source§

fn rename<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination. Read more
source§

fn renamenx<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination if destination does not yet exist. Read more
source§

fn append<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Append value to key if it’s a string. Read more
source§

fn mget<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the +special value nil is returned. Read more
source§

fn mset<V>(&self, values: V) -> impl Future<Output = RedisResult<()>>
where + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. Read more
source§

fn msetnx<R, V>(&self, values: V) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. MSETNX will not perform any operation at all even if just a +single key already exists. Read more
source§

fn incr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by_float<R, K>( + &self, + key: K, + val: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increment the string representing a floating point number stored at key by val. If the key does not exist, it +is set to 0 before performing the operation. Read more
source§

fn decr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn decr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn ttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in seconds. Read more
source§

fn pttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in milliseconds. Read more
source§

fn persist<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Remove the existing timeout on a key, turning the key from volatile (a key with an expiration) +to persistent (a key that will never expire as no timeout is associated). Read more
source§

fn expire<R, K>( + &self, + key: K, + seconds: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on key. After the timeout has expired, the key will be automatically deleted. Read more
source§

fn expire_at<R, K>( + &self, + key: K, + timestamp: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on a key based on a UNIX timestamp. Read more
source§

fn pexpire<R, K>( + &self, + key: K, + milliseconds: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command works exactly like EXPIRE but the time to live of the key is specified in milliseconds instead of +seconds. Read more
source§

fn pexpire_at<R, K>( + &self, + key: K, + timestamp: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

PEXPIREAT has the same effect and semantic as EXPIREAT, but the Unix time at which the key will expire is +specified in milliseconds instead of seconds. Read more
source§

fn exists<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns number of keys that exist from the keys arguments. Read more
source§

fn lcs<R, K1, K2>( + &self, + key1: K1, + key2: K2, + len: bool, + idx: bool, + minmatchlen: Option<i64>, + withmatchlen: bool, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis, + K1: Into<RedisKey>, + K2: Into<RedisKey>,

Runs the longest common subsequence algorithm on two keys. Read more
source§

impl ListInterface for Transaction

Available on crate feature i-lists only.
source§

fn blmpop<R, K>( + &self, + timeout: f64, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::lmpop. Read more
source§

fn blpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BLPOP is a blocking list pop primitive. It is the blocking version of LPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the head of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BRPOP is a blocking list pop primitive. It is the blocking version of RPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the tail of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpoplpush<R, S, D>( + &self, + source: S, + destination: D, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::rpoplpush. Read more
source§

fn blmove<R, S, D>( + &self, + source: S, + destination: D, + source_direction: LMoveDirection, + destination_direction: LMoveDirection, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::lmove. Read more
source§

fn lmpop<R, K>( + &self, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements from the first non-empty list key from the list of provided key names. Read more
source§

fn lindex<R, K>( + &self, + key: K, + index: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the element at index in the list stored at key. Read more
source§

fn linsert<R, K, P, V>( + &self, + key: K, + location: ListLocation, + pivot: P, + element: V, +) -> impl Future<Output = RedisResult<R>>

Inserts element in the list stored at key either before or after the reference value pivot. Read more
source§

fn llen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the list stored at key. Read more
source§

fn lpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the first elements of the list stored at key. Read more
source§

fn lpos<R, K, V>( + &self, + key: K, + element: V, + rank: Option<i64>, + count: Option<i64>, + maxlen: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The command returns the index of matching elements inside a Redis list. Read more
source§

fn lpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the head of the list stored at key. Read more
source§

fn lpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the head of the list stored at key, only if key already exists and holds a list. Read more
source§

fn lrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified elements of the list stored at key. Read more
source§

fn lrem<R, K, V>( + &self, + key: K, + count: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Removes the first count occurrences of elements equal to element from the list stored at key. Read more
source§

fn lset<R, K, V>( + &self, + key: K, + index: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets the list element at index to element. Read more
source§

fn ltrim<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Trim an existing list so that it will contain only the specified range of elements specified. Read more
source§

fn rpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the last elements of the list stored at key. Read more
source§

fn rpoplpush<R, S, D>( + &self, + source: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the last element (tail) of the list stored at source, and pushes the element at +the first element (head) of the list stored at destination. Read more
source§

fn lmove<R, S, D>( + &self, + source: S, + dest: D, + source_direction: LMoveDirection, + dest_direction: LMoveDirection, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the first/last element (head/tail depending on the source direction argument) of +the list stored at source, and pushes the element at the first/last element (head/tail depending on the +destination direction argument) of the list stored at destination. Read more
source§

fn rpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the tail of the list stored at key. Read more
source§

fn rpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the tail of the list stored at key, only if key already exists and holds a list. Read more
source§

fn sort<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, + store: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Returns or stores the elements contained in the list, set or sorted set at key. Read more
source§

fn sort_ro<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Read-only variant of the SORT command. It is exactly like the original SORT but refuses the STORE option and can +safely be used in read-only replicas. Read more
source§

impl MemoryInterface for Transaction

Available on crate feature i-memory only.
source§

fn memory_doctor<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY DOCTOR command reports about different memory-related issues that the Redis server experiences, and +advises about possible remedies. Read more
source§

fn memory_malloc_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY MALLOC-STATS command provides an internal statistics report from the memory allocator. Read more
source§

fn memory_purge(&self) -> impl Future<Output = RedisResult<()>>

The MEMORY PURGE command attempts to purge dirty pages so these can be reclaimed by the allocator. Read more
source§

fn memory_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY STATS command returns an Array reply about the memory usage of the server. Read more
source§

fn memory_usage<R, K>( + &self, + key: K, + samples: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

The MEMORY USAGE command reports the number of bytes that a key and its value require to be stored in RAM. Read more
source§

impl PartialEq for Transaction

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl PubsubInterface for Transaction

Available on crate feature i-pubsub only.
source§

fn subscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribe to a channel on the publish-subscribe interface. Read more
source§

fn unsubscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribe from a channel on the PubSub interface. Read more
source§

fn psubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribes the client to the given patterns. Read more
source§

fn punsubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribes the client from the given patterns, or from all of them if none is given. Read more
source§

fn publish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Publish a message on the PubSub interface, returning the number of clients that received the message. Read more
source§

fn ssubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Subscribes the client to the specified shard channels. Read more
source§

fn sunsubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Unsubscribes the client from the given shard channels, or from all of them if none is given. Read more
source§

fn spublish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Posts a message to the given shard channel. Read more
source§

fn pubsub_channels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active channels. Read more
source§

fn pubsub_numpat<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of unique patterns that are subscribed to by clients. Read more
source§

fn pubsub_numsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers (exclusive of clients subscribed to patterns) for the specified channels. Read more
source§

fn pubsub_shardchannels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active shard channels. Read more
source§

fn pubsub_shardnumsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers for the specified shard channels. Read more
source§

impl RediSearchInterface for Transaction

Available on crate feature i-redisearch only.
source§

fn ft_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns a list of all existing indexes. Read more
source§

fn ft_aggregate<R, I, Q>( + &self, + index: I, + query: Q, + options: FtAggregateOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Run a search query on an index, and perform aggregate transformations on the results. Read more
Search the index with a textual query, returning either documents or just ids. Read more
source§

fn ft_create<R, I>( + &self, + index: I, + options: FtCreateOptions, + schema: Vec<SearchSchema>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Create an index with the given specification. Read more
source§

fn ft_alter<R, I>( + &self, + index: I, + options: FtAlterOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Add a new attribute to the index. Read more
source§

fn ft_aliasadd<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. Read more
source§

fn ft_aliasdel<R, A>(&self, alias: A) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>,

Remove an alias from an index. Read more
source§

fn ft_aliasupdate<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. If the alias is already associated with another index, FT.ALIASUPDATE removes the +alias association with the previous index. Read more
source§

fn ft_config_get<R, S>(&self, option: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Retrieve configuration options. Read more
source§

fn ft_config_set<R, S, V>( + &self, + option: S, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set the value of a RediSearch configuration parameter. Read more
source§

fn ft_cursor_del<R, I, C>( + &self, + index: I, + cursor: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Delete a cursor. Read more
source§

fn ft_cursor_read<R, I, C>( + &self, + index: I, + cursor: C, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Read next results from an existing cursor. Read more
source§

fn ft_dictadd<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Add terms to a dictionary. Read more
source§

fn ft_dictdel<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Remove terms from a dictionary. Read more
source§

fn ft_dictdump<R, D>(&self, dict: D) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>,

Dump all terms in the given dictionary. Read more
source§

fn ft_dropindex<R, I>( + &self, + index: I, + dd: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Delete an index. Read more
source§

fn ft_explain<R, I, Q>( + &self, + index: I, + query: Q, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Return the execution plan for a complex query. Read more
source§

fn ft_info<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Return information and statistics on the index. Read more
source§

fn ft_spellcheck<R, I, Q>( + &self, + index: I, + query: Q, + distance: Option<u8>, + terms: Option<SpellcheckTerms>, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Perform spelling correction on a query, returning suggestions for misspelled terms. Read more
source§

fn ft_sugadd<R, K, S>( + &self, + key: K, + string: S, + score: f64, + incr: bool, + payload: Option<Bytes>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Add a suggestion string to an auto-complete suggestion dictionary. Read more
source§

fn ft_sugdel<R, K, S>( + &self, + key: K, + string: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Delete a string from a suggestion index. Read more
source§

fn ft_sugget<R, K, P>( + &self, + key: K, + prefix: P, + fuzzy: bool, + withscores: bool, + withpayloads: bool, + max: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Get completion suggestions for a prefix. Read more
source§

fn ft_suglen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the size of an auto-complete suggestion dictionary. Read more
source§

fn ft_syndump<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Dump the contents of a synonym group. Read more
source§

fn ft_synupdate<R, I, S, T>( + &self, + index: I, + synonym_group_id: S, + skipinitialscan: bool, + terms: T, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + S: Into<Str>, + T: Into<MultipleStrings>,

Update a synonym group. Read more
source§

fn ft_tagvals<R, I, F>( + &self, + index: I, + field_name: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + F: Into<Str>,

Return a distinct set of values indexed in a Tag field. Read more
source§

impl RedisJsonInterface for Transaction

Available on crate feature i-redis-json only.
source§

fn json_arrappend<R, K, P, V>( + &self, + key: K, + path: P, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json values into the array at path after the last element in it. Read more
source§

fn json_arrindex<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + start: Option<i64>, + stop: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Search for the first occurrence of a JSON value in an array. Read more
source§

fn json_arrinsert<R, K, P, V>( + &self, + key: K, + path: P, + index: i64, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Insert the json values into the array at path before the index (shifts to the right). Read more
source§

fn json_arrlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON array at path in key. Read more
source§

fn json_arrpop<R, K, P>( + &self, + key: K, + path: Option<P>, + index: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Remove and return an element from the index in the array Read more
source§

fn json_arrtrim<R, K, P>( + &self, + key: K, + path: P, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Trim an array so that it contains only the specified inclusive range of elements Read more
source§

fn json_clear<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Clear container values (arrays/objects) and set numeric values to 0 Read more
source§

fn json_debug_memory<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report a value’s memory usage in bytes Read more
source§

fn json_del<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Delete a value. Read more
source§

fn json_get<R, K, I, N, S, P>( + &self, + key: K, + indent: Option<I>, + newline: Option<N>, + space: Option<S>, + paths: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<Str>, + N: Into<Str>, + S: Into<Str>, + P: Into<MultipleStrings>,

Return the value at path in JSON serialized form. Read more
source§

fn json_merge<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Merge a given JSON value into matching paths. Read more
source§

fn json_mget<R, K, P>( + &self, + keys: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + P: Into<Str>,

Return the values at path from multiple key arguments. Read more
source§

fn json_mset<R, K, P, V>( + &self, + values: Vec<(K, P, V)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set or update one or more JSON values according to the specified key-path-value triplets. Read more
source§

fn json_numincrby<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Increment the number value stored at path by number Read more
source§

fn json_objkeys<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the keys in the object that’s referenced by path. Read more
source§

fn json_objlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the number of keys in the JSON object at path in key. Read more
source§

fn json_resp<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the JSON in key in Redis serialization protocol specification form. Read more
source§

fn json_set<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + options: Option<SetOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set the JSON value at path in key. Read more
source§

fn json_strappend<R, K, P, V>( + &self, + key: K, + path: Option<P>, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json-string values to the string at path. Read more
source§

fn json_strlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON String at path in key. Read more
source§

fn json_toggle<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Toggle a Boolean value stored at path. Read more
source§

fn json_type<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the type of JSON value at path. Read more
source§

impl ServerInterface for Transaction

Available on crate feature i-server only.
source§

fn bgrewriteaof<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Instruct Redis to start an Append Only File rewrite process. Read more
source§

fn bgsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Save the DB in background. Read more
source§

fn dbsize<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the number of keys in the selected database. Read more
source§

fn select(&self, db: u8) -> impl Future<Output = RedisResult<()>>

Select the database this client should use. Read more
source§

fn failover( + &self, + to: Option<(String, u16)>, + force: bool, + abort: bool, + timeout: Option<u32>, +) -> impl Future<Output = RedisResult<()>>

This command will start a coordinated failover between the currently-connected-to master and one of its +replicas. Read more
source§

fn lastsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the UNIX TIME of the last DB save executed with success. Read more
source§

fn wait<R>( + &self, + numreplicas: i64, + timeout: i64, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis,

This command blocks the current client until all the previous write commands are successfully transferred and +acknowledged by at least the specified number of replicas. If the timeout, specified in milliseconds, is +reached, the command returns even if the specified number of replicas were not yet reached. Read more
source§

fn sentinel_primary(&self) -> Option<Server>

Read the primary Redis server identifier returned from the sentinel nodes.
source§

fn sentinel_nodes(&self) -> Option<Vec<Server>>

Read the set of known sentinel nodes.
source§

impl SetsInterface for Transaction

Available on crate feature i-sets only.
source§

fn sadd<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Add the specified members to the set stored at key. Read more
source§

fn scard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the set cardinality (number of elements) of the set stored at key. Read more
source§

fn sdiff<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the difference between the first set and all the successive sets. Read more
source§

fn sdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SDIFF, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sinter<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the intersection of all the given sets. Read more
source§

fn sinterstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SINTER, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sismember<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns if member is a member of the set stored at key. Read more
source§

fn smismember<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns whether each member is a member of the set stored at key. Read more
source§

fn smembers<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all the members of the set value stored at key. Read more
source§

fn smove<R, S, D, V>( + &self, + source: S, + dest: D, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Move member from the set at source to the set at destination. Read more
source§

fn spop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns one or more random members from the set value store at key. Read more
source§

fn srandmember<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the set value stored at key. Read more
source§

fn srem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Remove the specified members from the set stored at key. Read more
source§

fn sunion<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the union of all the given sets. Read more
source§

fn sunionstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SUNION, but instead of returning the resulting set, it is stored in destination. Read more
source§

impl SortedSetsInterface for Transaction

Available on crate feature i-sorted-sets only.
source§

fn bzmpop<R, K>( + &self, + timeout: f64, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zmpop. Read more
source§

fn bzpopmin<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmin. Read more
source§

fn bzpopmax<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmax. Read more
source§

fn zadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + ordering: Option<Ordering>, + changed: bool, + incr: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>

Adds all the specified members with the specified scores to the sorted set stored at key. Read more
source§

fn zcard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the sorted set cardinality (number of elements) of the sorted set stored at key. Read more
source§

fn zcount<R, K>( + &self, + key: K, + min: f64, + max: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of elements in the sorted set at key with a score between min and max. Read more
source§

fn zdiff<R, K>( + &self, + keys: K, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

This command is similar to ZDIFFSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

Computes the difference between the first and all successive input sorted sets and stores the result in +destination. Read more
source§

fn zincrby<R, K, V>( + &self, + key: K, + increment: f64, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Increments the score of member in the sorted set stored at key by increment. Read more
source§

fn zinter<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZINTERSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zinterstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the intersection of the sorted sets given by the specified keys, and stores the result in +destination. Read more
source§

fn zlexcount<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns the number of elements in the sorted set at key with a value between min and +max. Read more
source§

fn zpopmax<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the highest scores in the sorted set stored at key. Read more
source§

fn zpopmin<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the lowest scores in the sorted set stored at key. Read more
source§

fn zmpop<R, K>( + &self, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements, that are member-score pairs, from the first non-empty sorted set in the provided list +of key names. Read more
source§

fn zrandmember<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the sorted set value stored at key. Read more
source§

fn zrangestore<R, D, S, M, N>( + &self, + dest: D, + source: S, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

This command is like ZRANGE, but stores the result in the destination key. Read more
source§

fn zrange<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between min and max. Read more
source§

fn zrevrangebylex<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between max and min. Read more
source§

fn zrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between min and max (including elements +with score equal to min or max). Read more
source§

fn zrevrangebyscore<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between max and min (including +elements with score equal to max or min). Read more
source§

fn zrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from low to high. Read more
source§

fn zrem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Removes the specified members from the sorted set stored at key. Non existing members are ignored. Read more
source§

fn zremrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command removes all elements in the sorted set stored at key between the lexicographical range +specified by min and max. Read more
source§

fn zremrangebyrank<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes all elements in the sorted set stored at key with rank between start and stop. Read more
source§

fn zremrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Removes all elements in the sorted set stored at key with a score between min and max. Read more
source§

fn zrevrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrevrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from high to low. Read more
source§

fn zscore<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the score of member in the sorted set at key. Read more
source§

fn zunion<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZUNIONSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zunionstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the union of the sorted sets given by the specified keys, and stores the result in destination. Read more
source§

fn zmscore<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns the scores associated with the specified members in the sorted set stored at key. Read more
source§

impl StreamsInterface for Transaction

Available on crate feature i-streams only.
source§

fn xinfo_consumers<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

This command returns the list of consumers that belong to the groupname consumer group of the stream stored at +key. Read more
source§

fn xinfo_groups<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns the list of all consumers groups of the stream stored at key. Read more
source§

fn xinfo_stream<R, K>( + &self, + key: K, + full: bool, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns information about the stream stored at key. Read more
source§

fn xadd<R, K, C, I, F>( + &self, + key: K, + nomkstream: bool, + cap: C, + id: I, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<XID>, + F: TryInto<MultipleOrderedPairs>, + F::Error: Into<RedisError>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Appends the specified stream entry to the stream at the specified key. If the key does not exist, as a side +effect of running this command the key is created with a stream value. The creation of stream’s key can be +disabled with the NOMKSTREAM option. Read more
source§

fn xtrim<R, K, C>(&self, key: K, cap: C) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Trims the stream by evicting older entries (entries with lower IDs) if needed. Read more
source§

fn xdel<R, K, S>(&self, key: K, ids: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Removes the specified entries from a stream, and returns the number of entries deleted. Read more
source§

fn xrange_values<Ri, Rk, Rv, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Return the stream entries matching the provided range of IDs, automatically converting to a less verbose type +definition. Read more
source§

fn xrange<R, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

The command returns the stream entries matching a given range of IDs. The range is specified by a minimum +and maximum ID. All the entries having an ID between the two specified or exactly one of the two IDs specified +(closed interval) are returned. Read more
source§

fn xrevrange_values<Ri, Rk, Rv, K, E, S>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Similar to XRANGE, but with the results returned in reverse order. The results will be automatically converted +to a less verbose type definition. Read more
source§

fn xrevrange<R, K, S, E>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

Similar to XRANGE, but with the results returned in reverse order. Read more
source§

fn xlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of entries inside a stream. Read more
source§

fn xread_map<Rk1, Rk2, Rk3, Rv, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xread<R, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xgroup_create<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, + mkstream: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

This command creates a new consumer group uniquely identified by groupname for the stream stored at key. Read more
source§

fn xgroup_createconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Create a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_delconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Delete a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_destroy<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Completely destroy a consumer group. Read more
source§

fn xgroup_setid<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

Set the last delivered ID for a consumer group. Read more
source§

fn xreadgroup_map<Rk1, Rk2, Rk3, Rv, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xreadgroup<R, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xack<R, K, G, I>( + &self, + key: K, + group: G, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + I: Into<MultipleIDs>,

Remove one or more messages from the Pending Entries List (PEL) of a stream consumer group. Read more
source§

fn xclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

A variation of xclaim with a less verbose return type.
source§

fn xclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

In the context of a stream consumer group, this command changes the ownership of a pending message, +so that the new owner is the consumer specified as the command argument. Read more
source§

fn xautoclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<(String, Vec<XReadValue<Ri, Rk, Rv>>)>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. It also converts +the response type to a less verbose type declaration and handles potential differences between RESP2 and RESP3. Read more
source§

fn xautoclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. Read more
source§

fn xpending<R, K, G, A>( + &self, + key: K, + group: G, + args: A, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + A: Into<XPendingArgs>,

Inspect the list of pending messages in a consumer group. Read more
source§

impl TimeSeriesInterface for Transaction

Available on crate feature i-time-series only.
source§

fn ts_add<R, K, T, L>( + &self, + key: K, + timestamp: T, + value: f64, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + on_duplicate: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + T: TryInto<Timestamp>, + T::Error: Into<RedisError>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Append a sample to a time series. Read more
source§

fn ts_alter<R, K, L>( + &self, + key: K, + retention: Option<u64>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Update the retention, chunk size, duplicate policy, and labels of an existing time series. Read more
source§

fn ts_create<R, K, L>( + &self, + key: K, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Create a new time series. Read more
source§

fn ts_createrule<R, S, D>( + &self, + src: S, + dest: D, + aggregation: (Aggregator, u64), + align_timestamp: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Create a compaction rule. Read more
source§

fn ts_decrby<R, K, L>( + &self, + key: K, + subtrahend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given decrement. Read more
source§

fn ts_del<R, K>( + &self, + key: K, + from: i64, + to: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Delete all samples between two timestamps for a given time series. Read more
source§

fn ts_deleterule<R, S, D>( + &self, + src: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Delete a compaction rule. Read more
source§

fn ts_get<R, K>( + &self, + key: K, + latest: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the sample with the highest timestamp from a given time series. Read more
source§

fn ts_incrby<R, K, L>( + &self, + key: K, + addend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given increment. Read more
source§

fn ts_info<R, K>( + &self, + key: K, + debug: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return information and statistics for a time series. Read more
source§

fn ts_madd<R, K, I>(&self, samples: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: IntoIterator<Item = (K, Timestamp, f64)>,

Append new samples to one or more time series. Read more
source§

fn ts_mget<R, L, S, I>( + &self, + latest: bool, + labels: Option<L>, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + L: Into<GetLabels>, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get the sample with the highest timestamp from each time series matching a specific filter. Read more
source§

fn ts_mrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the forward direction. Read more
source§

fn ts_mrevrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the reverse direction. Read more
source§

fn ts_queryindex<R, S, I>( + &self, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get all time series keys matching a filter list. Read more
source§

fn ts_range<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in forward direction. Read more
source§

fn ts_revrange<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in reverse direction. Read more
source§

impl Eq for Transaction

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/clients/struct.WithOptions.html b/doc/fred/clients/struct.WithOptions.html new file mode 100644 index 00000000..d4f5d1e6 --- /dev/null +++ b/doc/fred/clients/struct.WithOptions.html @@ -0,0 +1,2226 @@ +WithOptions in fred::clients - Rust

Struct fred::clients::WithOptions

source ·
pub struct WithOptions<C: ClientLike> { /* private fields */ }
Expand description

A client interface used to customize command configuration options.

+

See Options for more information.

+ +
async fn example() -> Result<(), RedisError> {
+  let client = RedisClient::default();
+  client.init().await?;
+
+  let options = Options {
+    max_redirections: Some(3),
+    max_attempts: Some(1),
+    timeout: Some(Duration::from_secs(10)),
+    ..Default::default()
+  };
+  let foo: Option<String> = client.with_options(&options).get("foo").await?;
+
+  // reuse the options bindings
+  let with_options = client.with_options(&options);
+  let foo: () = with_options.get("foo").await?;
+  let bar: () = with_options.get("bar").await?;
+
+  // combine with other client types
+  let pipeline = client.pipeline().with_options(&options);
+  let _: () = pipeline.get("foo").await?;
+  let _: () = pipeline.get("bar").await?;
+  // custom options will be applied to each command
+  println!("results: {:?}", pipeline.all::<i64>().await?);
+
+  Ok(())
+}
+

Implementations§

source§

impl<C: ClientLike> WithOptions<C>

source

pub fn options(&self) -> &Options

Read the options that will be applied to commands.

+

Trait Implementations§

source§

impl<C: AclInterface> AclInterface for WithOptions<C>

Available on crate feature i-acl only.
source§

fn acl_setuser<S, V>( + &self, + username: S, + rules: V, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Create an ACL user with the specified rules or modify the rules of an existing user. Read more
source§

fn acl_load(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will reload +the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. Read more
source§

fn acl_save(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will save the +currently defined ACLs from the server memory to the ACL file. Read more
source§

fn acl_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the currently active ACL rules in the Redis server. Read more
source§

fn acl_users<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows a list of all the usernames of the currently configured users in the Redis ACL system. Read more
source§

fn acl_getuser<R, S>(&self, username: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns all the rules defined for an existing ACL user. Read more
source§

fn acl_deluser<R, S>( + &self, + usernames: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Delete all the specified ACL users and terminate all the connections that are authenticated with such users. Read more
source§

fn acl_cat<R>( + &self, + category: Option<Str>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the available ACL categories if called without arguments. If a category name is given, +the command shows all the Redis commands in the specified category. Read more
source§

fn acl_genpass<R>( + &self, + bits: Option<u16>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Generate a password with length bits, returning the password. Read more
source§

fn acl_whoami<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the username the current connection is authenticated with. New connections are authenticated +with the “default” user. Read more
source§

fn acl_log_count<R>( + &self, + count: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read count recent ACL security events. Read more
source§

fn acl_log_reset(&self) -> impl Future<Output = RedisResult<()>>

Clear the ACL security events logs. Read more
source§

impl<C: AuthInterface> AuthInterface for WithOptions<C>

Available on crate feature i-server only.
source§

fn auth<S>( + &self, + username: Option<String>, + password: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Request for authentication in a password-protected Redis server. Returns ok if successful. Read more
source§

fn hello( + &self, + version: RespVersion, + auth: Option<(Str, Str)>, + setname: Option<Str>, +) -> impl Future<Output = RedisResult<()>>

Switch to a different protocol, optionally authenticating in the process. Read more
source§

impl<C: ClientInterface> ClientInterface for WithOptions<C>

Available on crate feature i-client only.
source§

fn client_id<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the ID of the current connection. Read more
source§

fn connection_ids(&self) -> impl Future<Output = HashMap<Server, i64>>

Read the connection IDs for the active connections to each server. Read more
source§

fn client_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns information and statistics about the current client connection in a mostly human readable +format. Read more
source§

fn client_kill<R>( + &self, + filters: Vec<ClientKillFilter>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Close a given connection or set of connections. Read more
source§

fn client_list<R, I>( + &self, + type: Option<ClientKillType>, + ids: Option<Vec<String>>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT LIST command returns information and statistics about the client connections server in a mostly human +readable format. Read more
source§

fn client_getname<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT GETNAME returns the name of the current connection as set by CLIENT SETNAME. Read more
source§

fn client_setname<S>(&self, name: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Assign a name to the current connection. Read more
source§

fn client_pause( + &self, + timeout: i64, + mode: Option<ClientPauseKind>, +) -> impl Future<Output = RedisResult<()>>

CLIENT PAUSE is a connections control command able to suspend all the Redis clients for the specified amount of +time (in milliseconds). Read more
source§

fn client_unpause(&self) -> impl Future<Output = RedisResult<()>>

CLIENT UNPAUSE is used to resume command processing for all clients that were paused by CLIENT PAUSE. Read more
source§

fn client_reply( + &self, + flag: ClientReplyFlag, +) -> impl Future<Output = RedisResult<()>>

The CLIENT REPLY command controls whether the server will reply the client’s commands. The following modes are +available: Read more
source§

fn client_unblock<R, S>( + &self, + id: S, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisValue>,

This command can unblock, from a different connection, a client blocked in a blocking operation, such as for +instance BRPOP or XREAD or WAIT. Read more
source§

fn unblock_self( + &self, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<()>>

A convenience function to unblock any blocked connection on this client.
source§

fn client_tracking<R, T, P>( + &self, + toggle: T, + redirect: Option<i64>, + prefixes: P, + bcast: bool, + optin: bool, + optout: bool, + noloop: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<Toggle>, + T::Error: Into<RedisError>, + P: Into<MultipleStrings>,

Available on crate feature i-tracking only.
This command enables the tracking feature of the Redis server that is used for server assisted client side +caching. Read more
source§

fn client_trackinginfo<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
The command returns information about the current client connection’s use of the server assisted client side +caching feature. Read more
source§

fn client_getredir<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command returns the client ID we are redirecting our tracking notifications to. Read more
source§

fn client_caching<R>( + &self, + enabled: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.
This command controls the tracking of the keys in the next command executed by the connection, when tracking is +enabled in OPTIN or OPTOUT mode. Read more
source§

impl<C: ClientLike> ClientLike for WithOptions<C>

source§

fn id(&self) -> &str

The unique ID identifying this client and underlying connections.
source§

fn client_config(&self) -> RedisConfig

Read the config used to initialize the client.
source§

fn client_reconnect_policy(&self) -> Option<ReconnectPolicy>

Read the reconnect policy used to initialize the client.
source§

fn connection_config(&self) -> &ConnectionConfig

Read the connection config used to initialize the client.
source§

fn protocol_version(&self) -> RespVersion

Read the RESP version used by the client when communicating with the server.
source§

fn has_reconnect_policy(&self) -> bool

Whether the client has a reconnection policy.
source§

fn is_pipelined(&self) -> bool

Whether the client will automatically pipeline commands.
source§

fn is_clustered(&self) -> bool

Whether the client is connected to a cluster.
source§

fn uses_sentinels(&self) -> bool

Whether the client uses the sentinel interface.
source§

fn update_perf_config(&self, config: PerformanceConfig)

Update the internal PerformanceConfig in place with new values.
source§

fn perf_config(&self) -> PerformanceConfig

Read the PerformanceConfig associated with this client.
source§

fn state(&self) -> ClientState

Read the state of the underlying connection(s). Read more
source§

fn is_connected(&self) -> bool

Whether all underlying connections are healthy.
source§

fn active_connections( + &self, +) -> impl Future<Output = Result<Vec<Server>, RedisError>>

Read the set of active connections managed by the client.
source§

fn server_version(&self) -> Option<Version>

Read the server version, if known.
source§

fn set_resolver(&self, resolver: Rc<dyn Resolve>) -> impl Future

Available on crate feature dns only.
Override the DNS resolution logic for the client.
source§

fn connect(&self) -> ConnectHandle

Connect to the server. Read more
source§

fn force_reconnection(&self) -> impl Future<Output = RedisResult<()>>

Force a reconnection to the server(s). Read more
source§

fn wait_for_connect(&self) -> impl Future<Output = RedisResult<()>>

Wait for the result of the next connection attempt. Read more
source§

fn init(&self) -> impl Future<Output = RedisResult<ConnectHandle>>

Initialize a new routing and connection task and wait for it to connect successfully. Read more
source§

fn quit(&self) -> impl Future<Output = RedisResult<()>>

Close the connection to the Redis server. The returned future resolves when the command has been written to the +socket, not when the connection has been fully closed. Some time after this future resolves the future +returned by connect will resolve which indicates that the connection has been fully closed. Read more
source§

fn shutdown( + &self, + flags: Option<ShutdownFlags>, +) -> impl Future<Output = RedisResult<()>>

Available on crate feature i-server only.
Shut down the server and quit the client. Read more
source§

fn flushall<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Delete the keys in all databases. Read more
source§

fn flushall_cluster(&self) -> impl Future<Output = RedisResult<()>>

Delete the keys on all nodes in the cluster. This is a special function that does not map directly to the Redis +interface.
source§

fn ping<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Ping the Redis server. Read more
source§

fn info<R>( + &self, + section: Option<InfoKind>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read info about the server. Read more
source§

fn custom<R, T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command that is not yet supported via another interface on this client. This is most useful when +interacting with third party modules or extensions. Read more
source§

fn custom_raw<T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<Resp3Frame>>
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command similar to custom, but return the response frame directly without any +parsing. Read more
source§

fn with_options(&self, options: &Options) -> WithOptions<Self>

Customize various configuration options on commands.
source§

impl<C: Clone + ClientLike> Clone for WithOptions<C>

source§

fn clone(&self) -> WithOptions<C>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<C: ClusterInterface> ClusterInterface for WithOptions<C>

Available on crate feature i-cluster only.
source§

fn cached_cluster_state(&self) -> Option<ClusterRouting>

Read the cached cluster state used for routing commands to the correct cluster nodes.
source§

fn num_primary_cluster_nodes(&self) -> usize

Read the number of known primary cluster nodes, or 0 if the cluster state is not known.
source§

fn sync_cluster(&self) -> impl Future<Output = Result<(), RedisError>>

Update the cached cluster state and add or remove any changed cluster node connections.
source§

fn cluster_bumpepoch<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Advances the cluster config epoch. Read more
source§

fn cluster_flushslots(&self) -> impl Future<Output = RedisResult<()>>

Deletes all slots from a node. Read more
source§

fn cluster_myid<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the node’s id. Read more
source§

fn cluster_nodes<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read the current cluster node configuration. Read more
source§

fn cluster_saveconfig(&self) -> impl Future<Output = RedisResult<()>>

Forces a node to save the nodes.conf configuration on disk. Read more
source§

fn cluster_slots<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER SLOTS returns details about which cluster slots map to which Redis instances. Read more
source§

fn cluster_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER INFO provides INFO style information about Redis Cluster vital parameters. Read more
source§

fn cluster_add_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

This command is useful in order to modify a node’s view of the cluster configuration. Specifically it assigns a +set of hash slots to the node receiving the command. Read more
source§

fn cluster_count_failure_reports<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns the number of failure reports for the specified node. Read more
source§

fn cluster_count_keys_in_slot<R>( + &self, + slot: u16, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of keys in the specified Redis Cluster hash slot. Read more
source§

fn cluster_del_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

The CLUSTER DELSLOTS command asks a particular Redis Cluster node to forget which master is serving the hash +slots specified as arguments. Read more
source§

fn cluster_failover( + &self, + flag: Option<ClusterFailoverFlag>, +) -> impl Future<Output = RedisResult<()>>

This command, that can only be sent to a Redis Cluster replica node, forces the replica to start a manual +failover of its master instance. Read more
source§

fn cluster_forget<S>(&self, node_id: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command is used in order to remove a node, specified via its node ID, from the set of known nodes of the +Redis Cluster node receiving the command. In other words the specified node is removed from the nodes table of +the node receiving the command. Read more
source§

fn cluster_get_keys_in_slot<R>( + &self, + slot: u16, + count: u64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns an array of keys names stored in the contacted node and hashing to the specified hash slot. Read more
source§

fn cluster_keyslot<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns an integer identifying the hash slot the specified key hashes to. Read more
source§

fn cluster_meet<S>( + &self, + ip: S, + port: u16, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

CLUSTER MEET is used in order to connect different Redis nodes with cluster support enabled, into a working +cluster. Read more
source§

fn cluster_replicate<S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command reconfigures a node as a replica of the specified master. If the node receiving the command is an +empty master, as a side effect of the command, the node role is changed from master to replica. Read more
source§

fn cluster_replicas<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command provides a list of replica nodes replicating from the specified master node. Read more
source§

fn cluster_reset( + &self, + mode: Option<ClusterResetFlag>, +) -> impl Future<Output = RedisResult<()>>

Reset a Redis Cluster node, in a more or less drastic way depending on the reset type, that can be hard or soft. +Note that this command does not work for masters if they hold one or more keys, in that case to completely +reset a master node keys must be removed first, e.g. by using FLUSHALL first, and then CLUSTER RESET. Read more
source§

fn cluster_set_config_epoch( + &self, + epoch: u64, +) -> impl Future<Output = RedisResult<()>>

This command sets a specific config epoch in a fresh node. Read more
source§

fn cluster_setslot( + &self, + slot: u16, + state: ClusterSetSlotState, +) -> impl Future<Output = RedisResult<()>>

CLUSTER SETSLOT is responsible for changing the state of a hash slot in the receiving node in different ways. Read more
source§

impl<C: ConfigInterface> ConfigInterface for WithOptions<C>

Available on crate feature i-config only.
source§

fn config_resetstat(&self) -> impl Future<Output = RedisResult<()>>

Resets the statistics reported by Redis using the INFO command. Read more
source§

fn config_rewrite(&self) -> impl Future<Output = RedisResult<()>>

The CONFIG REWRITE command rewrites the redis.conf file the server was started with, applying the minimal +changes needed to make it reflect the configuration currently used by the server, which may be different +compared to the original one because of the use of the CONFIG SET command. Read more
source§

fn config_get<R, S>(&self, parameter: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The CONFIG GET command is used to read the configuration parameters of a running Redis server. Read more
source§

fn config_set<P, V>( + &self, + parameter: P, + value: V, +) -> impl Future<Output = RedisResult<()>>
where + P: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The CONFIG SET command is used in order to reconfigure the server at run time without the need to restart Redis. Read more
source§

impl<C: ClientLike> Debug for WithOptions<C>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<C: ClientLike> Deref for WithOptions<C>

§

type Target = C

The resulting type after dereferencing.
source§

fn deref(&self) -> &Self::Target

Dereferences the value.
source§

impl<C: FunctionInterface> FunctionInterface for WithOptions<C>

Available on crate feature i-scripts only.
source§

fn fcall<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Invoke a function. Read more
source§

fn fcall_ro<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

This is a read-only variant of the FCALL command that cannot execute commands that modify data. Read more
source§

fn function_delete<R, S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Delete a library and all its functions. Read more
source§

fn function_delete_cluster<S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Delete a library and all its functions from each cluster node concurrently. Read more
source§

fn function_dump<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the serialized payload of loaded libraries. Read more
source§

fn function_flush<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Deletes all the libraries. Read more
source§

fn function_flush_cluster( + &self, + async: bool, +) -> impl Future<Output = RedisResult<()>>

Deletes all the libraries on all cluster nodes concurrently. Read more
source§

fn function_kill<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Kill a function that is currently executing. Read more
source§

fn function_list<R, S>( + &self, + library_name: Option<S>, + withcode: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Return information about the functions and libraries. Read more
source§

fn function_load<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis. Read more
source§

fn function_load_cluster<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis on all cluster nodes concurrently. Read more
source§

fn function_restore<R, B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload. Read more
source§

fn function_restore_cluster<B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<()>>
where + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload on all cluster nodes concurrently. Read more
source§

fn function_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return information about the function that’s currently running and information about the available execution +engines. Read more
source§

impl<C: GeoInterface> GeoInterface for WithOptions<C>

Available on crate feature i-geo only.
source§

fn geoadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + changed: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: Into<MultipleGeoValues>,

Adds the specified geospatial items (longitude, latitude, name) to the specified key. Read more
source§

fn geohash<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return valid Geohash strings representing the position of one or more elements in a sorted set value +representing a geospatial index (where elements were added using GEOADD). Read more
source§

fn geopos<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by +the sorted set at key. Read more
source§

fn geodist<R, K, S, D>( + &self, + key: K, + src: S, + dest: D, + unit: Option<GeoUnit>, +) -> impl Future<Output = RedisResult<R>>

Return the distance between two members in the geospatial index represented by the sorted set. Read more
source§

fn georadius<R, K, P>( + &self, + key: K, + position: P, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<GeoPosition>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified with the center location and the maximum distance from the center (the radius). Read more
source§

fn georadiusbymember<R, K, V>( + &self, + key: K, + member: V, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

This command is exactly like GEORADIUS with the sole difference that instead of taking, as the center of the +area to query, a longitude and latitude value, it takes the name of a member already existing inside the +geospatial index represented by the sorted set. Read more
source§

fn geosearch<R, K>( + &self, + key: K, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + withcoord: bool, + withdist: bool, + withhash: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified by a given shape. Read more
source§

fn geosearchstore<R, D, S>( + &self, + dest: D, + source: S, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + storedist: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>,

This command is like GEOSEARCH, but stores the result in destination key. Returns the number of members added to +the destination key. Read more
source§

impl<C: HashesInterface> HashesInterface for WithOptions<C>

Available on crate feature i-hashes only.
source§

fn hgetall<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all fields and values of the hash stored at key. Read more
source§

fn hdel<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Removes the specified fields from the hash stored at key. Read more
source§

fn hexists<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns if field is an existing field in the hash stored at key. Read more
source§

fn hget<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the value associated with field in the hash stored at key. Read more
source§

fn hincrby<R, K, F>( + &self, + key: K, + field: F, + increment: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increments the number stored at field in the hash stored at key by increment. Read more
source§

fn hincrbyfloat<R, K, F>( + &self, + key: K, + field: F, + increment: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increment the specified field of a hash stored at key, and representing a floating point number, by the +specified increment. Read more
source§

fn hkeys<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all field names in the hash stored at key. Read more
source§

fn hlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of fields contained in the hash stored at key. Read more
source§

fn hmget<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Returns the values associated with the specified fields in the hash stored at key. Read more
source§

fn hmset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the specified fields to their respective values in the hash stored at key. Read more
source§

fn hset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets fields in the hash stored at key to their provided values. Read more
source§

fn hsetnx<R, K, F, V>( + &self, + key: K, + field: F, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets field in the hash stored at key to value, only if field does not yet exist. Read more
source§

fn hrandfield<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random field from the hash value stored at key. Read more
source§

fn hstrlen<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the string length of the value associated with field in the hash stored at key. Read more
source§

fn hvals<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all values in the hash stored at key. Read more
source§

impl<C: HyperloglogInterface> HyperloglogInterface for WithOptions<C>

Available on crate feature i-hyperloglog only.
source§

fn pfadd<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Adds all the element arguments to the HyperLogLog data structure stored at the variable name specified as first +argument. Read more
source§

fn pfcount<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

When called with a single key, returns the approximated cardinality computed by the HyperLogLog data structure +stored at the specified variable, which is 0 if the variable does not exist. Read more
source§

fn pfmerge<R, D, S>( + &self, + dest: D, + sources: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<MultipleKeys>,

Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of the +observed sets of the source HyperLogLog structures. Read more
source§

impl<C: KeysInterface> KeysInterface for WithOptions<C>

Available on crate feature i-keys only.
source§

fn watch<K>(&self, keys: K) -> impl Future<Output = RedisResult<()>>
where + K: Into<MultipleKeys>,

Marks the given keys to be watched for conditional execution of a transaction. Read more
source§

fn unwatch(&self) -> impl Future<Output = RedisResult<()>>

Flushes all the previously watched keys for a transaction. Read more
source§

fn randomkey<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return a random key from the currently selected database. Read more
source§

fn copy<R, S, D>( + &self, + source: S, + destination: D, + db: Option<u8>, + replace: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

This command copies the value stored at the source key to the destination key. Read more
source§

fn dump<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Serialize the value stored at key in a Redis-specific format and return it as bulk string. Read more
source§

fn restore<R, K>( + &self, + key: K, + ttl: i64, + serialized: RedisValue, + replace: bool, + absttl: bool, + idletime: Option<i64>, + frequency: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Create a key associated with a value that is obtained by deserializing the provided serialized value Read more
source§

fn set<R, K, V>( + &self, + key: K, + value: V, + expire: Option<Expiration>, + options: Option<SetOptions>, + get: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set a value with optional NX|XX, EX|PX|EXAT|PXAT|KEEPTTL, and GET arguments. Read more
source§

fn get<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Read a value from the server. Read more
source§

fn getrange<R, K>( + &self, + key: K, + start: usize, + end: usize, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the substring of the string value stored at key with offsets start and end (both inclusive). Read more
source§

fn setrange<R, K, V>( + &self, + key: K, + offset: u32, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Overwrites part of the string stored at key, starting at the specified offset, for the entire length of +value. Read more
source§

fn getset<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Atomically sets key to value and returns the old value stored at key. Read more
source§

fn getdel<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the value of key and delete the key. This command is similar to GET, except for the fact that it also +deletes the key on success (if and only if the key’s value type is a string). Read more
source§

fn strlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the string value stored at key. An error is returned when key holds a non-string value. Read more
source§

fn del<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Removes the specified keys. A key is ignored if it does not exist. Read more
Unlinks the specified keys. A key is ignored if it does not exist Read more
source§

fn rename<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination. Read more
source§

fn renamenx<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination if destination does not yet exist. Read more
source§

fn append<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Append value to key if it’s a string. Read more
source§

fn mget<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the +special value nil is returned. Read more
source§

fn mset<V>(&self, values: V) -> impl Future<Output = RedisResult<()>>
where + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. Read more
source§

fn msetnx<R, V>(&self, values: V) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. MSETNX will not perform any operation at all even if just a +single key already exists. Read more
source§

fn incr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn incr_by_float<R, K>( + &self, + key: K, + val: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increment the string representing a floating point number stored at key by val. If the key does not exist, it +is set to 0 before performing the operation. Read more
source§

fn decr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn decr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation. Read more
source§

fn ttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in seconds. Read more
source§

fn pttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in milliseconds. Read more
source§

fn persist<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Remove the existing timeout on a key, turning the key from volatile (a key with an expiration) +to persistent (a key that will never expire as no timeout is associated). Read more
source§

fn expire<R, K>( + &self, + key: K, + seconds: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on key. After the timeout has expired, the key will be automatically deleted. Read more
source§

fn expire_at<R, K>( + &self, + key: K, + timestamp: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on a key based on a UNIX timestamp. Read more
source§

fn pexpire<R, K>( + &self, + key: K, + milliseconds: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command works exactly like EXPIRE but the time to live of the key is specified in milliseconds instead of +seconds. Read more
source§

fn pexpire_at<R, K>( + &self, + key: K, + timestamp: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

PEXPIREAT has the same effect and semantic as EXPIREAT, but the Unix time at which the key will expire is +specified in milliseconds instead of seconds. Read more
source§

fn exists<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns number of keys that exist from the keys arguments. Read more
source§

fn lcs<R, K1, K2>( + &self, + key1: K1, + key2: K2, + len: bool, + idx: bool, + minmatchlen: Option<i64>, + withmatchlen: bool, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis, + K1: Into<RedisKey>, + K2: Into<RedisKey>,

Runs the longest common subsequence algorithm on two keys. Read more
source§

impl<C: ListInterface> ListInterface for WithOptions<C>

Available on crate feature i-lists only.
source§

fn blmpop<R, K>( + &self, + timeout: f64, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::lmpop. Read more
source§

fn blpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BLPOP is a blocking list pop primitive. It is the blocking version of LPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the head of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BRPOP is a blocking list pop primitive. It is the blocking version of RPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the tail of the first list +that is non-empty, with the given keys being checked in the order that they are given. Read more
source§

fn brpoplpush<R, S, D>( + &self, + source: S, + destination: D, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::rpoplpush. Read more
source§

fn blmove<R, S, D>( + &self, + source: S, + destination: D, + source_direction: LMoveDirection, + destination_direction: LMoveDirection, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::lmove. Read more
source§

fn lmpop<R, K>( + &self, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements from the first non-empty list key from the list of provided key names. Read more
source§

fn lindex<R, K>( + &self, + key: K, + index: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the element at index in the list stored at key. Read more
source§

fn linsert<R, K, P, V>( + &self, + key: K, + location: ListLocation, + pivot: P, + element: V, +) -> impl Future<Output = RedisResult<R>>

Inserts element in the list stored at key either before or after the reference value pivot. Read more
source§

fn llen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the list stored at key. Read more
source§

fn lpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the first elements of the list stored at key. Read more
source§

fn lpos<R, K, V>( + &self, + key: K, + element: V, + rank: Option<i64>, + count: Option<i64>, + maxlen: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The command returns the index of matching elements inside a Redis list. Read more
source§

fn lpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the head of the list stored at key. Read more
source§

fn lpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the head of the list stored at key, only if key already exists and holds a list. Read more
source§

fn lrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified elements of the list stored at key. Read more
source§

fn lrem<R, K, V>( + &self, + key: K, + count: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Removes the first count occurrences of elements equal to element from the list stored at key. Read more
source§

fn lset<R, K, V>( + &self, + key: K, + index: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets the list element at index to element. Read more
source§

fn ltrim<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Trim an existing list so that it will contain only the specified range of elements specified. Read more
source§

fn rpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the last elements of the list stored at key. Read more
source§

fn rpoplpush<R, S, D>( + &self, + source: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the last element (tail) of the list stored at source, and pushes the element at +the first element (head) of the list stored at destination. Read more
source§

fn lmove<R, S, D>( + &self, + source: S, + dest: D, + source_direction: LMoveDirection, + dest_direction: LMoveDirection, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the first/last element (head/tail depending on the source direction argument) of +the list stored at source, and pushes the element at the first/last element (head/tail depending on the +destination direction argument) of the list stored at destination. Read more
source§

fn rpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the tail of the list stored at key. Read more
source§

fn rpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the tail of the list stored at key, only if key already exists and holds a list. Read more
source§

fn sort<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, + store: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Returns or stores the elements contained in the list, set or sorted set at key. Read more
source§

fn sort_ro<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Read-only variant of the SORT command. It is exactly like the original SORT but refuses the STORE option and can +safely be used in read-only replicas. Read more
source§

impl<C: MemoryInterface> MemoryInterface for WithOptions<C>

Available on crate feature i-memory only.
source§

fn memory_doctor<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY DOCTOR command reports about different memory-related issues that the Redis server experiences, and +advises about possible remedies. Read more
source§

fn memory_malloc_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY MALLOC-STATS command provides an internal statistics report from the memory allocator. Read more
source§

fn memory_purge(&self) -> impl Future<Output = RedisResult<()>>

The MEMORY PURGE command attempts to purge dirty pages so these can be reclaimed by the allocator. Read more
source§

fn memory_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY STATS command returns an Array reply about the memory usage of the server. Read more
source§

fn memory_usage<R, K>( + &self, + key: K, + samples: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

The MEMORY USAGE command reports the number of bytes that a key and its value require to be stored in RAM. Read more
source§

impl<C: PubsubInterface> PubsubInterface for WithOptions<C>

Available on crate feature i-pubsub only.
source§

fn subscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribe to a channel on the publish-subscribe interface. Read more
source§

fn unsubscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribe from a channel on the PubSub interface. Read more
source§

fn psubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribes the client to the given patterns. Read more
source§

fn punsubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribes the client from the given patterns, or from all of them if none is given. Read more
source§

fn publish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Publish a message on the PubSub interface, returning the number of clients that received the message. Read more
source§

fn ssubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Subscribes the client to the specified shard channels. Read more
source§

fn sunsubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Unsubscribes the client from the given shard channels, or from all of them if none is given. Read more
source§

fn spublish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Posts a message to the given shard channel. Read more
source§

fn pubsub_channels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active channels. Read more
source§

fn pubsub_numpat<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of unique patterns that are subscribed to by clients. Read more
source§

fn pubsub_numsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers (exclusive of clients subscribed to patterns) for the specified channels. Read more
source§

fn pubsub_shardchannels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active shard channels. Read more
source§

fn pubsub_shardnumsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers for the specified shard channels. Read more
source§

impl<C: RediSearchInterface> RediSearchInterface for WithOptions<C>

Available on crate feature i-redisearch only.
source§

fn ft_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns a list of all existing indexes. Read more
source§

fn ft_aggregate<R, I, Q>( + &self, + index: I, + query: Q, + options: FtAggregateOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Run a search query on an index, and perform aggregate transformations on the results. Read more
Search the index with a textual query, returning either documents or just ids. Read more
source§

fn ft_create<R, I>( + &self, + index: I, + options: FtCreateOptions, + schema: Vec<SearchSchema>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Create an index with the given specification. Read more
source§

fn ft_alter<R, I>( + &self, + index: I, + options: FtAlterOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Add a new attribute to the index. Read more
source§

fn ft_aliasadd<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. Read more
source§

fn ft_aliasdel<R, A>(&self, alias: A) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>,

Remove an alias from an index. Read more
source§

fn ft_aliasupdate<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. If the alias is already associated with another index, FT.ALIASUPDATE removes the +alias association with the previous index. Read more
source§

fn ft_config_get<R, S>(&self, option: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Retrieve configuration options. Read more
source§

fn ft_config_set<R, S, V>( + &self, + option: S, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set the value of a RediSearch configuration parameter. Read more
source§

fn ft_cursor_del<R, I, C>( + &self, + index: I, + cursor: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Delete a cursor. Read more
source§

fn ft_cursor_read<R, I, C>( + &self, + index: I, + cursor: C, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Read next results from an existing cursor. Read more
source§

fn ft_dictadd<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Add terms to a dictionary. Read more
source§

fn ft_dictdel<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Remove terms from a dictionary. Read more
source§

fn ft_dictdump<R, D>(&self, dict: D) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>,

Dump all terms in the given dictionary. Read more
source§

fn ft_dropindex<R, I>( + &self, + index: I, + dd: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Delete an index. Read more
source§

fn ft_explain<R, I, Q>( + &self, + index: I, + query: Q, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Return the execution plan for a complex query. Read more
source§

fn ft_info<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Return information and statistics on the index. Read more
source§

fn ft_spellcheck<R, I, Q>( + &self, + index: I, + query: Q, + distance: Option<u8>, + terms: Option<SpellcheckTerms>, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Perform spelling correction on a query, returning suggestions for misspelled terms. Read more
source§

fn ft_sugadd<R, K, S>( + &self, + key: K, + string: S, + score: f64, + incr: bool, + payload: Option<Bytes>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Add a suggestion string to an auto-complete suggestion dictionary. Read more
source§

fn ft_sugdel<R, K, S>( + &self, + key: K, + string: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Delete a string from a suggestion index. Read more
source§

fn ft_sugget<R, K, P>( + &self, + key: K, + prefix: P, + fuzzy: bool, + withscores: bool, + withpayloads: bool, + max: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Get completion suggestions for a prefix. Read more
source§

fn ft_suglen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the size of an auto-complete suggestion dictionary. Read more
source§

fn ft_syndump<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Dump the contents of a synonym group. Read more
source§

fn ft_synupdate<R, I, S, T>( + &self, + index: I, + synonym_group_id: S, + skipinitialscan: bool, + terms: T, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + S: Into<Str>, + T: Into<MultipleStrings>,

Update a synonym group. Read more
source§

fn ft_tagvals<R, I, F>( + &self, + index: I, + field_name: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + F: Into<Str>,

Return a distinct set of values indexed in a Tag field. Read more
source§

impl<C: RedisJsonInterface> RedisJsonInterface for WithOptions<C>

Available on crate feature i-redis-json only.
source§

fn json_arrappend<R, K, P, V>( + &self, + key: K, + path: P, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json values into the array at path after the last element in it. Read more
source§

fn json_arrindex<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + start: Option<i64>, + stop: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Search for the first occurrence of a JSON value in an array. Read more
source§

fn json_arrinsert<R, K, P, V>( + &self, + key: K, + path: P, + index: i64, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Insert the json values into the array at path before the index (shifts to the right). Read more
source§

fn json_arrlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON array at path in key. Read more
source§

fn json_arrpop<R, K, P>( + &self, + key: K, + path: Option<P>, + index: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Remove and return an element from the index in the array Read more
source§

fn json_arrtrim<R, K, P>( + &self, + key: K, + path: P, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Trim an array so that it contains only the specified inclusive range of elements Read more
source§

fn json_clear<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Clear container values (arrays/objects) and set numeric values to 0 Read more
source§

fn json_debug_memory<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report a value’s memory usage in bytes Read more
source§

fn json_del<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Delete a value. Read more
source§

fn json_get<R, K, I, N, S, P>( + &self, + key: K, + indent: Option<I>, + newline: Option<N>, + space: Option<S>, + paths: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<Str>, + N: Into<Str>, + S: Into<Str>, + P: Into<MultipleStrings>,

Return the value at path in JSON serialized form. Read more
source§

fn json_merge<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Merge a given JSON value into matching paths. Read more
source§

fn json_mget<R, K, P>( + &self, + keys: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + P: Into<Str>,

Return the values at path from multiple key arguments. Read more
source§

fn json_mset<R, K, P, V>( + &self, + values: Vec<(K, P, V)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set or update one or more JSON values according to the specified key-path-value triplets. Read more
source§

fn json_numincrby<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Increment the number value stored at path by number Read more
source§

fn json_objkeys<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the keys in the object that’s referenced by path. Read more
source§

fn json_objlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the number of keys in the JSON object at path in key. Read more
source§

fn json_resp<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the JSON in key in Redis serialization protocol specification form. Read more
source§

fn json_set<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + options: Option<SetOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set the JSON value at path in key. Read more
source§

fn json_strappend<R, K, P, V>( + &self, + key: K, + path: Option<P>, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json-string values to the string at path. Read more
source§

fn json_strlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON String at path in key. Read more
source§

fn json_toggle<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Toggle a Boolean value stored at path. Read more
source§

fn json_type<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the type of JSON value at path. Read more
source§

impl<C: ServerInterface> ServerInterface for WithOptions<C>

Available on crate feature i-server only.
source§

fn bgrewriteaof<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Instruct Redis to start an Append Only File rewrite process. Read more
source§

fn bgsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Save the DB in background. Read more
source§

fn dbsize<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the number of keys in the selected database. Read more
source§

fn select(&self, db: u8) -> impl Future<Output = RedisResult<()>>

Select the database this client should use. Read more
source§

fn failover( + &self, + to: Option<(String, u16)>, + force: bool, + abort: bool, + timeout: Option<u32>, +) -> impl Future<Output = RedisResult<()>>

This command will start a coordinated failover between the currently-connected-to master and one of its +replicas. Read more
source§

fn lastsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the UNIX TIME of the last DB save executed with success. Read more
source§

fn wait<R>( + &self, + numreplicas: i64, + timeout: i64, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis,

This command blocks the current client until all the previous write commands are successfully transferred and +acknowledged by at least the specified number of replicas. If the timeout, specified in milliseconds, is +reached, the command returns even if the specified number of replicas were not yet reached. Read more
source§

fn sentinel_primary(&self) -> Option<Server>

Read the primary Redis server identifier returned from the sentinel nodes.
source§

fn sentinel_nodes(&self) -> Option<Vec<Server>>

Read the set of known sentinel nodes.
source§

impl<C: SetsInterface> SetsInterface for WithOptions<C>

Available on crate feature i-sets only.
source§

fn sadd<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Add the specified members to the set stored at key. Read more
source§

fn scard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the set cardinality (number of elements) of the set stored at key. Read more
source§

fn sdiff<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the difference between the first set and all the successive sets. Read more
source§

fn sdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SDIFF, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sinter<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the intersection of all the given sets. Read more
source§

fn sinterstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SINTER, but instead of returning the resulting set, it is stored in destination. Read more
source§

fn sismember<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns if member is a member of the set stored at key. Read more
source§

fn smismember<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns whether each member is a member of the set stored at key. Read more
source§

fn smembers<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all the members of the set value stored at key. Read more
source§

fn smove<R, S, D, V>( + &self, + source: S, + dest: D, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Move member from the set at source to the set at destination. Read more
source§

fn spop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns one or more random members from the set value store at key. Read more
source§

fn srandmember<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the set value stored at key. Read more
source§

fn srem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Remove the specified members from the set stored at key. Read more
source§

fn sunion<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the union of all the given sets. Read more
source§

fn sunionstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SUNION, but instead of returning the resulting set, it is stored in destination. Read more
source§

impl<C: SlowlogInterface> SlowlogInterface for WithOptions<C>

Available on crate feature i-slowlog only.
source§

fn slowlog_get<R>( + &self, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read the slow queries log. Read more
source§

fn slowlog_length<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read length of the slow queries log. Read more
source§

fn slowlog_reset(&self) -> impl Future<Output = RedisResult<()>>

This command is used to reset the slow queries log. Read more
source§

impl<C: SortedSetsInterface> SortedSetsInterface for WithOptions<C>

Available on crate feature i-sorted-sets only.
source§

fn bzmpop<R, K>( + &self, + timeout: f64, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zmpop. Read more
source§

fn bzpopmin<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmin. Read more
source§

fn bzpopmax<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

The blocking variant of Self::zpopmax. Read more
source§

fn zadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + ordering: Option<Ordering>, + changed: bool, + incr: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>

Adds all the specified members with the specified scores to the sorted set stored at key. Read more
source§

fn zcard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the sorted set cardinality (number of elements) of the sorted set stored at key. Read more
source§

fn zcount<R, K>( + &self, + key: K, + min: f64, + max: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of elements in the sorted set at key with a score between min and max. Read more
source§

fn zdiff<R, K>( + &self, + keys: K, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

This command is similar to ZDIFFSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

Computes the difference between the first and all successive input sorted sets and stores the result in +destination. Read more
source§

fn zincrby<R, K, V>( + &self, + key: K, + increment: f64, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Increments the score of member in the sorted set stored at key by increment. Read more
source§

fn zinter<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZINTERSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zinterstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the intersection of the sorted sets given by the specified keys, and stores the result in +destination. Read more
source§

fn zlexcount<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns the number of elements in the sorted set at key with a value between min and +max. Read more
source§

fn zpopmax<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the highest scores in the sorted set stored at key. Read more
source§

fn zpopmin<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the lowest scores in the sorted set stored at key. Read more
source§

fn zmpop<R, K>( + &self, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements, that are member-score pairs, from the first non-empty sorted set in the provided list +of key names. Read more
source§

fn zrandmember<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the sorted set value stored at key. Read more
source§

fn zrangestore<R, D, S, M, N>( + &self, + dest: D, + source: S, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

This command is like ZRANGE, but stores the result in the destination key. Read more
source§

fn zrange<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between min and max. Read more
source§

fn zrevrangebylex<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between max and min. Read more
source§

fn zrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between min and max (including elements +with score equal to min or max). Read more
source§

fn zrevrangebyscore<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between max and min (including +elements with score equal to max or min). Read more
source§

fn zrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from low to high. Read more
source§

fn zrem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Removes the specified members from the sorted set stored at key. Non existing members are ignored. Read more
source§

fn zremrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command removes all elements in the sorted set stored at key between the lexicographical range +specified by min and max. Read more
source§

fn zremrangebyrank<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes all elements in the sorted set stored at key with rank between start and stop. Read more
source§

fn zremrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Removes all elements in the sorted set stored at key with a score between min and max. Read more
source§

fn zrevrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified range of elements in the sorted set stored at key. Read more
source§

fn zrevrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from high to low. Read more
source§

fn zscore<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the score of member in the sorted set at key. Read more
source§

fn zunion<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZUNIONSTORE, but instead of storing the resulting sorted set, it is returned to the +client. Read more
source§

fn zunionstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the union of the sorted sets given by the specified keys, and stores the result in destination. Read more
source§

fn zmscore<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns the scores associated with the specified members in the sorted set stored at key. Read more
source§

impl<C: StreamsInterface> StreamsInterface for WithOptions<C>

Available on crate feature i-streams only.
source§

fn xinfo_consumers<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

This command returns the list of consumers that belong to the groupname consumer group of the stream stored at +key. Read more
source§

fn xinfo_groups<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns the list of all consumers groups of the stream stored at key. Read more
source§

fn xinfo_stream<R, K>( + &self, + key: K, + full: bool, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns information about the stream stored at key. Read more
source§

fn xadd<R, K, C, I, F>( + &self, + key: K, + nomkstream: bool, + cap: C, + id: I, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<XID>, + F: TryInto<MultipleOrderedPairs>, + F::Error: Into<RedisError>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Appends the specified stream entry to the stream at the specified key. If the key does not exist, as a side +effect of running this command the key is created with a stream value. The creation of stream’s key can be +disabled with the NOMKSTREAM option. Read more
source§

fn xtrim<R, K, C>(&self, key: K, cap: C) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Trims the stream by evicting older entries (entries with lower IDs) if needed. Read more
source§

fn xdel<R, K, S>(&self, key: K, ids: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Removes the specified entries from a stream, and returns the number of entries deleted. Read more
source§

fn xrange_values<Ri, Rk, Rv, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Return the stream entries matching the provided range of IDs, automatically converting to a less verbose type +definition. Read more
source§

fn xrange<R, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

The command returns the stream entries matching a given range of IDs. The range is specified by a minimum +and maximum ID. All the entries having an ID between the two specified or exactly one of the two IDs specified +(closed interval) are returned. Read more
source§

fn xrevrange_values<Ri, Rk, Rv, K, E, S>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Similar to XRANGE, but with the results returned in reverse order. The results will be automatically converted +to a less verbose type definition. Read more
source§

fn xrevrange<R, K, S, E>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

Similar to XRANGE, but with the results returned in reverse order. Read more
source§

fn xlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of entries inside a stream. Read more
source§

fn xread_map<Rk1, Rk2, Rk3, Rv, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xread<R, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller. Read more
source§

fn xgroup_create<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, + mkstream: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

This command creates a new consumer group uniquely identified by groupname for the stream stored at key. Read more
source§

fn xgroup_createconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Create a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_delconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Delete a consumer named consumername in the consumer group groupname of the stream that’s stored at key. Read more
source§

fn xgroup_destroy<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Completely destroy a consumer group. Read more
source§

fn xgroup_setid<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

Set the last delivered ID for a consumer group. Read more
source§

fn xreadgroup_map<Rk1, Rk2, Rk3, Rv, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xreadgroup<R, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups. Read more
source§

fn xack<R, K, G, I>( + &self, + key: K, + group: G, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + I: Into<MultipleIDs>,

Remove one or more messages from the Pending Entries List (PEL) of a stream consumer group. Read more
source§

fn xclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

A variation of xclaim with a less verbose return type.
source§

fn xclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

In the context of a stream consumer group, this command changes the ownership of a pending message, +so that the new owner is the consumer specified as the command argument. Read more
source§

fn xautoclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<(String, Vec<XReadValue<Ri, Rk, Rv>>)>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. It also converts +the response type to a less verbose type declaration and handles potential differences between RESP2 and RESP3. Read more
source§

fn xautoclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. Read more
source§

fn xpending<R, K, G, A>( + &self, + key: K, + group: G, + args: A, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + A: Into<XPendingArgs>,

Inspect the list of pending messages in a consumer group. Read more
source§

impl<C: TimeSeriesInterface> TimeSeriesInterface for WithOptions<C>

Available on crate feature i-time-series only.
source§

fn ts_add<R, K, T, L>( + &self, + key: K, + timestamp: T, + value: f64, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + on_duplicate: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + T: TryInto<Timestamp>, + T::Error: Into<RedisError>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Append a sample to a time series. Read more
source§

fn ts_alter<R, K, L>( + &self, + key: K, + retention: Option<u64>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Update the retention, chunk size, duplicate policy, and labels of an existing time series. Read more
source§

fn ts_create<R, K, L>( + &self, + key: K, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Create a new time series. Read more
source§

fn ts_createrule<R, S, D>( + &self, + src: S, + dest: D, + aggregation: (Aggregator, u64), + align_timestamp: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Create a compaction rule. Read more
source§

fn ts_decrby<R, K, L>( + &self, + key: K, + subtrahend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given decrement. Read more
source§

fn ts_del<R, K>( + &self, + key: K, + from: i64, + to: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Delete all samples between two timestamps for a given time series. Read more
source§

fn ts_deleterule<R, S, D>( + &self, + src: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Delete a compaction rule. Read more
source§

fn ts_get<R, K>( + &self, + key: K, + latest: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the sample with the highest timestamp from a given time series. Read more
source§

fn ts_incrby<R, K, L>( + &self, + key: K, + addend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given increment. Read more
source§

fn ts_info<R, K>( + &self, + key: K, + debug: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return information and statistics for a time series. Read more
source§

fn ts_madd<R, K, I>(&self, samples: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: IntoIterator<Item = (K, Timestamp, f64)>,

Append new samples to one or more time series. Read more
source§

fn ts_mget<R, L, S, I>( + &self, + latest: bool, + labels: Option<L>, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + L: Into<GetLabels>, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get the sample with the highest timestamp from each time series matching a specific filter. Read more
source§

fn ts_mrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the forward direction. Read more
source§

fn ts_mrevrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the reverse direction. Read more
source§

fn ts_queryindex<R, S, I>( + &self, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get all time series keys matching a filter list. Read more
source§

fn ts_range<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in forward direction. Read more
source§

fn ts_revrange<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in reverse direction. Read more

Auto Trait Implementations§

§

impl<C> !Freeze for WithOptions<C>

§

impl<C> RefUnwindSafe for WithOptions<C>
where + C: RefUnwindSafe,

§

impl<C> Send for WithOptions<C>
where + C: Send,

§

impl<C> Sync for WithOptions<C>
where + C: Sync,

§

impl<C> Unpin for WithOptions<C>
where + C: Unpin,

§

impl<C> UnwindSafe for WithOptions<C>
where + C: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/clients/transaction/struct.Transaction.html b/doc/fred/clients/transaction/struct.Transaction.html new file mode 100644 index 00000000..29d8d0f9 --- /dev/null +++ b/doc/fred/clients/transaction/struct.Transaction.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/clients/struct.Transaction.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/acl/trait.AclInterface.html b/doc/fred/commands/interfaces/acl/trait.AclInterface.html new file mode 100644 index 00000000..7e6b4cb3 --- /dev/null +++ b/doc/fred/commands/interfaces/acl/trait.AclInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.AclInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/client/trait.ClientInterface.html b/doc/fred/commands/interfaces/client/trait.ClientInterface.html new file mode 100644 index 00000000..1d69390c --- /dev/null +++ b/doc/fred/commands/interfaces/client/trait.ClientInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.ClientInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/cluster/trait.ClusterInterface.html b/doc/fred/commands/interfaces/cluster/trait.ClusterInterface.html new file mode 100644 index 00000000..fb028970 --- /dev/null +++ b/doc/fred/commands/interfaces/cluster/trait.ClusterInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.ClusterInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/config/trait.ConfigInterface.html b/doc/fred/commands/interfaces/config/trait.ConfigInterface.html new file mode 100644 index 00000000..00c5f987 --- /dev/null +++ b/doc/fred/commands/interfaces/config/trait.ConfigInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.ConfigInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/geo/trait.GeoInterface.html b/doc/fred/commands/interfaces/geo/trait.GeoInterface.html new file mode 100644 index 00000000..2a32e4b6 --- /dev/null +++ b/doc/fred/commands/interfaces/geo/trait.GeoInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.GeoInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/hashes/trait.HashesInterface.html b/doc/fred/commands/interfaces/hashes/trait.HashesInterface.html new file mode 100644 index 00000000..d76cb791 --- /dev/null +++ b/doc/fred/commands/interfaces/hashes/trait.HashesInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.HashesInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/hyperloglog/trait.HyperloglogInterface.html b/doc/fred/commands/interfaces/hyperloglog/trait.HyperloglogInterface.html new file mode 100644 index 00000000..78148b61 --- /dev/null +++ b/doc/fred/commands/interfaces/hyperloglog/trait.HyperloglogInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.HyperloglogInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/keys/trait.KeysInterface.html b/doc/fred/commands/interfaces/keys/trait.KeysInterface.html new file mode 100644 index 00000000..2eec8f4e --- /dev/null +++ b/doc/fred/commands/interfaces/keys/trait.KeysInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.KeysInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/lists/trait.ListInterface.html b/doc/fred/commands/interfaces/lists/trait.ListInterface.html new file mode 100644 index 00000000..5cfd20eb --- /dev/null +++ b/doc/fred/commands/interfaces/lists/trait.ListInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.ListInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/lua/trait.FunctionInterface.html b/doc/fred/commands/interfaces/lua/trait.FunctionInterface.html new file mode 100644 index 00000000..3ac799dc --- /dev/null +++ b/doc/fred/commands/interfaces/lua/trait.FunctionInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.FunctionInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/lua/trait.LuaInterface.html b/doc/fred/commands/interfaces/lua/trait.LuaInterface.html new file mode 100644 index 00000000..73476dda --- /dev/null +++ b/doc/fred/commands/interfaces/lua/trait.LuaInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.LuaInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/memory/trait.MemoryInterface.html b/doc/fred/commands/interfaces/memory/trait.MemoryInterface.html new file mode 100644 index 00000000..6da99ff5 --- /dev/null +++ b/doc/fred/commands/interfaces/memory/trait.MemoryInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.MemoryInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/metrics/trait.MetricsInterface.html b/doc/fred/commands/interfaces/metrics/trait.MetricsInterface.html new file mode 100644 index 00000000..d2075a64 --- /dev/null +++ b/doc/fred/commands/interfaces/metrics/trait.MetricsInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.MetricsInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/pubsub/trait.PubsubInterface.html b/doc/fred/commands/interfaces/pubsub/trait.PubsubInterface.html new file mode 100644 index 00000000..30044851 --- /dev/null +++ b/doc/fred/commands/interfaces/pubsub/trait.PubsubInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.PubsubInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/redis_json/trait.RedisJsonInterface.html b/doc/fred/commands/interfaces/redis_json/trait.RedisJsonInterface.html new file mode 100644 index 00000000..1bc4ae7d --- /dev/null +++ b/doc/fred/commands/interfaces/redis_json/trait.RedisJsonInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.RedisJsonInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/redisearch/trait.RediSearchInterface.html b/doc/fred/commands/interfaces/redisearch/trait.RediSearchInterface.html new file mode 100644 index 00000000..0d3c9d20 --- /dev/null +++ b/doc/fred/commands/interfaces/redisearch/trait.RediSearchInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.RediSearchInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/sentinel/trait.SentinelInterface.html b/doc/fred/commands/interfaces/sentinel/trait.SentinelInterface.html new file mode 100644 index 00000000..7f8fe6c8 --- /dev/null +++ b/doc/fred/commands/interfaces/sentinel/trait.SentinelInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.SentinelInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/server/trait.ServerInterface.html b/doc/fred/commands/interfaces/server/trait.ServerInterface.html new file mode 100644 index 00000000..c75337dd --- /dev/null +++ b/doc/fred/commands/interfaces/server/trait.ServerInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.ServerInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/sets/trait.SetsInterface.html b/doc/fred/commands/interfaces/sets/trait.SetsInterface.html new file mode 100644 index 00000000..bb9fab9b --- /dev/null +++ b/doc/fred/commands/interfaces/sets/trait.SetsInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.SetsInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/slowlog/trait.SlowlogInterface.html b/doc/fred/commands/interfaces/slowlog/trait.SlowlogInterface.html new file mode 100644 index 00000000..40e30db4 --- /dev/null +++ b/doc/fred/commands/interfaces/slowlog/trait.SlowlogInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.SlowlogInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/sorted_sets/trait.SortedSetsInterface.html b/doc/fred/commands/interfaces/sorted_sets/trait.SortedSetsInterface.html new file mode 100644 index 00000000..80df8c31 --- /dev/null +++ b/doc/fred/commands/interfaces/sorted_sets/trait.SortedSetsInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.SortedSetsInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/streams/trait.StreamsInterface.html b/doc/fred/commands/interfaces/streams/trait.StreamsInterface.html new file mode 100644 index 00000000..8b60783b --- /dev/null +++ b/doc/fred/commands/interfaces/streams/trait.StreamsInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.StreamsInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/timeseries/trait.TimeSeriesInterface.html b/doc/fred/commands/interfaces/timeseries/trait.TimeSeriesInterface.html new file mode 100644 index 00000000..0a5190a1 --- /dev/null +++ b/doc/fred/commands/interfaces/timeseries/trait.TimeSeriesInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.TimeSeriesInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/tracking/trait.TrackingInterface.html b/doc/fred/commands/interfaces/tracking/trait.TrackingInterface.html new file mode 100644 index 00000000..d2fd7ec5 --- /dev/null +++ b/doc/fred/commands/interfaces/tracking/trait.TrackingInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.TrackingInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/commands/interfaces/transactions/trait.TransactionInterface.html b/doc/fred/commands/interfaces/transactions/trait.TransactionInterface.html new file mode 100644 index 00000000..a104a56f --- /dev/null +++ b/doc/fred/commands/interfaces/transactions/trait.TransactionInterface.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../fred/interfaces/trait.TransactionInterface.html...

+ + + \ No newline at end of file diff --git a/doc/fred/error/enum.RedisErrorKind.html b/doc/fred/error/enum.RedisErrorKind.html new file mode 100644 index 00000000..029d8b0a --- /dev/null +++ b/doc/fred/error/enum.RedisErrorKind.html @@ -0,0 +1,71 @@ +RedisErrorKind in fred::error - Rust

Enum fred::error::RedisErrorKind

source ·
pub enum RedisErrorKind {
+
Show 17 variants Config, + Auth, + IO, + InvalidCommand, + InvalidArgument, + Url, + Protocol, + Tls, + Canceled, + Unknown, + Timeout, + Cluster, + Parse, + Sentinel, + NotFound, + Backpressure, + Replica, +
}
Expand description

An enum representing the type of error from Redis.

+

Variants§

§

Config

A fatal client configuration error. These errors will shutdown a client and break out of any reconnection +attempts.

+
§

Auth

An authentication error.

+
§

IO

An IO error with the underlying connection.

+
§

InvalidCommand

An invalid command, such as trying to perform a set command on a client after calling subscribe.

+
§

InvalidArgument

An invalid argument or set of arguments to a command.

+
§

Url

An invalid URL error.

+
§

Protocol

A protocol error such as an invalid or unexpected frame from the server.

+
§

Tls

Available on crate features enable-native-tls or enable-rustls or enable-rustls-ring only.

A TLS error.

+
§

Canceled

An error indicating the request was canceled.

+
§

Unknown

An unknown error.

+
§

Timeout

A timeout error.

+
§

Cluster

An error used to indicate that the cluster’s state has changed. These errors will show up on the on_error +error stream even though the client will automatically attempt to recover.

+
§

Parse

A parser error.

+
§

Sentinel

An error communicating with redis sentinel.

+
§

NotFound

An error indicating a value was not found, often used when trying to cast a nil response from the server to a +non-nullable type.

+
§

Backpressure

An error indicating that the caller should apply backpressure and retry the command.

+
§

Replica

Available on crate feature replicas only.

An error associated with a replica node.

+

Implementations§

source§

impl RedisErrorKind

source

pub fn to_str(&self) -> &'static str

Trait Implementations§

source§

impl Clone for RedisErrorKind

source§

fn clone(&self) -> RedisErrorKind

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for RedisErrorKind

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for RedisErrorKind

source§

fn eq(&self, other: &RedisErrorKind) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for RedisErrorKind

source§

impl StructuralPartialEq for RedisErrorKind

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/error/index.html b/doc/fred/error/index.html new file mode 100644 index 00000000..9d3130ff --- /dev/null +++ b/doc/fred/error/index.html @@ -0,0 +1,2 @@ +fred::error - Rust

Module fred::error

source ·
Expand description

Error structs returned by Redis commands.

+

Structs§

Enums§

\ No newline at end of file diff --git a/doc/fred/error/sidebar-items.js b/doc/fred/error/sidebar-items.js new file mode 100644 index 00000000..f80cd418 --- /dev/null +++ b/doc/fred/error/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["RedisErrorKind"],"struct":["RedisError"]}; \ No newline at end of file diff --git a/doc/fred/error/struct.RedisError.html b/doc/fred/error/struct.RedisError.html new file mode 100644 index 00000000..b68602a1 --- /dev/null +++ b/doc/fred/error/struct.RedisError.html @@ -0,0 +1,44 @@ +RedisError in fred::error - Rust

Struct fred::error::RedisError

source ·
pub struct RedisError { /* private fields */ }
Expand description

An error from Redis.

+

Implementations§

source§

impl RedisError

source

pub fn new<T>(kind: RedisErrorKind, details: T) -> RedisError
where + T: Into<Cow<'static, str>>,

Create a new Redis error with the provided details.

+
source

pub fn kind(&self) -> &RedisErrorKind

Read the type of error without any associated data.

+
source

pub fn change_kind(&mut self, kind: RedisErrorKind)

Change the kind of the error.

+
source

pub fn details(&self) -> &str

Read details about the error.

+
source

pub fn new_canceled() -> Self

Create a new empty Canceled error.

+
source

pub fn is_cluster(&self) -> bool

Whether the error is a Cluster error.

+
source

pub fn is_canceled(&self) -> bool

Whether the error is a Canceled error.

+
source

pub fn is_replica(&self) -> bool

Available on crate feature replicas only.

Whether the error is a Replica error.

+
source

pub fn is_not_found(&self) -> bool

Whether the error is a NotFound error.

+

Trait Implementations§

source§

impl Clone for RedisError

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for RedisError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for RedisError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for RedisError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
source§

impl From<Error> for RedisError

Available on crate feature serde-json only.
source§

fn from(e: Error) -> Self

Converts to this type from the input type.
source§

impl PartialEq for RedisError

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for RedisError

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/glommio/interfaces/fn.spawn_event_listener.html b/doc/fred/glommio/interfaces/fn.spawn_event_listener.html new file mode 100644 index 00000000..b5d3060e --- /dev/null +++ b/doc/fred/glommio/interfaces/fn.spawn_event_listener.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/interfaces/fn.spawn_event_listener.html...

+ + + \ No newline at end of file diff --git a/doc/fred/glommio/interfaces/trait.ClientLike.html b/doc/fred/glommio/interfaces/trait.ClientLike.html new file mode 100644 index 00000000..a05f76d2 --- /dev/null +++ b/doc/fred/glommio/interfaces/trait.ClientLike.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/interfaces/trait.ClientLike.html...

+ + + \ No newline at end of file diff --git a/doc/fred/index.html b/doc/fred/index.html new file mode 100644 index 00000000..819d2be3 --- /dev/null +++ b/doc/fred/index.html @@ -0,0 +1,107 @@ +fred - Rust

Crate fred

source ·
Expand description

§Fred

+

License +License +CircleCI +Crates.io +API docs

+

An async client for Redis and Valkey

+

§Example

+
use fred::prelude::*;
+
+#[tokio::main]
+async fn main() -> Result<(), RedisError> {
+  let client = RedisClient::default();
+  client.init().await?;
+
+  // convert responses to many common Rust types
+  let foo: Option<String> = client.get("foo").await?;
+  assert!(foo.is_none());
+
+  client.set("foo", "bar", None, None, false).await?;
+  // or use turbofish to declare response types
+  println!("Foo: {:?}", client.get::<String, _>("foo").await?);
+
+  client.quit().await?;
+  Ok(())
+}
+

See the examples for more.

+

§Features

+ +

See the build features for more information.

+

§Client Features

+ + + + + + + + + + + + + + + + + + + + + + +
NameDefaultDescription
transactionsxEnable a Transaction interface.
enable-native-tlsEnable TLS support via native-tls.
enable-rustlsEnable TLS support via rustls with the default crypto backend features.
enable-rustls-ringEnable TLS support via rustls and the ring crypto backend.
vendored-opensslEnable the native-tls/vendored feature.
metricsEnable the metrics interface to track overall latency, network latency, and request/response sizes.
full-tracingEnable full tracing support. This can emit a lot of data.
partial-tracingEnable partial tracing support, only emitting traces for top level commands and network latency.
blocking-encodingUse a blocking task for encoding or decoding frames. This can be useful for clients that send or receive large payloads, but requires a multi-thread Tokio runtime.
custom-reconnect-errorsEnable an interface for callers to customize the types of errors that should automatically trigger reconnection logic.
monitorEnable an interface for running the MONITOR command.
sentinel-clientEnable an interface for communicating directly with Sentinel nodes. This is not necessary to use normal Redis clients behind a sentinel layer.
sentinel-authEnable an interface for using different authentication credentials to sentinel nodes.
subscriber-clientEnable a subscriber client interface that manages channel subscription state for callers.
serde-jsonEnable an interface to automatically convert Redis types to JSON via serde-json.
mocksEnable a mocking layer interface that can be used to intercept and process commands in tests.
dnsEnable an interface that allows callers to override the DNS lookup logic.
replicasEnable an interface that routes commands to replica nodes.
default-nil-typesEnable a looser parsing interface for nil values.
sha-1Enable an interface for hashing Lua scripts.
unix-socketsEnable Unix socket support.
glommioEnable experimental Glommio support. See the Runtime docs for more information.
+

§Interface Features

+

The command interfaces have many functions and compile times can add up quickly. Interface features +begin with i- and control which public interfaces are built.

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
NameDefaultDescription
i-allEnable the interfaces included with a basic Redis or Valkey installation. This does not include i-redis-stack features.
i-stdxEnable the common data structure interfaces (lists, sets, streams, keys, etc).
i-aclEnable the ACL command interface.
i-clientEnable the CLIENT command interface.
i-clusterEnable the CLUSTER command interface.
i-configEnable the CONFIG command interface.
i-geoEnable the GEO command interface.
i-hashesEnable the hashes (HGET, etc) command interface.
i-hyperloglogEnable the hyperloglog command interface.
i-keysEnable the main keys (GET, SET, etc) command interface.
i-listsEnable the lists (LPUSH, etc) command interface.
i-scriptsEnable the scripting command interfaces.
i-memoryEnable the MEMORY command interfaces.
i-pubsubEnable the publish-subscribe command interfaces.
i-serverEnable the server control (SHUTDOWN, BGSAVE, etc) interfaces.
i-setsEnable the sets (SADD, etc) interface.
i-sorted-setsEnable the sorted sets (ZADD, etc) interface.
i-slowlogEnable the SLOWLOG interface.
i-streamsEnable the streams (XADD, etc) interface.
i-trackingEnable a client tracking interface.
i-time-seriesEnable a Redis Timeseries interface.
i-redis-jsonEnable a RedisJSON interface.
i-redisearchEnable a RediSearch interface.
i-redis-stackEnable the Redis Stack interfaces (i-redis-json, i-time-series, etc).
+

§Debugging Features

+ + +
NameDefaultDescription
debug-idsEnable a global counter used to differentiate commands in logs.
network-logsEnable additional TRACE logs for all frames on all sockets.
+

Re-exports§

  • pub extern crate bytes;
  • pub extern crate bytes_utils;
  • pub extern crate native_tls;
  • pub extern crate rustls;
  • pub extern crate rustls_native_certs;
  • pub extern crate serde_json;
  • pub extern crate socket2;
  • pub extern crate tracing;

Modules§

  • Redis client implementations.
  • Error structs returned by Redis commands.
  • Traits that implement portions of the Redis interface.
  • monitormonitor
    An interface to run the MONITOR command.
  • Convenience module to import a RedisClient, all possible interfaces, error types, and common argument types or +return value types.
  • The structs and enums used by the Redis client.
  • Various client utility functions.

Macros§

\ No newline at end of file diff --git a/doc/fred/interfaces/enum.Resp3Frame.html b/doc/fred/interfaces/enum.Resp3Frame.html new file mode 100644 index 00000000..1bd7bc96 --- /dev/null +++ b/doc/fred/interfaces/enum.Resp3Frame.html @@ -0,0 +1,138 @@ +Resp3Frame in fred::interfaces - Rust

Enum fred::interfaces::Resp3Frame

pub enum Resp3Frame {
+
Show 16 variants BlobString { + data: Bytes, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + BlobError { + data: Bytes, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + SimpleString { + data: Bytes, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + SimpleError { + data: StrInner<Bytes>, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Boolean { + data: bool, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Null, + Number { + data: i64, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Double { + data: f64, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + BigNumber { + data: Bytes, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + VerbatimString { + data: Bytes, + format: VerbatimStringFormat, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Array { + data: Vec<BytesFrame>, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Map { + data: HashMap<BytesFrame, BytesFrame>, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Set { + data: HashSet<BytesFrame>, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Push { + data: Vec<BytesFrame>, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Hello { + version: RespVersion, + auth: Option<(StrInner<Bytes>, StrInner<Bytes>)>, + setname: Option<StrInner<Bytes>>, + }, + ChunkedString(Bytes), +
}
Expand description

A RESP3 frame that uses Bytes and Str as the underlying buffer type.

+

https://github.com/antirez/RESP3/blob/master/spec.md

+

Variants§

§

BlobString

A blob of bytes.

+

Fields

§data: Bytes
§

BlobError

A blob representing an error.

+

Fields

§data: Bytes
§

SimpleString

A small string.

+

The internal data type is Bytes in order to support callers that use this interface to parse a MONITOR +stream.

+

Fields

§data: Bytes
§

SimpleError

A small string representing an error.

+

Fields

§data: StrInner<Bytes>
§

Boolean

A boolean type.

+

Fields

§data: bool
§

Null

A null type.

+
§

Number

A signed 64-bit integer.

+

Fields

§data: i64
§

Double

A signed 64-bit floating point number.

+

Fields

§data: f64
§

BigNumber

A large number not representable as a Number or Double.

+

This library does not attempt to parse this.

+

Fields

§data: Bytes
§

VerbatimString

A string to be displayed without any escaping or filtering.

+

Fields

§data: Bytes
§format: VerbatimStringFormat
§

Array

An array of frames.

+
§

Map

An unordered map of key-value pairs.

+

According to the spec keys can be any other RESP3 data type. However, it doesn’t make sense to implement Hash +for certain aggregate types. The can_hash function can be used to +detect this.

+
§

Set

An unordered collection of other frames with a uniqueness constraint.

+
§

Push

Out-of-band data.

+
§

Hello

A special frame type used when first connecting to the server to describe the protocol version and optional +credentials.

+

Fields

§version: RespVersion
§auth: Option<(StrInner<Bytes>, StrInner<Bytes>)>
§setname: Option<StrInner<Bytes>>
§

ChunkedString(Bytes)

One chunk of a streaming blob.

+

Implementations§

§

impl BytesFrame

pub fn to_owned_frame(&self) -> OwnedFrame

Copy the frame contents into a new [OwnedFrame].

+

Trait Implementations§

§

impl Clone for BytesFrame

§

fn clone(&self) -> BytesFrame

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for BytesFrame

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl From<bool> for BytesFrame

§

fn from(value: bool) -> BytesFrame

Converts to this type from the input type.
§

impl From<f64> for BytesFrame

§

fn from(value: f64) -> BytesFrame

Converts to this type from the input type.
§

impl From<i64> for BytesFrame

§

fn from(value: i64) -> BytesFrame

Converts to this type from the input type.
§

impl Hash for BytesFrame

§

fn hash<H>(&self, state: &mut H)
where + H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
§

impl PartialEq for BytesFrame

§

fn eq(&self, other: &BytesFrame) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
§

impl Resp3Frame for BytesFrame

§

fn add_attributes( + &mut self, + attributes: <BytesFrame as Resp3Frame>::Attributes, +) -> Result<(), RedisProtocolError>

Attempt to add attributes to the frame, extending the existing attributes if needed.

+
§

type Attributes = HashMap<BytesFrame, BytesFrame>

§

fn from_buffer( + target: FrameKind, + buf: impl IntoIterator<Item = BytesFrame>, + attributes: Option<<BytesFrame as Resp3Frame>::Attributes>, +) -> Result<BytesFrame, RedisProtocolError>

Create the target aggregate type based on a buffered set of chunked frames.
§

fn attributes(&self) -> Option<&<BytesFrame as Resp3Frame>::Attributes>

Read the attributes attached to the frame.
§

fn take_attributes(&mut self) -> Option<<BytesFrame as Resp3Frame>::Attributes>

Take the attributes off this frame.
§

fn attributes_mut( + &mut self, +) -> Option<&mut <BytesFrame as Resp3Frame>::Attributes>

Read a mutable reference to any attributes attached to the frame.
§

fn new_empty() -> BytesFrame

Create a new empty frame with attribute support.
§

fn new_end_stream() -> BytesFrame

Create a new frame that terminates a stream.
§

fn len(&self) -> usize

A context-aware length function that returns the length of the inner frame contents. Read more
§

fn take(&mut self) -> BytesFrame

Replace self with Null, returning the original value.
§

fn kind(&self) -> FrameKind

Read the associated FrameKind.
§

fn is_end_stream_frame(&self) -> bool

Whether the frame is an empty chunked string, signifying the end of a chunked string stream.
§

fn verbatim_string_format(&self) -> Option<&VerbatimStringFormat>

If the frame is a verbatim string then read the associated format.
§

fn as_str(&self) -> Option<&str>

Read the frame as a string slice if it can be parsed as a UTF-8 string without allocating.
§

fn as_bool(&self) -> Option<bool>

Attempt to convert the frame to a bool.
§

fn to_string(&self) -> Option<String>

Read the frame as a String if it can be parsed as a UTF-8 string.
§

fn as_bytes(&self) -> Option<&[u8]>

Attempt to read the frame as a byte slice.
§

fn encode_len(&self) -> usize

Read the number of bytes necessary to represent the frame and any associated attributes.
§

fn is_normal_pubsub_message(&self) -> bool

Whether the frame is a message from a subscribe call.
§

fn is_pattern_pubsub_message(&self) -> bool

Whether the frame is a message from a psubscribe call.
§

fn is_shard_pubsub_message(&self) -> bool

Whether the frame is a message from a ssubscribe call.
§

fn is_redirection(&self) -> bool

Whether the frame is a MOVED or ASK redirection.
§

fn as_f64(&self) -> Option<f64>

Convert the frame to a double.
§

impl<B> TryFrom<(FrameKind, B)> for BytesFrame
where + B: Into<Bytes>,

§

type Error = RedisProtocolError

The type returned in the event of a conversion error.
§

fn try_from( + _: (FrameKind, B), +) -> Result<BytesFrame, <BytesFrame as TryFrom<(FrameKind, B)>>::Error>

Performs the conversion.
source§

impl TryFrom<BytesFrame> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: Resp3Frame) -> Result<Self, Self::Error>

Performs the conversion.
§

impl Eq for BytesFrame

§

impl StructuralPartialEq for BytesFrame

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CallHasher for T
where + T: Hash + ?Sized,

§

default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64
where + H: Hash + ?Sized, + B: BuildHasher,

source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/interfaces/fn.spawn_event_listener.html b/doc/fred/interfaces/fn.spawn_event_listener.html new file mode 100644 index 00000000..e3ad7e8a --- /dev/null +++ b/doc/fred/interfaces/fn.spawn_event_listener.html @@ -0,0 +1,6 @@ +spawn_event_listener in fred::interfaces - Rust

Function fred::interfaces::spawn_event_listener

source ·
pub fn spawn_event_listener<T, F>(
+    rx: BroadcastReceiver<T>,
+    func: F,
+) -> JoinHandle<RedisResult<()>>
where + T: Clone + 'static, + F: Fn(T) -> RedisResult<()> + 'static,
\ No newline at end of file diff --git a/doc/fred/interfaces/index.html b/doc/fred/interfaces/index.html new file mode 100644 index 00000000..1a1e6994 --- /dev/null +++ b/doc/fred/interfaces/index.html @@ -0,0 +1,2 @@ +fred::interfaces - Rust

Module fred::interfaces

source ·
Expand description

Traits that implement portions of the Redis interface.

+

Enums§

Traits§

Functions§

Type Aliases§

\ No newline at end of file diff --git a/doc/fred/interfaces/sidebar-items.js b/doc/fred/interfaces/sidebar-items.js new file mode 100644 index 00000000..f48b2ee9 --- /dev/null +++ b/doc/fred/interfaces/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Resp3Frame"],"fn":["spawn_event_listener"],"trait":["AclInterface","AuthInterface","ClientInterface","ClientLike","ClusterInterface","ConfigInterface","EventInterface","FunctionInterface","GeoInterface","HashesInterface","HeartbeatInterface","HyperloglogInterface","KeysInterface","ListInterface","LuaInterface","MemoryInterface","MetricsInterface","PubsubInterface","RediSearchInterface","RedisJsonInterface","SentinelInterface","ServerInterface","SetsInterface","SlowlogInterface","SortedSetsInterface","StreamsInterface","TimeSeriesInterface","TrackingInterface","TransactionInterface"],"type":["RedisResult"]}; \ No newline at end of file diff --git a/doc/fred/interfaces/trait.AclInterface.html b/doc/fred/interfaces/trait.AclInterface.html new file mode 100644 index 00000000..93fe80c2 --- /dev/null +++ b/doc/fred/interfaces/trait.AclInterface.html @@ -0,0 +1,105 @@ +AclInterface in fred::interfaces - Rust

Trait fred::interfaces::AclInterface

source ·
pub trait AclInterface: ClientLike + Sized {
+    // Provided methods
+    fn acl_setuser<S, V>(
+        &self,
+        username: S,
+        rules: V,
+    ) -> impl Future<Output = RedisResult<()>>
+       where S: Into<Str>,
+             V: TryInto<MultipleValues>,
+             V::Error: Into<RedisError> { ... }
+    fn acl_load(&self) -> impl Future<Output = RedisResult<()>> { ... }
+    fn acl_save(&self) -> impl Future<Output = RedisResult<()>> { ... }
+    fn acl_list<R>(&self) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn acl_users<R>(&self) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn acl_getuser<R, S>(
+        &self,
+        username: S,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             S: Into<Str> { ... }
+    fn acl_deluser<R, S>(
+        &self,
+        usernames: S,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             S: Into<MultipleStrings> { ... }
+    fn acl_cat<R>(
+        &self,
+        category: Option<Str>,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn acl_genpass<R>(
+        &self,
+        bits: Option<u16>,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn acl_whoami<R>(&self) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn acl_log_count<R>(
+        &self,
+        count: Option<u32>,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn acl_log_reset(&self) -> impl Future<Output = RedisResult<()>> { ... }
+}
Available on crate feature i-acl only.
Expand description

Functions that implement the ACL interface.

+

Provided Methods§

source

fn acl_setuser<S, V>( + &self, + username: S, + rules: V, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Create an ACL user with the specified rules or modify the rules of an existing user.

+

https://redis.io/commands/acl-setuser

+
source

fn acl_load(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will reload +the ACLs from the file, replacing all the current ACL rules with the ones defined in the file.

+

https://redis.io/commands/acl-load

+
source

fn acl_save(&self) -> impl Future<Output = RedisResult<()>>

When Redis is configured to use an ACL file (with the aclfile configuration option), this command will save the +currently defined ACLs from the server memory to the ACL file.

+

https://redis.io/commands/acl-save

+
source

fn acl_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the currently active ACL rules in the Redis server.

+

https://redis.io/commands/acl-list\

+
source

fn acl_users<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows a list of all the usernames of the currently configured users in the Redis ACL system.

+

https://redis.io/commands/acl-users

+
source

fn acl_getuser<R, S>(&self, username: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns all the rules defined for an existing ACL user.

+

https://redis.io/commands/acl-getuser

+
source

fn acl_deluser<R, S>( + &self, + usernames: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Delete all the specified ACL users and terminate all the connections that are authenticated with such users.

+

https://redis.io/commands/acl-deluser

+
source

fn acl_cat<R>( + &self, + category: Option<Str>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command shows the available ACL categories if called without arguments. If a category name is given, +the command shows all the Redis commands in the specified category.

+

https://redis.io/commands/acl-cat

+
source

fn acl_genpass<R>( + &self, + bits: Option<u16>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Generate a password with length bits, returning the password.

+

https://redis.io/commands/acl-genpass

+
source

fn acl_whoami<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the username the current connection is authenticated with. New connections are authenticated +with the “default” user.

+

https://redis.io/commands/acl-whoami

+
source

fn acl_log_count<R>( + &self, + count: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read count recent ACL security events.

+

https://redis.io/commands/acl-log

+
source

fn acl_log_reset(&self) -> impl Future<Output = RedisResult<()>>

Clear the ACL security events logs.

+

https://redis.io/commands/acl-log

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.AuthInterface.html b/doc/fred/interfaces/trait.AuthInterface.html new file mode 100644 index 00000000..ee8ef4c4 --- /dev/null +++ b/doc/fred/interfaces/trait.AuthInterface.html @@ -0,0 +1,34 @@ +AuthInterface in fred::interfaces - Rust

Trait fred::interfaces::AuthInterface

source ·
pub trait AuthInterface: ClientLike {
+    // Provided methods
+    fn auth<S>(
+        &self,
+        username: Option<String>,
+        password: S,
+    ) -> impl Future<Output = RedisResult<()>>
+       where S: Into<Str> { ... }
+    fn hello(
+        &self,
+        version: RespVersion,
+        auth: Option<(Str, Str)>,
+        setname: Option<Str>,
+    ) -> impl Future<Output = RedisResult<()>> { ... }
+}
Expand description

Functions for authenticating clients.

+

Provided Methods§

source

fn auth<S>( + &self, + username: Option<String>, + password: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Request for authentication in a password-protected Redis server. Returns ok if successful.

+

The client will automatically authenticate with the default user if a password is provided in the associated +RedisConfig when calling connect.

+

If running against clustered servers this function will authenticate all connections.

+

https://redis.io/commands/auth

+
source

fn hello( + &self, + version: RespVersion, + auth: Option<(Str, Str)>, + setname: Option<Str>, +) -> impl Future<Output = RedisResult<()>>

Switch to a different protocol, optionally authenticating in the process.

+

If running against clustered servers this function will issue the HELLO command to each server concurrently.

+

https://redis.io/commands/hello

+

Object Safety§

This trait is not object safe.

Implementors§

source§

impl AuthInterface for RedisClient

source§

impl AuthInterface for SentinelClient

source§

impl AuthInterface for SubscriberClient

source§

impl AuthInterface for Transaction

source§

impl<C: AuthInterface> AuthInterface for Pipeline<C>

Available on crate feature i-server only.
source§

impl<C: AuthInterface> AuthInterface for WithOptions<C>

Available on crate feature i-server only.
\ No newline at end of file diff --git a/doc/fred/interfaces/trait.ClientInterface.html b/doc/fred/interfaces/trait.ClientInterface.html new file mode 100644 index 00000000..8fa81636 --- /dev/null +++ b/doc/fred/interfaces/trait.ClientInterface.html @@ -0,0 +1,171 @@ +ClientInterface in fred::interfaces - Rust

Trait fred::interfaces::ClientInterface

source ·
pub trait ClientInterface: ClientLike + Sized {
+
Show 16 methods // Provided methods + fn client_id<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn connection_ids(&self) -> impl Future<Output = HashMap<Server, i64>> { ... } + fn client_info<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn client_kill<R>( + &self, + filters: Vec<ClientKillFilter>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn client_list<R, I>( + &self, + type: Option<ClientKillType>, + ids: Option<Vec<String>>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn client_getname<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn client_setname<S>( + &self, + name: S, + ) -> impl Future<Output = RedisResult<()>> + where S: Into<Str> { ... } + fn client_pause( + &self, + timeout: i64, + mode: Option<ClientPauseKind>, + ) -> impl Future<Output = RedisResult<()>> { ... } + fn client_unpause(&self) -> impl Future<Output = RedisResult<()>> { ... } + fn client_reply( + &self, + flag: ClientReplyFlag, + ) -> impl Future<Output = RedisResult<()>> { ... } + fn client_unblock<R, S>( + &self, + id: S, + flag: Option<ClientUnblockFlag>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<RedisValue> { ... } + fn unblock_self( + &self, + flag: Option<ClientUnblockFlag>, + ) -> impl Future<Output = RedisResult<()>> { ... } + fn client_tracking<R, T, P>( + &self, + toggle: T, + redirect: Option<i64>, + prefixes: P, + bcast: bool, + optin: bool, + optout: bool, + noloop: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + T: TryInto<Toggle>, + T::Error: Into<RedisError>, + P: Into<MultipleStrings> { ... } + fn client_trackinginfo<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn client_getredir<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn client_caching<R>( + &self, + enabled: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } +
}
Available on crate feature i-client only.
Expand description

Functions that implement the client interface.

+

Provided Methods§

source

fn client_id<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the ID of the current connection.

+

Note: Against a clustered deployment this will return the ID of a random connection. See +connection_ids for more information.

+

https://redis.io/commands/client-id

+
source

fn connection_ids(&self) -> impl Future<Output = HashMap<Server, i64>>

Read the connection IDs for the active connections to each server.

+

The returned map contains each server’s host:port and the result of calling CLIENT ID on the connection.

+

Note: despite being async this function will return cached information from the client if possible.

+
source

fn client_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns information and statistics about the current client connection in a mostly human readable +format.

+

https://redis.io/commands/client-info

+
source

fn client_kill<R>( + &self, + filters: Vec<ClientKillFilter>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Close a given connection or set of connections.

+

https://redis.io/commands/client-kill

+
source

fn client_list<R, I>( + &self, + type: Option<ClientKillType>, + ids: Option<Vec<String>>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT LIST command returns information and statistics about the client connections server in a mostly human +readable format.

+

https://redis.io/commands/client-list

+
source

fn client_getname<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The CLIENT GETNAME returns the name of the current connection as set by CLIENT SETNAME.

+

https://redis.io/commands/client-getname

+
source

fn client_setname<S>(&self, name: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Assign a name to the current connection.

+

Note: The client automatically generates a unique name for each client that is shared by all underlying +connections. Use `self.id() to read the automatically generated name.

+

https://redis.io/commands/client-setname

+
source

fn client_pause( + &self, + timeout: i64, + mode: Option<ClientPauseKind>, +) -> impl Future<Output = RedisResult<()>>

CLIENT PAUSE is a connections control command able to suspend all the Redis clients for the specified amount of +time (in milliseconds).

+

https://redis.io/commands/client-pause

+
source

fn client_unpause(&self) -> impl Future<Output = RedisResult<()>>

CLIENT UNPAUSE is used to resume command processing for all clients that were paused by CLIENT PAUSE.

+

https://redis.io/commands/client-unpause

+
source

fn client_reply( + &self, + flag: ClientReplyFlag, +) -> impl Future<Output = RedisResult<()>>

The CLIENT REPLY command controls whether the server will reply the client’s commands. The following modes are +available:

+

https://redis.io/commands/client-reply

+
source

fn client_unblock<R, S>( + &self, + id: S, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisValue>,

This command can unblock, from a different connection, a client blocked in a blocking operation, such as for +instance BRPOP or XREAD or WAIT.

+

Note: this command is sent on a backchannel connection and will work even when the main connection is blocked.

+

https://redis.io/commands/client-unblock

+
source

fn unblock_self( + &self, + flag: Option<ClientUnblockFlag>, +) -> impl Future<Output = RedisResult<()>>

A convenience function to unblock any blocked connection on this client.

+
source

fn client_tracking<R, T, P>( + &self, + toggle: T, + redirect: Option<i64>, + prefixes: P, + bcast: bool, + optin: bool, + optout: bool, + noloop: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<Toggle>, + T::Error: Into<RedisError>, + P: Into<MultipleStrings>,

Available on crate feature i-tracking only.

This command enables the tracking feature of the Redis server that is used for server assisted client side +caching.

+

https://redis.io/commands/client-tracking/

+

This function is designed to work against a specific server, either via a centralized server config or +with_options. See +crate::interfaces::TrackingInterface::start_tracking for a version that works with all server deployment +modes.

+
source

fn client_trackinginfo<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.

The command returns information about the current client connection’s use of the server assisted client side +caching feature.

+

https://redis.io/commands/client-trackinginfo/

+
source

fn client_getredir<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.

This command returns the client ID we are redirecting our tracking notifications to.

+

https://redis.io/commands/client-getredir/

+
source

fn client_caching<R>( + &self, + enabled: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Available on crate feature i-tracking only.

This command controls the tracking of the keys in the next command executed by the connection, when tracking is +enabled in OPTIN or OPTOUT mode.

+

https://redis.io/commands/client-caching/

+

This function is designed to work against a specific server. See +with_options for a variation that works with all deployment +types.

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.ClientLike.html b/doc/fred/interfaces/trait.ClientLike.html new file mode 100644 index 00000000..684aed31 --- /dev/null +++ b/doc/fred/interfaces/trait.ClientLike.html @@ -0,0 +1,154 @@ +ClientLike in fred::interfaces - Rust

Trait fred::interfaces::ClientLike

source ·
pub trait ClientLike: Clone + Sized {
+
Show 29 methods // Provided methods + fn id(&self) -> &str { ... } + fn client_config(&self) -> RedisConfig { ... } + fn client_reconnect_policy(&self) -> Option<ReconnectPolicy> { ... } + fn connection_config(&self) -> &ConnectionConfig { ... } + fn protocol_version(&self) -> RespVersion { ... } + fn has_reconnect_policy(&self) -> bool { ... } + fn is_pipelined(&self) -> bool { ... } + fn is_clustered(&self) -> bool { ... } + fn uses_sentinels(&self) -> bool { ... } + fn update_perf_config(&self, config: PerformanceConfig) { ... } + fn perf_config(&self) -> PerformanceConfig { ... } + fn state(&self) -> ClientState { ... } + fn is_connected(&self) -> bool { ... } + fn active_connections( + &self, + ) -> impl Future<Output = Result<Vec<Server>, RedisError>> { ... } + fn server_version(&self) -> Option<Version> { ... } + fn set_resolver(&self, resolver: Rc<dyn Resolve>) -> impl Future { ... } + fn connect(&self) -> ConnectHandle { ... } + fn force_reconnection(&self) -> impl Future<Output = RedisResult<()>> { ... } + fn wait_for_connect(&self) -> impl Future<Output = RedisResult<()>> { ... } + fn init(&self) -> impl Future<Output = RedisResult<ConnectHandle>> { ... } + fn quit(&self) -> impl Future<Output = RedisResult<()>> { ... } + fn shutdown( + &self, + flags: Option<ShutdownFlags>, + ) -> impl Future<Output = RedisResult<()>> { ... } + fn flushall<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn flushall_cluster(&self) -> impl Future<Output = RedisResult<()>> { ... } + fn ping<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn info<R>( + &self, + section: Option<InfoKind>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn custom<R, T>( + &self, + cmd: CustomCommand, + args: Vec<T>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + T: TryInto<RedisValue>, + T::Error: Into<RedisError> { ... } + fn custom_raw<T>( + &self, + cmd: CustomCommand, + args: Vec<T>, + ) -> impl Future<Output = RedisResult<Resp3Frame>> + where T: TryInto<RedisValue>, + T::Error: Into<RedisError> { ... } + fn with_options(&self, options: &Options) -> WithOptions<Self> { ... } +
}

Provided Methods§

source

fn id(&self) -> &str

The unique ID identifying this client and underlying connections.

+
source

fn client_config(&self) -> RedisConfig

Read the config used to initialize the client.

+
source

fn client_reconnect_policy(&self) -> Option<ReconnectPolicy>

Read the reconnect policy used to initialize the client.

+
source

fn connection_config(&self) -> &ConnectionConfig

Read the connection config used to initialize the client.

+
source

fn protocol_version(&self) -> RespVersion

Read the RESP version used by the client when communicating with the server.

+
source

fn has_reconnect_policy(&self) -> bool

Whether the client has a reconnection policy.

+
source

fn is_pipelined(&self) -> bool

Whether the client will automatically pipeline commands.

+
source

fn is_clustered(&self) -> bool

Whether the client is connected to a cluster.

+
source

fn uses_sentinels(&self) -> bool

Whether the client uses the sentinel interface.

+
source

fn update_perf_config(&self, config: PerformanceConfig)

Update the internal PerformanceConfig in place with new values.

+
source

fn perf_config(&self) -> PerformanceConfig

Read the PerformanceConfig associated with this client.

+
source

fn state(&self) -> ClientState

Read the state of the underlying connection(s).

+

If running against a cluster the underlying state will reflect the state of the least healthy connection.

+
source

fn is_connected(&self) -> bool

Whether all underlying connections are healthy.

+
source

fn active_connections( + &self, +) -> impl Future<Output = Result<Vec<Server>, RedisError>>

Read the set of active connections managed by the client.

+
source

fn server_version(&self) -> Option<Version>

Read the server version, if known.

+
source

fn set_resolver(&self, resolver: Rc<dyn Resolve>) -> impl Future

Available on crate feature dns only.

Override the DNS resolution logic for the client.

+
source

fn connect(&self) -> ConnectHandle

Connect to the server.

+

This function returns a JoinHandle to a task that drives the connection. It will not resolve until the +connection closes, or if a reconnection policy with unlimited attempts is provided then it will +run until QUIT is called. Callers should avoid calling abort on the returned +JoinHandle unless the client will no longer be used.

+

Calling this function more than once will drop all state associated with the previous connection(s). Any +pending commands on the old connection(s) will either finish or timeout, but they will not be retried on the +new connection(s).

+

See init for an alternative shorthand.

+
source

fn force_reconnection(&self) -> impl Future<Output = RedisResult<()>>

Force a reconnection to the server(s).

+

When running against a cluster this function will also refresh the cached cluster routing table.

+
source

fn wait_for_connect(&self) -> impl Future<Output = RedisResult<()>>

Wait for the result of the next connection attempt.

+

This can be used with on_reconnect to separate initialization logic that needs to occur only on the next +connection attempt vs all subsequent attempts.

+
source

fn init(&self) -> impl Future<Output = RedisResult<ConnectHandle>>

Initialize a new routing and connection task and wait for it to connect successfully.

+

The returned ConnectHandle refers to the task that drives the routing and +connection layer. It will not finish until the max reconnection count is reached. Callers should avoid calling +abort on the returned JoinHandle unless the client will no longer be used.

+

Callers can also use connect and wait_for_connect separately if +needed.

+ +
use fred::prelude::*;
+
+#[tokio::main]
+async fn main() -> Result<(), RedisError> {
+  let client = RedisClient::default();
+  let connection_task = client.init().await?;
+
+  // ...
+
+  client.quit().await?;
+  connection_task.await?
+}
+
source

fn quit(&self) -> impl Future<Output = RedisResult<()>>

Close the connection to the Redis server. The returned future resolves when the command has been written to the +socket, not when the connection has been fully closed. Some time after this future resolves the future +returned by connect will resolve which indicates that the connection has been fully closed.

+

This function will also close all error, pubsub message, and reconnection event streams.

+
source

fn shutdown( + &self, + flags: Option<ShutdownFlags>, +) -> impl Future<Output = RedisResult<()>>

Available on crate feature i-server only.

Shut down the server and quit the client.

+

https://redis.io/commands/shutdown

+
source

fn flushall<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Delete the keys in all databases.

+

https://redis.io/commands/flushall

+
source

fn flushall_cluster(&self) -> impl Future<Output = RedisResult<()>>

Delete the keys on all nodes in the cluster. This is a special function that does not map directly to the Redis +interface.

+
source

fn ping<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Ping the Redis server.

+

https://redis.io/commands/ping

+
source

fn info<R>( + &self, + section: Option<InfoKind>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read info about the server.

+

https://redis.io/commands/info

+
source

fn custom<R, T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command that is not yet supported via another interface on this client. This is most useful when +interacting with third party modules or extensions.

+

Callers should use the re-exported redis_keyslot function to hash the command’s +key, if necessary.

+

This interface should be used with caution as it may break the automatic pipeline features in the client if +command flags are not properly configured.

+
source

fn custom_raw<T>( + &self, + cmd: CustomCommand, + args: Vec<T>, +) -> impl Future<Output = RedisResult<Resp3Frame>>
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

Run a custom command similar to custom, but return the response frame directly without any +parsing.

+

Note: RESP2 frames from the server are automatically converted to the RESP3 format when parsed by the client.

+
source

fn with_options(&self, options: &Options) -> WithOptions<Self>

Customize various configuration options on commands.

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.ClusterInterface.html b/doc/fred/interfaces/trait.ClusterInterface.html new file mode 100644 index 00000000..58681877 --- /dev/null +++ b/doc/fred/interfaces/trait.ClusterInterface.html @@ -0,0 +1,204 @@ +ClusterInterface in fred::interfaces - Rust

Trait fred::interfaces::ClusterInterface

source ·
pub trait ClusterInterface: ClientLike + Sized {
+
Show 24 methods // Provided methods + fn cached_cluster_state(&self) -> Option<ClusterRouting> { ... } + fn num_primary_cluster_nodes(&self) -> usize { ... } + fn sync_cluster(&self) -> impl Future<Output = Result<(), RedisError>> { ... } + fn cluster_bumpepoch<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn cluster_flushslots(&self) -> impl Future<Output = RedisResult<()>> { ... } + fn cluster_myid<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn cluster_nodes<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn cluster_saveconfig(&self) -> impl Future<Output = RedisResult<()>> { ... } + fn cluster_slots<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn cluster_info<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn cluster_add_slots<S>( + &self, + slots: S, + ) -> impl Future<Output = RedisResult<()>> + where S: Into<MultipleHashSlots> { ... } + fn cluster_count_failure_reports<R, S>( + &self, + node_id: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<Str> { ... } + fn cluster_count_keys_in_slot<R>( + &self, + slot: u16, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn cluster_del_slots<S>( + &self, + slots: S, + ) -> impl Future<Output = RedisResult<()>> + where S: Into<MultipleHashSlots> { ... } + fn cluster_failover( + &self, + flag: Option<ClusterFailoverFlag>, + ) -> impl Future<Output = RedisResult<()>> { ... } + fn cluster_forget<S>( + &self, + node_id: S, + ) -> impl Future<Output = RedisResult<()>> + where S: Into<Str> { ... } + fn cluster_get_keys_in_slot<R>( + &self, + slot: u16, + count: u64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn cluster_keyslot<R, K>( + &self, + key: K, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn cluster_meet<S>( + &self, + ip: S, + port: u16, + ) -> impl Future<Output = RedisResult<()>> + where S: Into<Str> { ... } + fn cluster_replicate<S>( + &self, + node_id: S, + ) -> impl Future<Output = RedisResult<()>> + where S: Into<Str> { ... } + fn cluster_replicas<R, S>( + &self, + node_id: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<Str> { ... } + fn cluster_reset( + &self, + mode: Option<ClusterResetFlag>, + ) -> impl Future<Output = RedisResult<()>> { ... } + fn cluster_set_config_epoch( + &self, + epoch: u64, + ) -> impl Future<Output = RedisResult<()>> { ... } + fn cluster_setslot( + &self, + slot: u16, + state: ClusterSetSlotState, + ) -> impl Future<Output = RedisResult<()>> { ... } +
}
Available on crate feature i-cluster only.
Expand description

Functions that implement the cluster interface.

+

Provided Methods§

source

fn cached_cluster_state(&self) -> Option<ClusterRouting>

Read the cached cluster state used for routing commands to the correct cluster nodes.

+
source

fn num_primary_cluster_nodes(&self) -> usize

Read the number of known primary cluster nodes, or 0 if the cluster state is not known.

+
source

fn sync_cluster(&self) -> impl Future<Output = Result<(), RedisError>>

Update the cached cluster state and add or remove any changed cluster node connections.

+
source

fn cluster_bumpepoch<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Advances the cluster config epoch.

+

https://redis.io/commands/cluster-bumpepoch

+
source

fn cluster_flushslots(&self) -> impl Future<Output = RedisResult<()>>

Deletes all slots from a node.

+

https://redis.io/commands/cluster-flushslots

+
source

fn cluster_myid<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the node’s id.

+

https://redis.io/commands/cluster-myid

+
source

fn cluster_nodes<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Read the current cluster node configuration.

+

Note: The client keeps a cached, parsed version of the cluster state in memory available at +cached_cluster_state.

+

https://redis.io/commands/cluster-nodes

+
source

fn cluster_saveconfig(&self) -> impl Future<Output = RedisResult<()>>

Forces a node to save the nodes.conf configuration on disk.

+

https://redis.io/commands/cluster-saveconfig

+
source

fn cluster_slots<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER SLOTS returns details about which cluster slots map to which Redis instances.

+

https://redis.io/commands/cluster-slots

+
source

fn cluster_info<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

CLUSTER INFO provides INFO style information about Redis Cluster vital parameters.

+

https://redis.io/commands/cluster-info

+
source

fn cluster_add_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

This command is useful in order to modify a node’s view of the cluster configuration. Specifically it assigns a +set of hash slots to the node receiving the command.

+

https://redis.io/commands/cluster-addslots

+
source

fn cluster_count_failure_reports<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command returns the number of failure reports for the specified node.

+

https://redis.io/commands/cluster-count-failure-reports

+
source

fn cluster_count_keys_in_slot<R>( + &self, + slot: u16, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of keys in the specified Redis Cluster hash slot.

+

https://redis.io/commands/cluster-countkeysinslot

+
source

fn cluster_del_slots<S>( + &self, + slots: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleHashSlots>,

The CLUSTER DELSLOTS command asks a particular Redis Cluster node to forget which master is serving the hash +slots specified as arguments.

+

https://redis.io/commands/cluster-delslots

+
source

fn cluster_failover( + &self, + flag: Option<ClusterFailoverFlag>, +) -> impl Future<Output = RedisResult<()>>

This command, that can only be sent to a Redis Cluster replica node, forces the replica to start a manual +failover of its master instance.

+

https://redis.io/commands/cluster-failover

+
source

fn cluster_forget<S>(&self, node_id: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command is used in order to remove a node, specified via its node ID, from the set of known nodes of the +Redis Cluster node receiving the command. In other words the specified node is removed from the nodes table of +the node receiving the command.

+

https://redis.io/commands/cluster-forget

+
source

fn cluster_get_keys_in_slot<R>( + &self, + slot: u16, + count: u64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The command returns an array of keys names stored in the contacted node and hashing to the specified hash slot.

+

https://redis.io/commands/cluster-getkeysinslot

+
source

fn cluster_keyslot<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns an integer identifying the hash slot the specified key hashes to.

+

https://redis.io/commands/cluster-keyslot

+
source

fn cluster_meet<S>( + &self, + ip: S, + port: u16, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

CLUSTER MEET is used in order to connect different Redis nodes with cluster support enabled, into a working +cluster.

+

https://redis.io/commands/cluster-meet

+
source

fn cluster_replicate<S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

The command reconfigures a node as a replica of the specified master. If the node receiving the command is an +empty master, as a side effect of the command, the node role is changed from master to replica.

+

https://redis.io/commands/cluster-replicate

+
source

fn cluster_replicas<R, S>( + &self, + node_id: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The command provides a list of replica nodes replicating from the specified master node.

+

https://redis.io/commands/cluster-replicas

+
source

fn cluster_reset( + &self, + mode: Option<ClusterResetFlag>, +) -> impl Future<Output = RedisResult<()>>

Reset a Redis Cluster node, in a more or less drastic way depending on the reset type, that can be hard or soft. +Note that this command does not work for masters if they hold one or more keys, in that case to completely +reset a master node keys must be removed first, e.g. by using FLUSHALL first, and then CLUSTER RESET.

+

https://redis.io/commands/cluster-reset

+
source

fn cluster_set_config_epoch( + &self, + epoch: u64, +) -> impl Future<Output = RedisResult<()>>

This command sets a specific config epoch in a fresh node.

+

https://redis.io/commands/cluster-set-config-epoch

+
source

fn cluster_setslot( + &self, + slot: u16, + state: ClusterSetSlotState, +) -> impl Future<Output = RedisResult<()>>

CLUSTER SETSLOT is responsible for changing the state of a hash slot in the receiving node in different ways.

+

https://redis.io/commands/cluster-setslot

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.ConfigInterface.html b/doc/fred/interfaces/trait.ConfigInterface.html new file mode 100644 index 00000000..7a6ef1f9 --- /dev/null +++ b/doc/fred/interfaces/trait.ConfigInterface.html @@ -0,0 +1,39 @@ +ConfigInterface in fred::interfaces - Rust

Trait fred::interfaces::ConfigInterface

source ·
pub trait ConfigInterface: ClientLike + Sized {
+    // Provided methods
+    fn config_resetstat(&self) -> impl Future<Output = RedisResult<()>> { ... }
+    fn config_rewrite(&self) -> impl Future<Output = RedisResult<()>> { ... }
+    fn config_get<R, S>(
+        &self,
+        parameter: S,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             S: Into<Str> { ... }
+    fn config_set<P, V>(
+        &self,
+        parameter: P,
+        value: V,
+    ) -> impl Future<Output = RedisResult<()>>
+       where P: Into<Str>,
+             V: TryInto<RedisValue>,
+             V::Error: Into<RedisError> { ... }
+}
Available on crate feature i-config only.
Expand description

Functions that implement the config interface.

+

Provided Methods§

source

fn config_resetstat(&self) -> impl Future<Output = RedisResult<()>>

Resets the statistics reported by Redis using the INFO command.

+

https://redis.io/commands/config-resetstat

+
source

fn config_rewrite(&self) -> impl Future<Output = RedisResult<()>>

The CONFIG REWRITE command rewrites the redis.conf file the server was started with, applying the minimal +changes needed to make it reflect the configuration currently used by the server, which may be different +compared to the original one because of the use of the CONFIG SET command.

+

https://redis.io/commands/config-rewrite

+
source

fn config_get<R, S>(&self, parameter: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

The CONFIG GET command is used to read the configuration parameters of a running Redis server.

+

https://redis.io/commands/config-get

+
source

fn config_set<P, V>( + &self, + parameter: P, + value: V, +) -> impl Future<Output = RedisResult<()>>
where + P: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The CONFIG SET command is used in order to reconfigure the server at run time without the need to restart Redis.

+

https://redis.io/commands/config-set

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.EventInterface.html b/doc/fred/interfaces/trait.EventInterface.html new file mode 100644 index 00000000..ce1f4af2 --- /dev/null +++ b/doc/fred/interfaces/trait.EventInterface.html @@ -0,0 +1,78 @@ +EventInterface in fred::interfaces - Rust

Trait fred::interfaces::EventInterface

source ·
pub trait EventInterface: ClientLike {
+
Show 13 methods // Provided methods + fn on_message<F>(&self, func: F) -> JoinHandle<RedisResult<()>> + where F: Fn(Message) -> RedisResult<()> + 'static { ... } + fn on_keyspace_event<F>(&self, func: F) -> JoinHandle<RedisResult<()>> + where F: Fn(KeyspaceEvent) -> RedisResult<()> + 'static { ... } + fn on_reconnect<F>(&self, func: F) -> JoinHandle<RedisResult<()>> + where F: Fn(Server) -> RedisResult<()> + 'static { ... } + fn on_cluster_change<F>(&self, func: F) -> JoinHandle<RedisResult<()>> + where F: Fn(Vec<ClusterStateChange>) -> RedisResult<()> + 'static { ... } + fn on_error<F>(&self, func: F) -> JoinHandle<RedisResult<()>> + where F: Fn(RedisError) -> RedisResult<()> + 'static { ... } + fn on_unresponsive<F>(&self, func: F) -> JoinHandle<RedisResult<()>> + where F: Fn(Server) -> RedisResult<()> + 'static { ... } + fn on_any<Fe, Fr, Fc>( + &self, + error_fn: Fe, + reconnect_fn: Fr, + cluster_change_fn: Fc, + ) -> JoinHandle<RedisResult<()>> + where Fe: Fn(RedisError) -> RedisResult<()> + 'static, + Fr: Fn(Server) -> RedisResult<()> + 'static, + Fc: Fn(Vec<ClusterStateChange>) -> RedisResult<()> + 'static { ... } + fn message_rx(&self) -> BroadcastReceiver<Message> { ... } + fn keyspace_event_rx(&self) -> BroadcastReceiver<KeyspaceEvent> { ... } + fn reconnect_rx(&self) -> BroadcastReceiver<Server> { ... } + fn cluster_change_rx(&self) -> BroadcastReceiver<Vec<ClusterStateChange>> { ... } + fn error_rx(&self) -> BroadcastReceiver<RedisError> { ... } + fn unresponsive_rx(&self) -> BroadcastReceiver<Server> { ... } +
}
Expand description

An interface that exposes various client and connection events.

+

Calling quit will close all event streams.

+

Provided Methods§

source

fn on_message<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Message) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each publish-subscribe message.

+

See message_rx for more information.

+
source

fn on_keyspace_event<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(KeyspaceEvent) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each keyspace event.

+

https://redis.io/topics/notifications

+
source

fn on_reconnect<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Server) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each reconnection event.

+

Errors returned by func will exit the task.

+
source

fn on_cluster_change<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Vec<ClusterStateChange>) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each cluster change event.

+

Errors returned by func will exit the task.

+
source

fn on_error<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(RedisError) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function on each connection error event.

+

Errors returned by func will exit the task.

+
source

fn on_unresponsive<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Server) -> RedisResult<()> + 'static,

Spawn a task that runs the provided function whenever the client detects an unresponsive connection.

+
source

fn on_any<Fe, Fr, Fc>( + &self, + error_fn: Fe, + reconnect_fn: Fr, + cluster_change_fn: Fc, +) -> JoinHandle<RedisResult<()>>
where + Fe: Fn(RedisError) -> RedisResult<()> + 'static, + Fr: Fn(Server) -> RedisResult<()> + 'static, + Fc: Fn(Vec<ClusterStateChange>) -> RedisResult<()> + 'static,

Spawn one task that listens for all connection management event types.

+

Errors in any of the provided functions will exit the task.

+
source

fn message_rx(&self) -> BroadcastReceiver<Message>

Listen for messages on the publish-subscribe interface.

+

Keyspace events are not sent on this interface.

+

If the connection to the Redis server closes for any reason this function does not need to be called again. +Messages will start appearing on the original stream after +subscribe is called again.

+
source

fn keyspace_event_rx(&self) -> BroadcastReceiver<KeyspaceEvent>

Listen for keyspace and keyevent notifications on the publish-subscribe interface.

+

Callers still need to configure the server and subscribe to the relevant channels, but this interface will +parse and format the messages automatically.

+

https://redis.io/topics/notifications

+
source

fn reconnect_rx(&self) -> BroadcastReceiver<Server>

Listen for reconnection notifications.

+

This function can be used to receive notifications whenever the client reconnects in order to +re-subscribe to channels, etc.

+

A reconnection event is also triggered upon first connecting to the server.

+
source

fn cluster_change_rx(&self) -> BroadcastReceiver<Vec<ClusterStateChange>>

Listen for notifications whenever the cluster state changes.

+

This is usually triggered in response to a MOVED error, but can also happen when connections close +unexpectedly.

+
source

fn error_rx(&self) -> BroadcastReceiver<RedisError>

Listen for protocol and connection errors. This stream can be used to more intelligently handle errors that may +not appear in the request-response cycle, and so cannot be handled by response futures.

+
source

fn unresponsive_rx(&self) -> BroadcastReceiver<Server>

Receive a message when the client initiates a reconnection after detecting an unresponsive connection.

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.FunctionInterface.html b/doc/fred/interfaces/trait.FunctionInterface.html new file mode 100644 index 00000000..c3be7e97 --- /dev/null +++ b/doc/fred/interfaces/trait.FunctionInterface.html @@ -0,0 +1,193 @@ +FunctionInterface in fred::interfaces - Rust

Trait fred::interfaces::FunctionInterface

source ·
pub trait FunctionInterface: ClientLike + Sized {
+
Show 14 methods // Provided methods + fn fcall<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError> { ... } + fn fcall_ro<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError> { ... } + fn function_delete<R, S>( + &self, + library_name: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<Str> { ... } + fn function_delete_cluster<S>( + &self, + library_name: S, + ) -> impl Future<Output = RedisResult<()>> + where S: Into<Str> { ... } + fn function_dump<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn function_flush<R>( + &self, + async: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn function_flush_cluster( + &self, + async: bool, + ) -> impl Future<Output = RedisResult<()>> { ... } + fn function_kill<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn function_list<R, S>( + &self, + library_name: Option<S>, + withcode: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<Str> { ... } + fn function_load<R, S>( + &self, + replace: bool, + code: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<Str> { ... } + fn function_load_cluster<R, S>( + &self, + replace: bool, + code: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<Str> { ... } + fn function_restore<R, B, P>( + &self, + serialized: B, + policy: P, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError> { ... } + fn function_restore_cluster<B, P>( + &self, + serialized: B, + policy: P, + ) -> impl Future<Output = RedisResult<()>> + where B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError> { ... } + fn function_stats<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } +
}
Available on crate feature i-scripts only.
Expand description

Functions that implement the function interface.

+

Provided Methods§

source

fn fcall<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Invoke a function.

+

https://redis.io/commands/fcall/

+
source

fn fcall_ro<R, F, K, V>( + &self, + func: F, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

This is a read-only variant of the FCALL command that cannot execute commands that modify data.

+

https://redis.io/commands/fcall_ro/

+
source

fn function_delete<R, S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Delete a library and all its functions.

+

https://redis.io/commands/function-delete/

+
source

fn function_delete_cluster<S>( + &self, + library_name: S, +) -> impl Future<Output = RedisResult<()>>
where + S: Into<Str>,

Delete a library and all its functions from each cluster node concurrently.

+

https://redis.io/commands/function-delete/

+
source

fn function_dump<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the serialized payload of loaded libraries.

+

https://redis.io/commands/function-dump/

+
source

fn function_flush<R>(&self, async: bool) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Deletes all the libraries.

+

https://redis.io/commands/function-flush/

+
source

fn function_flush_cluster( + &self, + async: bool, +) -> impl Future<Output = RedisResult<()>>

Deletes all the libraries on all cluster nodes concurrently.

+

https://redis.io/commands/function-flush/

+
source

fn function_kill<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Kill a function that is currently executing.

+

Note: This command runs on a backchannel connection to the server in order to take effect as quickly as +possible.

+

https://redis.io/commands/function-kill/

+
source

fn function_list<R, S>( + &self, + library_name: Option<S>, + withcode: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Return information about the functions and libraries.

+

https://redis.io/commands/function-list/

+
source

fn function_load<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis.

+

https://redis.io/commands/function-load/

+
source

fn function_load_cluster<R, S>( + &self, + replace: bool, + code: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a library to Redis on all cluster nodes concurrently.

+

https://redis.io/commands/function-load/

+
source

fn function_restore<R, B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload.

+

https://redis.io/commands/function-restore/

+

Note: Use FnPolicy::default() to use the default function restore policy ("APPEND").

+
source

fn function_restore_cluster<B, P>( + &self, + serialized: B, + policy: P, +) -> impl Future<Output = RedisResult<()>>
where + B: Into<Bytes>, + P: TryInto<FnPolicy>, + P::Error: Into<RedisError>,

Restore libraries from the serialized payload on all cluster nodes concurrently.

+

https://redis.io/commands/function-restore/

+

Note: Use FnPolicy::default() to use the default function restore policy ("APPEND").

+
source

fn function_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return information about the function that’s currently running and information about the available execution +engines.

+

Note: This command runs on a backchannel connection to the server.

+

https://redis.io/commands/function-stats/

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.GeoInterface.html b/doc/fred/interfaces/trait.GeoInterface.html new file mode 100644 index 00000000..10268ce3 --- /dev/null +++ b/doc/fred/interfaces/trait.GeoInterface.html @@ -0,0 +1,232 @@ +GeoInterface in fred::interfaces - Rust

Trait fred::interfaces::GeoInterface

source ·
pub trait GeoInterface: ClientLike + Sized {
+    // Provided methods
+    fn geoadd<R, K, V>(
+        &self,
+        key: K,
+        options: Option<SetOptions>,
+        changed: bool,
+        values: V,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             K: Into<RedisKey>,
+             V: Into<MultipleGeoValues> { ... }
+    fn geohash<R, K, V>(
+        &self,
+        key: K,
+        members: V,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             K: Into<RedisKey>,
+             V: TryInto<MultipleValues>,
+             V::Error: Into<RedisError> { ... }
+    fn geopos<R, K, V>(
+        &self,
+        key: K,
+        members: V,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             K: Into<RedisKey>,
+             V: TryInto<MultipleValues>,
+             V::Error: Into<RedisError> { ... }
+    fn geodist<R, K, S, D>(
+        &self,
+        key: K,
+        src: S,
+        dest: D,
+        unit: Option<GeoUnit>,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             K: Into<RedisKey>,
+             S: TryInto<RedisValue>,
+             S::Error: Into<RedisError>,
+             D: TryInto<RedisValue>,
+             D::Error: Into<RedisError> { ... }
+    fn georadius<R, K, P>(
+        &self,
+        key: K,
+        position: P,
+        radius: f64,
+        unit: GeoUnit,
+        withcoord: bool,
+        withdist: bool,
+        withhash: bool,
+        count: Option<(u64, Any)>,
+        ord: Option<SortOrder>,
+        store: Option<RedisKey>,
+        storedist: Option<RedisKey>,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             K: Into<RedisKey>,
+             P: Into<GeoPosition> { ... }
+    fn georadiusbymember<R, K, V>(
+        &self,
+        key: K,
+        member: V,
+        radius: f64,
+        unit: GeoUnit,
+        withcoord: bool,
+        withdist: bool,
+        withhash: bool,
+        count: Option<(u64, Any)>,
+        ord: Option<SortOrder>,
+        store: Option<RedisKey>,
+        storedist: Option<RedisKey>,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             K: Into<RedisKey>,
+             V: TryInto<RedisValue>,
+             V::Error: Into<RedisError> { ... }
+    fn geosearch<R, K>(
+        &self,
+        key: K,
+        from_member: Option<RedisValue>,
+        from_lonlat: Option<GeoPosition>,
+        by_radius: Option<(f64, GeoUnit)>,
+        by_box: Option<(f64, f64, GeoUnit)>,
+        ord: Option<SortOrder>,
+        count: Option<(u64, Any)>,
+        withcoord: bool,
+        withdist: bool,
+        withhash: bool,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             K: Into<RedisKey> { ... }
+    fn geosearchstore<R, D, S>(
+        &self,
+        dest: D,
+        source: S,
+        from_member: Option<RedisValue>,
+        from_lonlat: Option<GeoPosition>,
+        by_radius: Option<(f64, GeoUnit)>,
+        by_box: Option<(f64, f64, GeoUnit)>,
+        ord: Option<SortOrder>,
+        count: Option<(u64, Any)>,
+        storedist: bool,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             D: Into<RedisKey>,
+             S: Into<RedisKey> { ... }
+}
Available on crate feature i-geo only.
Expand description

Functions that implement the geo interface.

+

Provided Methods§

source

fn geoadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + changed: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: Into<MultipleGeoValues>,

Adds the specified geospatial items (longitude, latitude, name) to the specified key.

+

https://redis.io/commands/geoadd

+
source

fn geohash<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return valid Geohash strings representing the position of one or more elements in a sorted set value +representing a geospatial index (where elements were added using GEOADD).

+

https://redis.io/commands/geohash

+
source

fn geopos<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by +the sorted set at key.

+

Callers can use as_geo_position to lazily parse results as needed.

+

https://redis.io/commands/geopos

+
source

fn geodist<R, K, S, D>( + &self, + key: K, + src: S, + dest: D, + unit: Option<GeoUnit>, +) -> impl Future<Output = RedisResult<R>>

Return the distance between two members in the geospatial index represented by the sorted set.

+

https://redis.io/commands/geodist

+
source

fn georadius<R, K, P>( + &self, + key: K, + position: P, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<GeoPosition>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified with the center location and the maximum distance from the center (the radius).

+

https://redis.io/commands/georadius

+
source

fn georadiusbymember<R, K, V>( + &self, + key: K, + member: V, + radius: f64, + unit: GeoUnit, + withcoord: bool, + withdist: bool, + withhash: bool, + count: Option<(u64, Any)>, + ord: Option<SortOrder>, + store: Option<RedisKey>, + storedist: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

This command is exactly like GEORADIUS with the sole difference that instead of taking, as the center of the +area to query, a longitude and latitude value, it takes the name of a member already existing inside the +geospatial index represented by the sorted set.

+

https://redis.io/commands/georadiusbymember

+
source

fn geosearch<R, K>( + &self, + key: K, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + withcoord: bool, + withdist: bool, + withhash: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return the members of a sorted set populated with geospatial information using GEOADD, which are within the +borders of the area specified by a given shape.

+

https://redis.io/commands/geosearch

+
source

fn geosearchstore<R, D, S>( + &self, + dest: D, + source: S, + from_member: Option<RedisValue>, + from_lonlat: Option<GeoPosition>, + by_radius: Option<(f64, GeoUnit)>, + by_box: Option<(f64, f64, GeoUnit)>, + ord: Option<SortOrder>, + count: Option<(u64, Any)>, + storedist: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>,

This command is like GEOSEARCH, but stores the result in destination key. Returns the number of members added to +the destination key.

+

https://redis.io/commands/geosearchstore

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.HashesInterface.html b/doc/fred/interfaces/trait.HashesInterface.html new file mode 100644 index 00000000..3609cb9d --- /dev/null +++ b/doc/fred/interfaces/trait.HashesInterface.html @@ -0,0 +1,233 @@ +HashesInterface in fred::interfaces - Rust

Trait fred::interfaces::HashesInterface

source ·
pub trait HashesInterface: ClientLike + Sized {
+
Show 15 methods // Provided methods + fn hgetall<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn hdel<R, K, F>( + &self, + key: K, + fields: F, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys> { ... } + fn hexists<R, K, F>( + &self, + key: K, + field: F, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey> { ... } + fn hget<R, K, F>( + &self, + key: K, + field: F, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey> { ... } + fn hincrby<R, K, F>( + &self, + key: K, + field: F, + increment: i64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey> { ... } + fn hincrbyfloat<R, K, F>( + &self, + key: K, + field: F, + increment: f64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey> { ... } + fn hkeys<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn hlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn hmget<R, K, F>( + &self, + key: K, + fields: F, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys> { ... } + fn hmset<R, K, V>( + &self, + key: K, + values: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError> { ... } + fn hset<R, K, V>( + &self, + key: K, + values: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError> { ... } + fn hsetnx<R, K, F, V>( + &self, + key: K, + field: F, + value: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn hrandfield<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn hstrlen<R, K, F>( + &self, + key: K, + field: F, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey> { ... } + fn hvals<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } +
}
Available on crate feature i-hashes only.
Expand description

Functions that implement the hashes interface.

+

Provided Methods§

source

fn hgetall<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all fields and values of the hash stored at key.

+

https://redis.io/commands/hgetall

+
source

fn hdel<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Removes the specified fields from the hash stored at key.

+

https://redis.io/commands/hdel

+
source

fn hexists<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns if field is an existing field in the hash stored at key.

+

https://redis.io/commands/hexists

+
source

fn hget<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the value associated with field in the hash stored at key.

+

https://redis.io/commands/hget

+
source

fn hincrby<R, K, F>( + &self, + key: K, + field: F, + increment: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increments the number stored at field in the hash stored at key by increment.

+

https://redis.io/commands/hincrby

+
source

fn hincrbyfloat<R, K, F>( + &self, + key: K, + field: F, + increment: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Increment the specified field of a hash stored at key, and representing a floating point number, by the +specified increment.

+

https://redis.io/commands/hincrbyfloat

+
source

fn hkeys<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all field names in the hash stored at key.

+

https://redis.io/commands/hkeys

+
source

fn hlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of fields contained in the hash stored at key.

+

https://redis.io/commands/hlen

+
source

fn hmget<R, K, F>( + &self, + key: K, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<MultipleKeys>,

Returns the values associated with the specified fields in the hash stored at key.

+

https://redis.io/commands/hmget

+
source

fn hmset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the specified fields to their respective values in the hash stored at key.

+

https://redis.io/commands/hmset

+
source

fn hset<R, K, V>( + &self, + key: K, + values: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets fields in the hash stored at key to their provided values.

+

https://redis.io/commands/hset

+
source

fn hsetnx<R, K, F, V>( + &self, + key: K, + field: F, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets field in the hash stored at key to value, only if field does not yet exist.

+

https://redis.io/commands/hsetnx

+
source

fn hrandfield<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random field from the hash value stored at key.

+

If the provided count argument is positive, return an array of distinct fields.

+

https://redis.io/commands/hrandfield

+
source

fn hstrlen<R, K, F>( + &self, + key: K, + field: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: Into<RedisKey>,

Returns the string length of the value associated with field in the hash stored at key.

+

https://redis.io/commands/hstrlen

+
source

fn hvals<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all values in the hash stored at key.

+

https://redis.io/commands/hvals

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.HeartbeatInterface.html b/doc/fred/interfaces/trait.HeartbeatInterface.html new file mode 100644 index 00000000..8f35de60 --- /dev/null +++ b/doc/fred/interfaces/trait.HeartbeatInterface.html @@ -0,0 +1,14 @@ +HeartbeatInterface in fred::interfaces - Rust

Trait fred::interfaces::HeartbeatInterface

source ·
pub trait HeartbeatInterface: ClientLike {
+    // Provided method
+    fn enable_heartbeat(
+        &self,
+        interval: Duration,
+        break_on_error: bool,
+    ) -> impl Future<Output = RedisResult<()>> { ... }
+}
Expand description

Functions that provide a connection heartbeat interface.

+

Provided Methods§

source

fn enable_heartbeat( + &self, + interval: Duration, + break_on_error: bool, +) -> impl Future<Output = RedisResult<()>>

Return a future that will ping the server on an interval.

+

Object Safety§

This trait is not object safe.

Implementors§

source§

impl HeartbeatInterface for RedisClient

Available on crate feature i-server only.
source§

impl HeartbeatInterface for RedisPool

source§

impl HeartbeatInterface for SentinelClient

Available on crate feature i-server only.
source§

impl HeartbeatInterface for SubscriberClient

Available on crate feature i-server only.
\ No newline at end of file diff --git a/doc/fred/interfaces/trait.HyperloglogInterface.html b/doc/fred/interfaces/trait.HyperloglogInterface.html new file mode 100644 index 00000000..2efcd585 --- /dev/null +++ b/doc/fred/interfaces/trait.HyperloglogInterface.html @@ -0,0 +1,52 @@ +HyperloglogInterface in fred::interfaces - Rust

Trait fred::interfaces::HyperloglogInterface

source ·
pub trait HyperloglogInterface: ClientLike + Sized {
+    // Provided methods
+    fn pfadd<R, K, V>(
+        &self,
+        key: K,
+        elements: V,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             K: Into<RedisKey>,
+             V: TryInto<MultipleValues>,
+             V::Error: Into<RedisError> { ... }
+    fn pfcount<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             K: Into<MultipleKeys> { ... }
+    fn pfmerge<R, D, S>(
+        &self,
+        dest: D,
+        sources: S,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             D: Into<RedisKey>,
+             S: Into<MultipleKeys> { ... }
+}
Available on crate feature i-hyperloglog only.
Expand description

Functions that implement the HyperLogLog interface.

+

Provided Methods§

source

fn pfadd<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Adds all the element arguments to the HyperLogLog data structure stored at the variable name specified as first +argument.

+

https://redis.io/commands/pfadd

+
source

fn pfcount<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

When called with a single key, returns the approximated cardinality computed by the HyperLogLog data structure +stored at the specified variable, which is 0 if the variable does not exist.

+

When called with multiple keys, returns the approximated cardinality of the union of the HyperLogLogs passed, by +internally merging the HyperLogLogs stored at the provided keys into a temporary HyperLogLog.

+

https://redis.io/commands/pfcount

+
source

fn pfmerge<R, D, S>( + &self, + dest: D, + sources: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<MultipleKeys>,

Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of the +observed sets of the source HyperLogLog structures.

+

https://redis.io/commands/pfmerge

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.KeysInterface.html b/doc/fred/interfaces/trait.KeysInterface.html new file mode 100644 index 00000000..c7d653af --- /dev/null +++ b/doc/fred/interfaces/trait.KeysInterface.html @@ -0,0 +1,465 @@ +KeysInterface in fred::interfaces - Rust

Trait fred::interfaces::KeysInterface

source ·
pub trait KeysInterface: ClientLike + Sized {
+
Show 35 methods // Provided methods + fn watch<K>(&self, keys: K) -> impl Future<Output = RedisResult<()>> + where K: Into<MultipleKeys> { ... } + fn unwatch(&self) -> impl Future<Output = RedisResult<()>> { ... } + fn randomkey<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn copy<R, S, D>( + &self, + source: S, + destination: D, + db: Option<u8>, + replace: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey> { ... } + fn dump<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn restore<R, K>( + &self, + key: K, + ttl: i64, + serialized: RedisValue, + replace: bool, + absttl: bool, + idletime: Option<i64>, + frequency: Option<i64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn set<R, K, V>( + &self, + key: K, + value: V, + expire: Option<Expiration>, + options: Option<SetOptions>, + get: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn get<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn getrange<R, K>( + &self, + key: K, + start: usize, + end: usize, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn setrange<R, K, V>( + &self, + key: K, + offset: u32, + value: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn getset<R, K, V>( + &self, + key: K, + value: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn getdel<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn strlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn del<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn unlink<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn rename<R, S, D>( + &self, + source: S, + destination: D, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey> { ... } + fn renamenx<R, S, D>( + &self, + source: S, + destination: D, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey> { ... } + fn append<R, K, V>( + &self, + key: K, + value: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn mget<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn mset<V>(&self, values: V) -> impl Future<Output = RedisResult<()>> + where V: TryInto<RedisMap>, + V::Error: Into<RedisError> { ... } + fn msetnx<R, V>(&self, values: V) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + V: TryInto<RedisMap>, + V::Error: Into<RedisError> { ... } + fn incr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn incr_by<R, K>( + &self, + key: K, + val: i64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn incr_by_float<R, K>( + &self, + key: K, + val: f64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn decr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn decr_by<R, K>( + &self, + key: K, + val: i64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn ttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn pttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn persist<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn expire<R, K>( + &self, + key: K, + seconds: i64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn expire_at<R, K>( + &self, + key: K, + timestamp: i64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn pexpire<R, K>( + &self, + key: K, + milliseconds: i64, + options: Option<ExpireOptions>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn pexpire_at<R, K>( + &self, + key: K, + timestamp: i64, + options: Option<ExpireOptions>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn exists<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn lcs<R, K1, K2>( + &self, + key1: K1, + key2: K2, + len: bool, + idx: bool, + minmatchlen: Option<i64>, + withmatchlen: bool, + ) -> impl Future<Output = Result<R, RedisError>> + where R: FromRedis, + K1: Into<RedisKey>, + K2: Into<RedisKey> { ... } +
}
Available on crate feature i-keys only.
Expand description

Functions that implement the generic keys interface.

+

Provided Methods§

source

fn watch<K>(&self, keys: K) -> impl Future<Output = RedisResult<()>>
where + K: Into<MultipleKeys>,

Marks the given keys to be watched for conditional execution of a transaction.

+

https://redis.io/commands/watch

+
source

fn unwatch(&self) -> impl Future<Output = RedisResult<()>>

Flushes all the previously watched keys for a transaction.

+

https://redis.io/commands/unwatch

+
source

fn randomkey<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return a random key from the currently selected database.

+

https://redis.io/commands/randomkey

+
source

fn copy<R, S, D>( + &self, + source: S, + destination: D, + db: Option<u8>, + replace: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

This command copies the value stored at the source key to the destination key.

+

https://redis.io/commands/copy

+
source

fn dump<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Serialize the value stored at key in a Redis-specific format and return it as bulk string.

+

https://redis.io/commands/dump

+
source

fn restore<R, K>( + &self, + key: K, + ttl: i64, + serialized: RedisValue, + replace: bool, + absttl: bool, + idletime: Option<i64>, + frequency: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Create a key associated with a value that is obtained by deserializing the provided serialized value

+

https://redis.io/commands/restore

+
source

fn set<R, K, V>( + &self, + key: K, + value: V, + expire: Option<Expiration>, + options: Option<SetOptions>, + get: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set a value with optional NX|XX, EX|PX|EXAT|PXAT|KEEPTTL, and GET arguments.

+

Note: the get flag was added in 6.2.0. Setting it as false works with Redis versions <=6.2.0.

+

https://redis.io/commands/set

+
source

fn get<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Read a value from the server.

+

https://redis.io/commands/get

+
source

fn getrange<R, K>( + &self, + key: K, + start: usize, + end: usize, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the substring of the string value stored at key with offsets start and end (both inclusive).

+

Note: Command formerly called SUBSTR in Redis verison <=2.0.

+

https://redis.io/commands/getrange

+
source

fn setrange<R, K, V>( + &self, + key: K, + offset: u32, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Overwrites part of the string stored at key, starting at the specified offset, for the entire length of +value.

+

https://redis.io/commands/setrange

+
source

fn getset<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Atomically sets key to value and returns the old value stored at key.

+

Returns an error if key does not hold string value. Returns nil if key does not exist.

+

https://redis.io/commands/getset

+
source

fn getdel<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the value of key and delete the key. This command is similar to GET, except for the fact that it also +deletes the key on success (if and only if the key’s value type is a string).

+

https://redis.io/commands/getdel

+
source

fn strlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the string value stored at key. An error is returned when key holds a non-string value.

+

https://redis.io/commands/strlen

+
source

fn del<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Removes the specified keys. A key is ignored if it does not exist.

+

Returns the number of keys removed.

+

https://redis.io/commands/del

+

Unlinks the specified keys. A key is ignored if it does not exist

+

Returns the number of keys removed.

+

https://redis.io/commands/del

+
source

fn rename<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination.

+

Returns an error when source does not exist. If destination exists, it gets overwritten.

+

https://redis.io/commands/rename

+
source

fn renamenx<R, S, D>( + &self, + source: S, + destination: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Renames source key to destination if destination does not yet exist.

+

Returns an error when source does not exist.

+

https://redis.io/commands/renamenx

+
source

fn append<R, K, V>( + &self, + key: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Append value to key if it’s a string.

+

https://redis.io/commands/append/

+
source

fn mget<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the +special value nil is returned.

+

https://redis.io/commands/mget

+
source

fn mset<V>(&self, values: V) -> impl Future<Output = RedisResult<()>>
where + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values.

+

https://redis.io/commands/mset

+
source

fn msetnx<R, V>(&self, values: V) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Sets the given keys to their respective values. MSETNX will not perform any operation at all even if just a +single key already exists.

+

https://redis.io/commands/msetnx

+
source

fn incr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation.

+

Returns an error if the value at key is of the wrong type.

+

https://redis.io/commands/incr

+
source

fn incr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increments the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation.

+

Returns an error if the value at key is of the wrong type.

+

https://redis.io/commands/incrby

+
source

fn incr_by_float<R, K>( + &self, + key: K, + val: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Increment the string representing a floating point number stored at key by val. If the key does not exist, it +is set to 0 before performing the operation.

+

Returns an error if key value is the wrong type or if the current value cannot be parsed as a floating point +value.

+

https://redis.io/commands/incrbyfloat

+
source

fn decr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by one. If the key does not exist, it is set to 0 before performing the +operation.

+

Returns an error if the key contains a value of the wrong type.

+

https://redis.io/commands/decr

+
source

fn decr_by<R, K>( + &self, + key: K, + val: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Decrements the number stored at key by val. If the key does not exist, it is set to 0 before performing the +operation.

+

Returns an error if the key contains a value of the wrong type.

+

https://redis.io/commands/decrby

+
source

fn ttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in seconds.

+

https://redis.io/commands/ttl

+
source

fn pttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the remaining time to live of a key that has a timeout, in milliseconds.

+

https://redis.io/commands/pttl

+
source

fn persist<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Remove the existing timeout on a key, turning the key from volatile (a key with an expiration) +to persistent (a key that will never expire as no timeout is associated).

+

Returns a boolean value describing whether the timeout was removed.

+

https://redis.io/commands/persist

+
source

fn expire<R, K>( + &self, + key: K, + seconds: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on key. After the timeout has expired, the key will be automatically deleted.

+

https://redis.io/commands/expire

+
source

fn expire_at<R, K>( + &self, + key: K, + timestamp: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Set a timeout on a key based on a UNIX timestamp.

+

https://redis.io/commands/expireat

+
source

fn pexpire<R, K>( + &self, + key: K, + milliseconds: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command works exactly like EXPIRE but the time to live of the key is specified in milliseconds instead of +seconds.

+

https://redis.io/docs/latest/commands/pexpire/

+
source

fn pexpire_at<R, K>( + &self, + key: K, + timestamp: i64, + options: Option<ExpireOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

PEXPIREAT has the same effect and semantic as EXPIREAT, but the Unix time at which the key will expire is +specified in milliseconds instead of seconds.

+

https://redis.io/docs/latest/commands/pexpireat/

+
source

fn exists<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns number of keys that exist from the keys arguments.

+

https://redis.io/commands/exists

+
source

fn lcs<R, K1, K2>( + &self, + key1: K1, + key2: K2, + len: bool, + idx: bool, + minmatchlen: Option<i64>, + withmatchlen: bool, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis, + K1: Into<RedisKey>, + K2: Into<RedisKey>,

Runs the longest common subsequence algorithm on two keys.

+

https://redis.io/commands/lcs/

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.ListInterface.html b/doc/fred/interfaces/trait.ListInterface.html new file mode 100644 index 00000000..62f50f92 --- /dev/null +++ b/doc/fred/interfaces/trait.ListInterface.html @@ -0,0 +1,466 @@ +ListInterface in fred::interfaces - Rust

Trait fred::interfaces::ListInterface

source ·
pub trait ListInterface: ClientLike + Sized {
+
Show 24 methods // Provided methods + fn blmpop<R, K>( + &self, + timeout: f64, + keys: K, + direction: LMoveDirection, + count: Option<i64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn blpop<R, K>( + &self, + keys: K, + timeout: f64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn brpop<R, K>( + &self, + keys: K, + timeout: f64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn brpoplpush<R, S, D>( + &self, + source: S, + destination: D, + timeout: f64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey> { ... } + fn blmove<R, S, D>( + &self, + source: S, + destination: D, + source_direction: LMoveDirection, + destination_direction: LMoveDirection, + timeout: f64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey> { ... } + fn lmpop<R, K>( + &self, + keys: K, + direction: LMoveDirection, + count: Option<i64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn lindex<R, K>( + &self, + key: K, + index: i64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn linsert<R, K, P, V>( + &self, + key: K, + location: ListLocation, + pivot: P, + element: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: TryInto<RedisValue>, + P::Error: Into<RedisError>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn llen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn lpop<R, K>( + &self, + key: K, + count: Option<usize>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn lpos<R, K, V>( + &self, + key: K, + element: V, + rank: Option<i64>, + count: Option<i64>, + maxlen: Option<i64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn lpush<R, K, V>( + &self, + key: K, + elements: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError> { ... } + fn lpushx<R, K, V>( + &self, + key: K, + elements: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError> { ... } + fn lrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn lrem<R, K, V>( + &self, + key: K, + count: i64, + element: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn lset<R, K, V>( + &self, + key: K, + index: i64, + element: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn ltrim<R, K>( + &self, + key: K, + start: i64, + stop: i64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn rpop<R, K>( + &self, + key: K, + count: Option<usize>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn rpoplpush<R, S, D>( + &self, + source: S, + dest: D, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey> { ... } + fn lmove<R, S, D>( + &self, + source: S, + dest: D, + source_direction: LMoveDirection, + dest_direction: LMoveDirection, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey> { ... } + fn rpush<R, K, V>( + &self, + key: K, + elements: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError> { ... } + fn rpushx<R, K, V>( + &self, + key: K, + elements: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError> { ... } + fn sort<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, + store: Option<RedisKey>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings> { ... } + fn sort_ro<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings> { ... } +
}
Available on crate feature i-lists only.
Expand description

Functions that implement the lists interface.

+

Provided Methods§

source

fn blmpop<R, K>( + &self, + timeout: f64, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

source

fn blpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BLPOP is a blocking list pop primitive. It is the blocking version of LPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the head of the first list +that is non-empty, with the given keys being checked in the order that they are given.

+

https://redis.io/commands/blpop

+
source

fn brpop<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

BRPOP is a blocking list pop primitive. It is the blocking version of RPOP because it blocks the connection when +there are no elements to pop from any of the given lists. An element is popped from the tail of the first list +that is non-empty, with the given keys being checked in the order that they are given.

+

https://redis.io/commands/brpop

+
source

fn brpoplpush<R, S, D>( + &self, + source: S, + destination: D, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

source

fn blmove<R, S, D>( + &self, + source: S, + destination: D, + source_direction: LMoveDirection, + destination_direction: LMoveDirection, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

The blocking equivalent of Self::lmove.

+

https://redis.io/commands/blmove

+
source

fn lmpop<R, K>( + &self, + keys: K, + direction: LMoveDirection, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements from the first non-empty list key from the list of provided key names.

+

https://redis.io/commands/lmpop/

+
source

fn lindex<R, K>( + &self, + key: K, + index: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the element at index in the list stored at key.

+

https://redis.io/commands/lindex

+
source

fn linsert<R, K, P, V>( + &self, + key: K, + location: ListLocation, + pivot: P, + element: V, +) -> impl Future<Output = RedisResult<R>>

Inserts element in the list stored at key either before or after the reference value pivot.

+

https://redis.io/commands/linsert

+
source

fn llen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the length of the list stored at key.

+

https://redis.io/commands/llen

+
source

fn lpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the first elements of the list stored at key.

+

https://redis.io/commands/lpop

+
source

fn lpos<R, K, V>( + &self, + key: K, + element: V, + rank: Option<i64>, + count: Option<i64>, + maxlen: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

The command returns the index of matching elements inside a Redis list.

+

https://redis.io/commands/lpos

+
source

fn lpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the head of the list stored at key.

+

https://redis.io/commands/lpush

+
source

fn lpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the head of the list stored at key, only if key already exists and holds a list.

+

https://redis.io/commands/lpushx

+
source

fn lrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified elements of the list stored at key.

+

https://redis.io/commands/lrange

+
source

fn lrem<R, K, V>( + &self, + key: K, + count: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Removes the first count occurrences of elements equal to element from the list stored at key.

+

https://redis.io/commands/lrem

+
source

fn lset<R, K, V>( + &self, + key: K, + index: i64, + element: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Sets the list element at index to element.

+

https://redis.io/commands/lset

+
source

fn ltrim<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Trim an existing list so that it will contain only the specified range of elements specified.

+

https://redis.io/commands/ltrim

+
source

fn rpop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns the last elements of the list stored at key.

+

https://redis.io/commands/rpop

+
source

fn rpoplpush<R, S, D>( + &self, + source: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the last element (tail) of the list stored at source, and pushes the element at +the first element (head) of the list stored at destination.

+

https://redis.io/commands/rpoplpush

+
source

fn lmove<R, S, D>( + &self, + source: S, + dest: D, + source_direction: LMoveDirection, + dest_direction: LMoveDirection, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Atomically returns and removes the first/last element (head/tail depending on the source direction argument) of +the list stored at source, and pushes the element at the first/last element (head/tail depending on the +destination direction argument) of the list stored at destination.

+

https://redis.io/commands/lmove

+
source

fn rpush<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Insert all the specified values at the tail of the list stored at key.

+

https://redis.io/commands/rpush

+
source

fn rpushx<R, K, V>( + &self, + key: K, + elements: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Inserts specified values at the tail of the list stored at key, only if key already exists and holds a list.

+

https://redis.io/commands/rpushx

+
source

fn sort<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, + store: Option<RedisKey>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Returns or stores the elements contained in the list, set or sorted set at key.

+

https://redis.io/commands/sort/

+
source

fn sort_ro<R, K, S>( + &self, + key: K, + by: Option<Str>, + limit: Option<Limit>, + get: S, + order: Option<SortOrder>, + alpha: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Read-only variant of the SORT command. It is exactly like the original SORT but refuses the STORE option and can +safely be used in read-only replicas.

+

https://redis.io/commands/sort_ro/

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.LuaInterface.html b/doc/fred/interfaces/trait.LuaInterface.html new file mode 100644 index 00000000..9b31788f --- /dev/null +++ b/doc/fred/interfaces/trait.LuaInterface.html @@ -0,0 +1,114 @@ +LuaInterface in fred::interfaces - Rust

Trait fred::interfaces::LuaInterface

source ·
pub trait LuaInterface: ClientLike + Sized {
+    // Provided methods
+    fn script_load<R, S>(
+        &self,
+        script: S,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             S: Into<Str> { ... }
+    fn script_load_cluster<R, S>(
+        &self,
+        script: S,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             S: Into<Str> { ... }
+    fn script_kill(&self) -> impl Future<Output = RedisResult<()>> { ... }
+    fn script_kill_cluster(&self) -> impl Future<Output = RedisResult<()>> { ... }
+    fn script_flush(&self, async: bool) -> impl Future<Output = RedisResult<()>> { ... }
+    fn script_flush_cluster(
+        &self,
+        async: bool,
+    ) -> impl Future<Output = RedisResult<()>> { ... }
+    fn script_exists<R, H>(
+        &self,
+        hashes: H,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             H: Into<MultipleStrings> { ... }
+    fn script_debug(
+        &self,
+        flag: ScriptDebugFlag,
+    ) -> impl Future<Output = RedisResult<()>> { ... }
+    fn evalsha<R, S, K, V>(
+        &self,
+        hash: S,
+        keys: K,
+        args: V,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             S: Into<Str>,
+             K: Into<MultipleKeys>,
+             V: TryInto<MultipleValues>,
+             V::Error: Into<RedisError> { ... }
+    fn eval<R, S, K, V>(
+        &self,
+        script: S,
+        keys: K,
+        args: V,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             S: Into<Str>,
+             K: Into<MultipleKeys>,
+             V: TryInto<MultipleValues>,
+             V::Error: Into<RedisError> { ... }
+}
Available on crate feature i-scripts only.
Expand description

Functions that implement the lua interface.

+

Provided Methods§

source

fn script_load<R, S>(&self, script: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Load a script into the scripts cache, without executing it. After the specified command is loaded into the +script cache it will be callable using EVALSHA with the correct SHA1 digest of the script.

+

Returns the SHA-1 hash of the script.

+

https://redis.io/commands/script-load

+
source

fn script_load_cluster<R, S>( + &self, + script: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Available on crate feature sha-1 only.

A clustered variant of script_load that loads the script on all primary nodes in a cluster.

+

Returns the SHA-1 hash of the script.

+
source

fn script_kill(&self) -> impl Future<Output = RedisResult<()>>

Kills the currently executing Lua script, assuming no write operation was yet performed by the script.

+

https://redis.io/commands/script-kill

+
source

fn script_kill_cluster(&self) -> impl Future<Output = RedisResult<()>>

A clustered variant of the script_kill command that issues the command to all primary nodes +in the cluster.

+
source

fn script_flush(&self, async: bool) -> impl Future<Output = RedisResult<()>>

Flush the Lua scripts cache.

+

https://redis.io/commands/script-flush

+
source

fn script_flush_cluster( + &self, + async: bool, +) -> impl Future<Output = RedisResult<()>>

A clustered variant of script_flush that flushes the script cache on all primary nodes in +the cluster.

+
source

fn script_exists<R, H>(&self, hashes: H) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + H: Into<MultipleStrings>,

Returns information about the existence of the scripts in the script cache.

+

https://redis.io/commands/script-exists

+
source

fn script_debug( + &self, + flag: ScriptDebugFlag, +) -> impl Future<Output = RedisResult<()>>

Set the debug mode for subsequent scripts executed with EVAL.

+

https://redis.io/commands/script-debug

+
source

fn evalsha<R, S, K, V>( + &self, + hash: S, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Evaluates a script cached on the server side by its SHA1 digest.

+

https://redis.io/commands/evalsha

+

Note: Use None to represent an empty set of keys or args.

+
source

fn eval<R, S, K, V>( + &self, + script: S, + keys: K, + args: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + K: Into<MultipleKeys>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Evaluate a Lua script on the server.

+

https://redis.io/commands/eval

+

Note: Use None to represent an empty set of keys or args.

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.MemoryInterface.html b/doc/fred/interfaces/trait.MemoryInterface.html new file mode 100644 index 00000000..55543dd0 --- /dev/null +++ b/doc/fred/interfaces/trait.MemoryInterface.html @@ -0,0 +1,38 @@ +MemoryInterface in fred::interfaces - Rust

Trait fred::interfaces::MemoryInterface

source ·
pub trait MemoryInterface: ClientLike + Sized {
+    // Provided methods
+    fn memory_doctor<R>(&self) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn memory_malloc_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn memory_purge(&self) -> impl Future<Output = RedisResult<()>> { ... }
+    fn memory_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn memory_usage<R, K>(
+        &self,
+        key: K,
+        samples: Option<u32>,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis,
+             K: Into<RedisKey> { ... }
+}
Available on crate feature i-memory only.
Expand description

Functions that implement the memory interface.

+

Provided Methods§

source

fn memory_doctor<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY DOCTOR command reports about different memory-related issues that the Redis server experiences, and +advises about possible remedies.

+

https://redis.io/commands/memory-doctor

+
source

fn memory_malloc_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY MALLOC-STATS command provides an internal statistics report from the memory allocator.

+

https://redis.io/commands/memory-malloc-stats

+
source

fn memory_purge(&self) -> impl Future<Output = RedisResult<()>>

The MEMORY PURGE command attempts to purge dirty pages so these can be reclaimed by the allocator.

+

https://redis.io/commands/memory-purge

+
source

fn memory_stats<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

The MEMORY STATS command returns an Array reply about the memory usage of the server.

+

https://redis.io/commands/memory-stats

+
source

fn memory_usage<R, K>( + &self, + key: K, + samples: Option<u32>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

The MEMORY USAGE command reports the number of bytes that a key and its value require to be stored in RAM.

+

https://redis.io/commands/memory-usage

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.MetricsInterface.html b/doc/fred/interfaces/trait.MetricsInterface.html new file mode 100644 index 00000000..0e408fc3 --- /dev/null +++ b/doc/fred/interfaces/trait.MetricsInterface.html @@ -0,0 +1,34 @@ +MetricsInterface in fred::interfaces - Rust

Trait fred::interfaces::MetricsInterface

source ·
pub trait MetricsInterface: ClientLike + Sized {
+    // Provided methods
+    fn read_redelivery_count(&self) -> usize { ... }
+    fn take_redelivery_count(&self) -> usize { ... }
+    fn command_queue_len(&self) -> usize { ... }
+    fn read_latency_metrics(&self) -> Stats { ... }
+    fn take_latency_metrics(&self) -> Stats { ... }
+    fn read_network_latency_metrics(&self) -> Stats { ... }
+    fn take_network_latency_metrics(&self) -> Stats { ... }
+    fn read_req_size_metrics(&self) -> Stats { ... }
+    fn take_req_size_metrics(&self) -> Stats { ... }
+    fn read_res_size_metrics(&self) -> Stats { ... }
+    fn take_res_size_metrics(&self) -> Stats { ... }
+}
Expand description

Functions that implement the internal metrics interface.

+

Provided Methods§

source

fn read_redelivery_count(&self) -> usize

Read the number of request redeliveries.

+

This is the number of times a request had to be sent again due to a connection closing while waiting on a +response.

+
source

fn take_redelivery_count(&self) -> usize

Read and reset the number of request redeliveries.

+
source

fn command_queue_len(&self) -> usize

Read the number of buffered commands that have not yet been sent to the server.

+
source

fn read_latency_metrics(&self) -> Stats

Available on crate feature metrics only.

Read latency metrics across all commands.

+

This metric reflects the total latency experienced by callers, including time spent waiting in memory to be +written and network latency. Features such as automatic reconnect and frame serialization time can all affect +these values.

+
source

fn take_latency_metrics(&self) -> Stats

Available on crate feature metrics only.

Read and consume latency metrics, resetting their values afterwards.

+
source

fn read_network_latency_metrics(&self) -> Stats

Available on crate feature metrics only.

Read network latency metrics across all commands.

+

This metric only reflects time spent waiting on a response. It will factor in reconnect time if a response +doesn’t arrive due to a connection closing, but it does not include the time a command spends waiting to be +written, serialization time, backpressure, etc.

+
source

fn take_network_latency_metrics(&self) -> Stats

Available on crate feature metrics only.

Read and consume network latency metrics, resetting their values afterwards.

+
source

fn read_req_size_metrics(&self) -> Stats

Available on crate feature metrics only.

Read request payload size metrics across all commands.

+
source

fn take_req_size_metrics(&self) -> Stats

Available on crate feature metrics only.

Read and consume request payload size metrics, resetting their values afterwards.

+
source

fn read_res_size_metrics(&self) -> Stats

Available on crate feature metrics only.

Read response payload size metrics across all commands.

+
source

fn take_res_size_metrics(&self) -> Stats

Available on crate feature metrics only.

Read and consume response payload size metrics, resetting their values afterwards.

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.PubsubInterface.html b/doc/fred/interfaces/trait.PubsubInterface.html new file mode 100644 index 00000000..1cab0f2b --- /dev/null +++ b/doc/fred/interfaces/trait.PubsubInterface.html @@ -0,0 +1,146 @@ +PubsubInterface in fred::interfaces - Rust

Trait fred::interfaces::PubsubInterface

source ·
pub trait PubsubInterface: ClientLike + Sized {
+
Show 13 methods // Provided methods + fn subscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>> + where S: Into<MultipleStrings> { ... } + fn unsubscribe<S>( + &self, + channels: S, + ) -> impl Future<Output = RedisResult<()>> + where S: Into<MultipleStrings> { ... } + fn psubscribe<S>( + &self, + patterns: S, + ) -> impl Future<Output = RedisResult<()>> + where S: Into<MultipleStrings> { ... } + fn punsubscribe<S>( + &self, + patterns: S, + ) -> impl Future<Output = RedisResult<()>> + where S: Into<MultipleStrings> { ... } + fn publish<R, S, V>( + &self, + channel: S, + message: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn ssubscribe<C>( + &self, + channels: C, + ) -> impl Future<Output = RedisResult<()>> + where C: Into<MultipleStrings> { ... } + fn sunsubscribe<C>( + &self, + channels: C, + ) -> impl Future<Output = RedisResult<()>> + where C: Into<MultipleStrings> { ... } + fn spublish<R, S, V>( + &self, + channel: S, + message: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn pubsub_channels<R, S>( + &self, + pattern: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<Str> { ... } + fn pubsub_numpat<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn pubsub_numsub<R, S>( + &self, + channels: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<MultipleStrings> { ... } + fn pubsub_shardchannels<R, S>( + &self, + pattern: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<Str> { ... } + fn pubsub_shardnumsub<R, S>( + &self, + channels: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<MultipleStrings> { ... } +
}
Available on crate feature i-pubsub only.
Expand description

Functions that implement the pubsub interface.

+

Provided Methods§

source

fn subscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribe to a channel on the publish-subscribe interface.

+

https://redis.io/commands/subscribe

+
source

fn unsubscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribe from a channel on the PubSub interface.

+

https://redis.io/commands/unsubscribe

+
source

fn psubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Subscribes the client to the given patterns.

+

https://redis.io/commands/psubscribe

+
source

fn punsubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>>
where + S: Into<MultipleStrings>,

Unsubscribes the client from the given patterns, or from all of them if none is given.

+

If no channels are provided this command returns an empty array.

+

https://redis.io/commands/punsubscribe

+
source

fn publish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Publish a message on the PubSub interface, returning the number of clients that received the message.

+

https://redis.io/commands/publish

+
source

fn ssubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Subscribes the client to the specified shard channels.

+

https://redis.io/commands/ssubscribe/

+
source

fn sunsubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>>
where + C: Into<MultipleStrings>,

Unsubscribes the client from the given shard channels, or from all of them if none is given.

+

If no channels are provided this command returns an empty array.

+

https://redis.io/commands/sunsubscribe/

+
source

fn spublish<R, S, V>( + &self, + channel: S, + message: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Posts a message to the given shard channel.

+

https://redis.io/commands/spublish/

+
source

fn pubsub_channels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active channels.

+

https://redis.io/commands/pubsub-channels/

+
source

fn pubsub_numpat<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns the number of unique patterns that are subscribed to by clients.

+

https://redis.io/commands/pubsub-numpat/

+
source

fn pubsub_numsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers (exclusive of clients subscribed to patterns) for the specified channels.

+

https://redis.io/commands/pubsub-numsub/

+
source

fn pubsub_shardchannels<R, S>( + &self, + pattern: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Lists the currently active shard channels.

+

https://redis.io/commands/pubsub-shardchannels/

+
source

fn pubsub_shardnumsub<R, S>( + &self, + channels: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<MultipleStrings>,

Returns the number of subscribers for the specified shard channels.

+

https://redis.io/commands/pubsub-shardnumsub/

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.RediSearchInterface.html b/doc/fred/interfaces/trait.RediSearchInterface.html new file mode 100644 index 00000000..a2782cde --- /dev/null +++ b/doc/fred/interfaces/trait.RediSearchInterface.html @@ -0,0 +1,418 @@ +RediSearchInterface in fred::interfaces - Rust

Trait fred::interfaces::RediSearchInterface

source ·
pub trait RediSearchInterface: ClientLike + Sized {
+
Show 26 methods // Provided methods + fn ft_list<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn ft_aggregate<R, I, Q>( + &self, + index: I, + query: Q, + options: FtAggregateOptions, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + I: Into<Str>, + Q: Into<Str> { ... } + fn ft_search<R, I, Q>( + &self, + index: I, + query: Q, + options: FtSearchOptions, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + I: Into<Str>, + Q: Into<Str> { ... } + fn ft_create<R, I>( + &self, + index: I, + options: FtCreateOptions, + schema: Vec<SearchSchema>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + I: Into<Str> { ... } + fn ft_alter<R, I>( + &self, + index: I, + options: FtAlterOptions, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + I: Into<Str> { ... } + fn ft_aliasadd<R, A, I>( + &self, + alias: A, + index: I, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + A: Into<Str>, + I: Into<Str> { ... } + fn ft_aliasdel<R, A>( + &self, + alias: A, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + A: Into<Str> { ... } + fn ft_aliasupdate<R, A, I>( + &self, + alias: A, + index: I, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + A: Into<Str>, + I: Into<Str> { ... } + fn ft_config_get<R, S>( + &self, + option: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<Str> { ... } + fn ft_config_set<R, S, V>( + &self, + option: S, + value: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn ft_cursor_del<R, I, C>( + &self, + index: I, + cursor: C, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError> { ... } + fn ft_cursor_read<R, I, C>( + &self, + index: I, + cursor: C, + count: Option<u64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError> { ... } + fn ft_dictadd<R, D, S>( + &self, + dict: D, + terms: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings> { ... } + fn ft_dictdel<R, D, S>( + &self, + dict: D, + terms: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings> { ... } + fn ft_dictdump<R, D>(&self, dict: D) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + D: Into<Str> { ... } + fn ft_dropindex<R, I>( + &self, + index: I, + dd: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + I: Into<Str> { ... } + fn ft_explain<R, I, Q>( + &self, + index: I, + query: Q, + dialect: Option<i64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + I: Into<Str>, + Q: Into<Str> { ... } + fn ft_info<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + I: Into<Str> { ... } + fn ft_spellcheck<R, I, Q>( + &self, + index: I, + query: Q, + distance: Option<u8>, + terms: Option<SpellcheckTerms>, + dialect: Option<i64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + I: Into<Str>, + Q: Into<Str> { ... } + fn ft_sugadd<R, K, S>( + &self, + key: K, + string: S, + score: f64, + incr: bool, + payload: Option<Bytes>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + S: Into<Str> { ... } + fn ft_sugdel<R, K, S>( + &self, + key: K, + string: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + S: Into<Str> { ... } + fn ft_sugget<R, K, P>( + &self, + key: K, + prefix: P, + fuzzy: bool, + withscores: bool, + withpayloads: bool, + max: Option<u64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str> { ... } + fn ft_suglen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn ft_syndump<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + I: Into<Str> { ... } + fn ft_synupdate<R, I, S, T>( + &self, + index: I, + synonym_group_id: S, + skipinitialscan: bool, + terms: T, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + I: Into<Str>, + S: Into<Str>, + T: Into<MultipleStrings> { ... } + fn ft_tagvals<R, I, F>( + &self, + index: I, + field_name: F, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + I: Into<Str>, + F: Into<Str> { ... } +
}
Available on crate feature i-redisearch only.
Expand description

A RediSearch interface.

+

Provided Methods§

source

fn ft_list<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Returns a list of all existing indexes.

+

https://redis.io/docs/latest/commands/ft._list/

+
source

fn ft_aggregate<R, I, Q>( + &self, + index: I, + query: Q, + options: FtAggregateOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Run a search query on an index, and perform aggregate transformations on the results.

+

https://redis.io/docs/latest/commands/ft.aggregate/

+

Search the index with a textual query, returning either documents or just ids.

+

https://redis.io/docs/latest/commands/ft.search/

+

Note: FT.SEARCH uses a different format in RESP3 mode.

+
source

fn ft_create<R, I>( + &self, + index: I, + options: FtCreateOptions, + schema: Vec<SearchSchema>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Create an index with the given specification.

+

https://redis.io/docs/latest/commands/ft.create/

+
source

fn ft_alter<R, I>( + &self, + index: I, + options: FtAlterOptions, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Add a new attribute to the index.

+

https://redis.io/docs/latest/commands/ft.alter/

+
source

fn ft_aliasadd<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

source

fn ft_aliasdel<R, A>(&self, alias: A) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>,

Remove an alias from an index.

+

https://redis.io/docs/latest/commands/ft.aliasdel/

+
source

fn ft_aliasupdate<R, A, I>( + &self, + alias: A, + index: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + A: Into<Str>, + I: Into<Str>,

Add an alias to an index. If the alias is already associated with another index, FT.ALIASUPDATE removes the +alias association with the previous index.

+

https://redis.io/docs/latest/commands/ft.aliasupdate/

+
source

fn ft_config_get<R, S>(&self, option: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>,

Retrieve configuration options.

+

https://redis.io/docs/latest/commands/ft.config-get/

+
source

fn ft_config_set<R, S, V>( + &self, + option: S, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set the value of a RediSearch configuration parameter.

+

https://redis.io/docs/latest/commands/ft.config-set/

+
source

fn ft_cursor_del<R, I, C>( + &self, + index: I, + cursor: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

source

fn ft_cursor_read<R, I, C>( + &self, + index: I, + cursor: C, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + C: TryInto<RedisValue>, + C::Error: Into<RedisError>,

Read next results from an existing cursor.

+

https://redis.io/docs/latest/commands/ft.cursor-read/

+
source

fn ft_dictadd<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

source

fn ft_dictdel<R, D, S>( + &self, + dict: D, + terms: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>, + S: Into<MultipleStrings>,

Remove terms from a dictionary.

+

https://redis.io/docs/latest/commands/ft.dictdel/

+
source

fn ft_dictdump<R, D>(&self, dict: D) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<Str>,

Dump all terms in the given dictionary.

+

https://redis.io/docs/latest/commands/ft.dictdump/

+
source

fn ft_dropindex<R, I>( + &self, + index: I, + dd: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

source

fn ft_explain<R, I, Q>( + &self, + index: I, + query: Q, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Return the execution plan for a complex query.

+

https://redis.io/docs/latest/commands/ft.explain/

+
source

fn ft_info<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Return information and statistics on the index.

+

https://redis.io/docs/latest/commands/ft.info/

+
source

fn ft_spellcheck<R, I, Q>( + &self, + index: I, + query: Q, + distance: Option<u8>, + terms: Option<SpellcheckTerms>, + dialect: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + Q: Into<Str>,

Perform spelling correction on a query, returning suggestions for misspelled terms.

+

https://redis.io/docs/latest/commands/ft.spellcheck/

+
source

fn ft_sugadd<R, K, S>( + &self, + key: K, + string: S, + score: f64, + incr: bool, + payload: Option<Bytes>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Add a suggestion string to an auto-complete suggestion dictionary.

+

https://redis.io/docs/latest/commands/ft.sugadd/

+
source

fn ft_sugdel<R, K, S>( + &self, + key: K, + string: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Delete a string from a suggestion index.

+

https://redis.io/docs/latest/commands/ft.sugdel/

+
source

fn ft_sugget<R, K, P>( + &self, + key: K, + prefix: P, + fuzzy: bool, + withscores: bool, + withpayloads: bool, + max: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Get completion suggestions for a prefix.

+

https://redis.io/docs/latest/commands/ft.sugget/

+
source

fn ft_suglen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the size of an auto-complete suggestion dictionary.

+

https://redis.io/docs/latest/commands/ft.suglen/

+
source

fn ft_syndump<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>,

Dump the contents of a synonym group.

+

https://redis.io/docs/latest/commands/ft.syndump/

+
source

fn ft_synupdate<R, I, S, T>( + &self, + index: I, + synonym_group_id: S, + skipinitialscan: bool, + terms: T, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + S: Into<Str>, + T: Into<MultipleStrings>,

source

fn ft_tagvals<R, I, F>( + &self, + index: I, + field_name: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + I: Into<Str>, + F: Into<Str>,

Return a distinct set of values indexed in a Tag field.

+

https://redis.io/docs/latest/commands/ft.tagvals/

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.RedisJsonInterface.html b/doc/fred/interfaces/trait.RedisJsonInterface.html new file mode 100644 index 00000000..cd739a9d --- /dev/null +++ b/doc/fred/interfaces/trait.RedisJsonInterface.html @@ -0,0 +1,462 @@ +RedisJsonInterface in fred::interfaces - Rust

Trait fred::interfaces::RedisJsonInterface

source ·
pub trait RedisJsonInterface: ClientLike + Sized {
+
Show 22 methods // Provided methods + fn json_arrappend<R, K, P, V>( + &self, + key: K, + path: P, + values: Vec<V>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value> { ... } + fn json_arrindex<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + start: Option<i64>, + stop: Option<i64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value> { ... } + fn json_arrinsert<R, K, P, V>( + &self, + key: K, + path: P, + index: i64, + values: Vec<V>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value> { ... } + fn json_arrlen<R, K, P>( + &self, + key: K, + path: Option<P>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str> { ... } + fn json_arrpop<R, K, P>( + &self, + key: K, + path: Option<P>, + index: Option<i64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str> { ... } + fn json_arrtrim<R, K, P>( + &self, + key: K, + path: P, + start: i64, + stop: i64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str> { ... } + fn json_clear<R, K, P>( + &self, + key: K, + path: Option<P>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str> { ... } + fn json_debug_memory<R, K, P>( + &self, + key: K, + path: Option<P>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str> { ... } + fn json_del<R, K, P>( + &self, + key: K, + path: P, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str> { ... } + fn json_get<R, K, I, N, S, P>( + &self, + key: K, + indent: Option<I>, + newline: Option<N>, + space: Option<S>, + paths: P, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + I: Into<Str>, + N: Into<Str>, + S: Into<Str>, + P: Into<MultipleStrings> { ... } + fn json_merge<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value> { ... } + fn json_mget<R, K, P>( + &self, + keys: K, + path: P, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys>, + P: Into<Str> { ... } + fn json_mset<R, K, P, V>( + &self, + values: Vec<(K, P, V)>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value> { ... } + fn json_numincrby<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value> { ... } + fn json_objkeys<R, K, P>( + &self, + key: K, + path: Option<P>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str> { ... } + fn json_objlen<R, K, P>( + &self, + key: K, + path: Option<P>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str> { ... } + fn json_resp<R, K, P>( + &self, + key: K, + path: Option<P>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str> { ... } + fn json_set<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + options: Option<SetOptions>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value> { ... } + fn json_strappend<R, K, P, V>( + &self, + key: K, + path: Option<P>, + value: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value> { ... } + fn json_strlen<R, K, P>( + &self, + key: K, + path: Option<P>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str> { ... } + fn json_toggle<R, K, P>( + &self, + key: K, + path: P, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str> { ... } + fn json_type<R, K, P>( + &self, + key: K, + path: Option<P>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + P: Into<Str> { ... } +
}
Available on crate feature i-redis-json only.
Expand description

The client commands in the RedisJSON interface.

+

§String Values

+

This interface uses serde_json::Value as the baseline type and will convert non-string values +to RESP bulk strings via to_string.

+

Some of the RedisJSON commands include the following notice in the documentation:

+
+

To specify a string as an array value to append, wrap the quoted string with an additional set of single quotes. +Example: ‘“silver”’.

+
+

The serde_json::to_string functions are often the easiest way to do +this. The json_quote macro can also help.

+

For example:

+ +
use fred::{json_quote, prelude::*};
+use serde_json::json;
+async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  let _: () = client.json_set("foo", "$", json!(["a", "b"]), None).await?;
+
+  // need to double quote string values in this context
+  let size: i64 = client
+    .json_arrappend("foo", Some("$"), vec![
+      json!("c").to_string(),
+      // or
+      serde_json::to_string(&json!("d"))?,
+      // or
+      json_quote!("e"),
+    ])
+    .await?;
+  assert_eq!(size, 5);
+  Ok(())
+}
+

Provided Methods§

source

fn json_arrappend<R, K, P, V>( + &self, + key: K, + path: P, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json values into the array at path after the last element in it.

+

https://redis.io/commands/json.arrappend

+
source

fn json_arrindex<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + start: Option<i64>, + stop: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Search for the first occurrence of a JSON value in an array.

+

https://redis.io/commands/json.arrindex/

+
source

fn json_arrinsert<R, K, P, V>( + &self, + key: K, + path: P, + index: i64, + values: Vec<V>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Insert the json values into the array at path before the index (shifts to the right).

+

https://redis.io/commands/json.arrinsert/

+
source

fn json_arrlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON array at path in key.

+

https://redis.io/commands/json.arrlen/

+
source

fn json_arrpop<R, K, P>( + &self, + key: K, + path: Option<P>, + index: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Remove and return an element from the index in the array

+

https://redis.io/commands/json.arrpop/

+
source

fn json_arrtrim<R, K, P>( + &self, + key: K, + path: P, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Trim an array so that it contains only the specified inclusive range of elements

+

https://redis.io/commands/json.arrtrim/

+
source

fn json_clear<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Clear container values (arrays/objects) and set numeric values to 0

+

https://redis.io/commands/json.clear/

+
source

fn json_debug_memory<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report a value’s memory usage in bytes

+

https://redis.io/commands/json.debug-memory/

+
source

fn json_del<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

source

fn json_get<R, K, I, N, S, P>( + &self, + key: K, + indent: Option<I>, + newline: Option<N>, + space: Option<S>, + paths: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<Str>, + N: Into<Str>, + S: Into<Str>, + P: Into<MultipleStrings>,

Return the value at path in JSON serialized form.

+

https://redis.io/commands/json.get/

+
source

fn json_merge<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Merge a given JSON value into matching paths.

+

https://redis.io/commands/json.merge/

+
source

fn json_mget<R, K, P>( + &self, + keys: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + P: Into<Str>,

Return the values at path from multiple key arguments.

+

https://redis.io/commands/json.mget/

+
source

fn json_mset<R, K, P, V>( + &self, + values: Vec<(K, P, V)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set or update one or more JSON values according to the specified key-path-value triplets.

+

https://redis.io/commands/json.mset/

+
source

fn json_numincrby<R, K, P, V>( + &self, + key: K, + path: P, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Increment the number value stored at path by number

+

https://redis.io/commands/json.numincrby/

+
source

fn json_objkeys<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the keys in the object that’s referenced by path.

+

https://redis.io/commands/json.objkeys/

+
source

fn json_objlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the number of keys in the JSON object at path in key.

+

https://redis.io/commands/json.objlen/

+
source

fn json_resp<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Return the JSON in key in Redis serialization protocol specification form.

+

https://redis.io/commands/json.resp/

+
source

fn json_set<R, K, P, V>( + &self, + key: K, + path: P, + value: V, + options: Option<SetOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Set the JSON value at path in key.

+

https://redis.io/commands/json.set/

+
source

fn json_strappend<R, K, P, V>( + &self, + key: K, + path: Option<P>, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>, + V: Into<Value>,

Append the json-string values to the string at path.

+

https://redis.io/commands/json.strappend/

+
source

fn json_strlen<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the length of the JSON String at path in key.

+

https://redis.io/commands/json.strlen/

+
source

fn json_toggle<R, K, P>( + &self, + key: K, + path: P, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Toggle a Boolean value stored at path.

+

https://redis.io/commands/json.toggle/

+
source

fn json_type<R, K, P>( + &self, + key: K, + path: Option<P>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + P: Into<Str>,

Report the type of JSON value at path.

+

https://redis.io/commands/json.type/

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.SentinelInterface.html b/doc/fred/interfaces/trait.SentinelInterface.html new file mode 100644 index 00000000..963be6b3 --- /dev/null +++ b/doc/fred/interfaces/trait.SentinelInterface.html @@ -0,0 +1,149 @@ +SentinelInterface in fred::interfaces - Rust

Trait fred::interfaces::SentinelInterface

source ·
pub trait SentinelInterface: ClientLike + Sized {
+
Show 18 methods // Provided methods + fn ckquorum<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + N: Into<Str> { ... } + fn flushconfig<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn failover<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + N: Into<Str> { ... } + fn get_master_addr_by_name<R, N>( + &self, + name: N, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + N: Into<Str> { ... } + fn info_cache<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn master<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + N: Into<Str> { ... } + fn masters<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn monitor<R, N>( + &self, + name: N, + ip: IpAddr, + port: u16, + quorum: u32, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + N: Into<Str> { ... } + fn myid<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn pending_scripts<R>(&self) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn remove<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + N: Into<Str> { ... } + fn replicas<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + N: Into<Str> { ... } + fn sentinels<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + N: Into<Str> { ... } + fn set<R, N, V>( + &self, + name: N, + args: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + N: Into<Str>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError> { ... } + fn simulate_failure<R>( + &self, + kind: SentinelFailureKind, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis { ... } + fn reset<R, P>(&self, pattern: P) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + P: Into<Str> { ... } + fn config_get<R, K>(&self, name: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<Str> { ... } + fn config_set<R, K, V>( + &self, + name: K, + value: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } +
}
Available on crate feature sentinel-client only.
Expand description

Functions that implement the sentinel interface.

+

Provided Methods§

source

fn ckquorum<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Check if the current Sentinel configuration is able to reach the quorum needed to failover a master, and the +majority needed to authorize the failover.

+
source

fn flushconfig<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Force Sentinel to rewrite its configuration on disk, including the current Sentinel state.

+
source

fn failover<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Force a failover as if the master was not reachable, and without asking for agreement to other Sentinels.

+
source

fn get_master_addr_by_name<R, N>( + &self, + name: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Return the ip and port number of the master with that name.

+
source

fn info_cache<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return cached INFO output from masters and replicas.

+
source

fn master<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Show the state and info of the specified master.

+
source

fn masters<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Show a list of monitored masters and their state.

+
source

fn monitor<R, N>( + &self, + name: N, + ip: IpAddr, + port: u16, + quorum: u32, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

source

fn myid<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the ID of the Sentinel instance.

+
source

fn pending_scripts<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command returns information about pending scripts.

+
source

fn remove<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

source

fn replicas<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Show a list of replicas for this master, and their state.

+
source

fn sentinels<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>,

Show a list of sentinel instances for this master, and their state.

+
source

fn set<R, N, V>(&self, name: N, args: V) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + N: Into<Str>, + V: TryInto<RedisMap>, + V::Error: Into<RedisError>,

Set Sentinel’s monitoring configuration.

+

https://redis.io/topics/sentinel#reconfiguring-sentinel-at-runtime

+
source

fn simulate_failure<R>( + &self, + kind: SentinelFailureKind, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command simulates different Sentinel crash scenarios.

+
source

fn reset<R, P>(&self, pattern: P) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + P: Into<Str>,

This command will reset all the masters with matching name.

+
source

fn config_get<R, K>(&self, name: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<Str>,

Get the current value of a global Sentinel configuration parameter. The specified name may be a wildcard, +similar to the Redis CONFIG GET command.

+
source

fn config_set<R, K, V>( + &self, + name: K, + value: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<Str>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Set the value of a global Sentinel configuration parameter.

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.ServerInterface.html b/doc/fred/interfaces/trait.ServerInterface.html new file mode 100644 index 00000000..54f831f6 --- /dev/null +++ b/doc/fred/interfaces/trait.ServerInterface.html @@ -0,0 +1,62 @@ +ServerInterface in fred::interfaces - Rust

Trait fred::interfaces::ServerInterface

source ·
pub trait ServerInterface: ClientLike {
+    // Provided methods
+    fn bgrewriteaof<R>(&self) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn bgsave<R>(&self) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn dbsize<R>(&self) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn select(&self, db: u8) -> impl Future<Output = RedisResult<()>> { ... }
+    fn failover(
+        &self,
+        to: Option<(String, u16)>,
+        force: bool,
+        abort: bool,
+        timeout: Option<u32>,
+    ) -> impl Future<Output = RedisResult<()>> { ... }
+    fn lastsave<R>(&self) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn wait<R>(
+        &self,
+        numreplicas: i64,
+        timeout: i64,
+    ) -> impl Future<Output = Result<R, RedisError>>
+       where R: FromRedis { ... }
+    fn sentinel_primary(&self) -> Option<Server> { ... }
+    fn sentinel_nodes(&self) -> Option<Vec<Server>> { ... }
+}
Available on crate feature i-server only.
Expand description

Functions that implement the server interface.

+

Provided Methods§

source

fn bgrewriteaof<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Instruct Redis to start an Append Only File rewrite process.

+

https://redis.io/commands/bgrewriteaof

+
source

fn bgsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Save the DB in background.

+

https://redis.io/commands/bgsave

+
source

fn dbsize<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the number of keys in the selected database.

+

https://redis.io/commands/dbsize

+
source

fn select(&self, db: u8) -> impl Future<Output = RedisResult<()>>

Select the database this client should use.

+

https://redis.io/commands/select

+
source

fn failover( + &self, + to: Option<(String, u16)>, + force: bool, + abort: bool, + timeout: Option<u32>, +) -> impl Future<Output = RedisResult<()>>

This command will start a coordinated failover between the currently-connected-to master and one of its +replicas.

+

https://redis.io/commands/failover

+
source

fn lastsave<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

Return the UNIX TIME of the last DB save executed with success.

+

https://redis.io/commands/lastsave

+
source

fn wait<R>( + &self, + numreplicas: i64, + timeout: i64, +) -> impl Future<Output = Result<R, RedisError>>
where + R: FromRedis,

This command blocks the current client until all the previous write commands are successfully transferred and +acknowledged by at least the specified number of replicas. If the timeout, specified in milliseconds, is +reached, the command returns even if the specified number of replicas were not yet reached.

+

https://redis.io/commands/wait/

+
source

fn sentinel_primary(&self) -> Option<Server>

Read the primary Redis server identifier returned from the sentinel nodes.

+
source

fn sentinel_nodes(&self) -> Option<Vec<Server>>

Read the set of known sentinel nodes.

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.SetsInterface.html b/doc/fred/interfaces/trait.SetsInterface.html new file mode 100644 index 00000000..dadb5f54 --- /dev/null +++ b/doc/fred/interfaces/trait.SetsInterface.html @@ -0,0 +1,221 @@ +SetsInterface in fred::interfaces - Rust

Trait fred::interfaces::SetsInterface

source ·
pub trait SetsInterface: ClientLike + Sized {
+
Show 15 methods // Provided methods + fn sadd<R, K, V>( + &self, + key: K, + members: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError> { ... } + fn scard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn sdiff<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn sdiffstore<R, D, K>( + &self, + dest: D, + keys: K, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys> { ... } + fn sinter<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn sinterstore<R, D, K>( + &self, + dest: D, + keys: K, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys> { ... } + fn sismember<R, K, V>( + &self, + key: K, + member: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn smismember<R, K, V>( + &self, + key: K, + members: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError> { ... } + fn smembers<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn smove<R, S, D, V>( + &self, + source: S, + dest: D, + member: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn spop<R, K>( + &self, + key: K, + count: Option<usize>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn srandmember<R, K>( + &self, + key: K, + count: Option<usize>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn srem<R, K, V>( + &self, + key: K, + members: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError> { ... } + fn sunion<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn sunionstore<R, D, K>( + &self, + dest: D, + keys: K, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys> { ... } +
}
Available on crate feature i-sets only.
Expand description

Functions that implement the sets interface.

+

Provided Methods§

source

fn sadd<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Add the specified members to the set stored at key.

+

https://redis.io/commands/sadd

+
source

fn scard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the set cardinality (number of elements) of the set stored at key.

+

https://redis.io/commands/scard

+
source

fn sdiff<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the difference between the first set and all the successive sets.

+

https://redis.io/commands/sdiff

+
source

fn sdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SDIFF, but instead of returning the resulting set, it is stored in destination.

+

https://redis.io/commands/sdiffstore

+
source

fn sinter<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the intersection of all the given sets.

+

https://redis.io/commands/sinter

+
source

fn sinterstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SINTER, but instead of returning the resulting set, it is stored in destination.

+

https://redis.io/commands/sinterstore

+
source

fn sismember<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns if member is a member of the set stored at key.

+

https://redis.io/commands/sismember

+
source

fn smismember<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns whether each member is a member of the set stored at key.

+

https://redis.io/commands/smismember

+
source

fn smembers<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns all the members of the set value stored at key.

+

https://redis.io/commands/smembers

+
source

fn smove<R, S, D, V>( + &self, + source: S, + dest: D, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Move member from the set at source to the set at destination.

+

https://redis.io/commands/smove

+
source

fn spop<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns one or more random members from the set value store at key.

+

https://redis.io/commands/spop

+
source

fn srandmember<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the set value stored at key.

+

If the provided count argument is positive, return an array of distinct elements. The array’s length is either +count or the set’s cardinality (SCARD), whichever is lower.

+

https://redis.io/commands/srandmember

+
source

fn srem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Remove the specified members from the set stored at key.

+

https://redis.io/commands/srem

+
source

fn sunion<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Returns the members of the set resulting from the union of all the given sets.

+

https://redis.io/commands/sunion

+
source

fn sunionstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

This command is equal to SUNION, but instead of returning the resulting set, it is stored in destination.

+

https://redis.io/commands/sunionstore

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.SlowlogInterface.html b/doc/fred/interfaces/trait.SlowlogInterface.html new file mode 100644 index 00000000..ca94731c --- /dev/null +++ b/doc/fred/interfaces/trait.SlowlogInterface.html @@ -0,0 +1,23 @@ +SlowlogInterface in fred::interfaces - Rust

Trait fred::interfaces::SlowlogInterface

source ·
pub trait SlowlogInterface: ClientLike + Sized {
+    // Provided methods
+    fn slowlog_get<R>(
+        &self,
+        count: Option<i64>,
+    ) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn slowlog_length<R>(&self) -> impl Future<Output = RedisResult<R>>
+       where R: FromRedis { ... }
+    fn slowlog_reset(&self) -> impl Future<Output = RedisResult<()>> { ... }
+}
Available on crate feature i-slowlog only.
Expand description

Functions that implement the slowlog interface.

+

Provided Methods§

source

fn slowlog_get<R>( + &self, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read the slow queries log.

+

https://redis.io/commands/slowlog#reading-the-slow-log

+
source

fn slowlog_length<R>(&self) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis,

This command is used to read length of the slow queries log.

+

https://redis.io/commands/slowlog#obtaining-the-current-length-of-the-slow-log

+
source

fn slowlog_reset(&self) -> impl Future<Output = RedisResult<()>>

This command is used to reset the slow queries log.

+

https://redis.io/commands/slowlog#resetting-the-slow-log

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.SortedSetsInterface.html b/doc/fred/interfaces/trait.SortedSetsInterface.html new file mode 100644 index 00000000..05c5a75c --- /dev/null +++ b/doc/fred/interfaces/trait.SortedSetsInterface.html @@ -0,0 +1,707 @@ +SortedSetsInterface in fred::interfaces - Rust

Trait fred::interfaces::SortedSetsInterface

source ·
pub trait SortedSetsInterface: ClientLike + Sized {
+
Show 33 methods // Provided methods + fn bzmpop<R, K>( + &self, + timeout: f64, + keys: K, + sort: ZCmp, + count: Option<i64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn bzpopmin<R, K>( + &self, + keys: K, + timeout: f64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn bzpopmax<R, K>( + &self, + keys: K, + timeout: f64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn zadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + ordering: Option<Ordering>, + changed: bool, + incr: bool, + values: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleZaddValues>, + V::Error: Into<RedisError> { ... } + fn zcard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn zcount<R, K>( + &self, + key: K, + min: f64, + max: f64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn zdiff<R, K>( + &self, + keys: K, + withscores: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn zdiffstore<R, D, K>( + &self, + dest: D, + keys: K, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys> { ... } + fn zincrby<R, K, V>( + &self, + key: K, + increment: f64, + member: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn zinter<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights> { ... } + fn zinterstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights> { ... } + fn zlexcount<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError> { ... } + fn zpopmax<R, K>( + &self, + key: K, + count: Option<usize>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn zpopmin<R, K>( + &self, + key: K, + count: Option<usize>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn zmpop<R, K>( + &self, + keys: K, + sort: ZCmp, + count: Option<i64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys> { ... } + fn zrandmember<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn zrangestore<R, D, S, M, N>( + &self, + dest: D, + source: S, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError> { ... } + fn zrange<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, + withscores: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError> { ... } + fn zrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + limit: Option<Limit>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError> { ... } + fn zrevrangebylex<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + limit: Option<Limit>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError> { ... } + fn zrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + withscores: bool, + limit: Option<Limit>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError> { ... } + fn zrevrangebyscore<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + withscores: bool, + limit: Option<Limit>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError> { ... } + fn zrank<R, K, V>( + &self, + key: K, + member: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn zrem<R, K, V>( + &self, + key: K, + members: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError> { ... } + fn zremrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError> { ... } + fn zremrangebyrank<R, K>( + &self, + key: K, + start: i64, + stop: i64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn zremrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError> { ... } + fn zrevrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, + withscores: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn zrevrank<R, K, V>( + &self, + key: K, + member: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn zscore<R, K, V>( + &self, + key: K, + member: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError> { ... } + fn zunion<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights> { ... } + fn zunionstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights> { ... } + fn zmscore<R, K, V>( + &self, + key: K, + members: V, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError> { ... } +
}
Available on crate feature i-sorted-sets only.
Expand description

Functions that implement the sorted sets interface.

+

Provided Methods§

source

fn bzmpop<R, K>( + &self, + timeout: f64, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

source

fn bzpopmin<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

source

fn bzpopmax<R, K>( + &self, + keys: K, + timeout: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

source

fn zadd<R, K, V>( + &self, + key: K, + options: Option<SetOptions>, + ordering: Option<Ordering>, + changed: bool, + incr: bool, + values: V, +) -> impl Future<Output = RedisResult<R>>

Adds all the specified members with the specified scores to the sorted set stored at key.

+

https://redis.io/commands/zadd

+
source

fn zcard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the sorted set cardinality (number of elements) of the sorted set stored at key.

+

https://redis.io/commands/zcard

+
source

fn zcount<R, K>( + &self, + key: K, + min: f64, + max: f64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of elements in the sorted set at key with a score between min and max.

+

https://redis.io/commands/zcount

+
source

fn zdiff<R, K>( + &self, + keys: K, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

This command is similar to ZDIFFSTORE, but instead of storing the resulting sorted set, it is returned to the +client.

+

https://redis.io/commands/zdiff

+
source

fn zdiffstore<R, D, K>( + &self, + dest: D, + keys: K, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>,

Computes the difference between the first and all successive input sorted sets and stores the result in +destination.

+

https://redis.io/commands/zdiffstore

+
source

fn zincrby<R, K, V>( + &self, + key: K, + increment: f64, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Increments the score of member in the sorted set stored at key by increment.

+

https://redis.io/commands/zincrby

+
source

fn zinter<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZINTERSTORE, but instead of storing the resulting sorted set, it is returned to the +client.

+

https://redis.io/commands/zinter

+
source

fn zinterstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the intersection of the sorted sets given by the specified keys, and stores the result in +destination.

+

https://redis.io/commands/zinterstore

+
source

fn zlexcount<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns the number of elements in the sorted set at key with a value between min and +max.

+

https://redis.io/commands/zlexcount

+
source

fn zpopmax<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the highest scores in the sorted set stored at key.

+

https://redis.io/commands/zpopmax

+
source

fn zpopmin<R, K>( + &self, + key: K, + count: Option<usize>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes and returns up to count members with the lowest scores in the sorted set stored at key.

+

https://redis.io/commands/zpopmin

+
source

fn zmpop<R, K>( + &self, + keys: K, + sort: ZCmp, + count: Option<i64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>,

Pops one or more elements, that are member-score pairs, from the first non-empty sorted set in the provided list +of key names.

+

https://redis.io/commands/zmpop/

+
source

fn zrandmember<R, K>( + &self, + key: K, + count: Option<(i64, bool)>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

When called with just the key argument, return a random element from the sorted set value stored at key.

+

https://redis.io/commands/zrandmember

+
source

fn zrangestore<R, D, S, M, N>( + &self, + dest: D, + source: S, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + S: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

This command is like ZRANGE, but stores the result in the destination key.

+

https://redis.io/commands/zrangestore

+
source

fn zrange<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + sort: Option<ZSort>, + rev: bool, + limit: Option<Limit>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns the specified range of elements in the sorted set stored at key.

+

https://redis.io/commands/zrange

+
source

fn zrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between min and max.

+

https://redis.io/commands/zrangebylex

+
source

fn zrevrangebylex<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command returns all the elements in the sorted set at key with a value between max and min.

+

https://redis.io/commands/zrevrangebylex

+
source

fn zrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between min and max (including elements +with score equal to min or max).

+

https://redis.io/commands/zrangebyscore

+
source

fn zrevrangebyscore<R, K, M, N>( + &self, + key: K, + max: M, + min: N, + withscores: bool, + limit: Option<Limit>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Returns all the elements in the sorted set at key with a score between max and min (including +elements with score equal to max or min).

+

https://redis.io/commands/zrevrangebyscore

+
source

fn zrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from low to high.

+

https://redis.io/commands/zrank

+
source

fn zrem<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Removes the specified members from the sorted set stored at key. Non existing members are ignored.

+

https://redis.io/commands/zrem

+
source

fn zremrangebylex<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

When all the elements in a sorted set are inserted with the same score, in order to force lexicographical +ordering, this command removes all elements in the sorted set stored at key between the lexicographical range +specified by min and max.

+

https://redis.io/commands/zremrangebylex

+
source

fn zremrangebyrank<R, K>( + &self, + key: K, + start: i64, + stop: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Removes all elements in the sorted set stored at key with rank between start and stop.

+

https://redis.io/commands/zremrangebyrank

+
source

fn zremrangebyscore<R, K, M, N>( + &self, + key: K, + min: M, + max: N, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + M: TryInto<ZRange>, + M::Error: Into<RedisError>, + N: TryInto<ZRange>, + N::Error: Into<RedisError>,

Removes all elements in the sorted set stored at key with a score between min and max.

+

https://redis.io/commands/zremrangebyscore

+
source

fn zrevrange<R, K>( + &self, + key: K, + start: i64, + stop: i64, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the specified range of elements in the sorted set stored at key.

+

https://redis.io/commands/zrevrange

+
source

fn zrevrank<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the rank of member in the sorted set stored at key, with the scores ordered from high to low.

+

https://redis.io/commands/zrevrank

+
source

fn zscore<R, K, V>( + &self, + key: K, + member: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

Returns the score of member in the sorted set at key.

+

https://redis.io/commands/zscore

+
source

fn zunion<R, K, W>( + &self, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, + withscores: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

This command is similar to ZUNIONSTORE, but instead of storing the resulting sorted set, it is returned to the +client.

+

https://redis.io/commands/zunion

+
source

fn zunionstore<R, D, K, W>( + &self, + dest: D, + keys: K, + weights: W, + aggregate: Option<AggregateOptions>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + D: Into<RedisKey>, + K: Into<MultipleKeys>, + W: Into<MultipleWeights>,

Computes the union of the sorted sets given by the specified keys, and stores the result in destination.

+

https://redis.io/commands/zunionstore

+
source

fn zmscore<R, K, V>( + &self, + key: K, + members: V, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + V: TryInto<MultipleValues>, + V::Error: Into<RedisError>,

Returns the scores associated with the specified members in the sorted set stored at key.

+

https://redis.io/commands/zmscore

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.StreamsInterface.html b/doc/fred/interfaces/trait.StreamsInterface.html new file mode 100644 index 00000000..fa8cabfc --- /dev/null +++ b/doc/fred/interfaces/trait.StreamsInterface.html @@ -0,0 +1,689 @@ +StreamsInterface in fred::interfaces - Rust

Trait fred::interfaces::StreamsInterface

source ·
pub trait StreamsInterface: ClientLike + Sized {
+
Show 26 methods // Provided methods + fn xinfo_consumers<R, K, S>( + &self, + key: K, + groupname: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + S: Into<Str> { ... } + fn xinfo_groups<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn xinfo_stream<R, K>( + &self, + key: K, + full: bool, + count: Option<u64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn xadd<R, K, C, I, F>( + &self, + key: K, + nomkstream: bool, + cap: C, + id: I, + fields: F, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + I: Into<XID>, + F: TryInto<MultipleOrderedPairs>, + F::Error: Into<RedisError>, + C: TryInto<XCap>, + C::Error: Into<RedisError> { ... } + fn xtrim<R, K, C>( + &self, + key: K, + cap: C, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + C: TryInto<XCap>, + C::Error: Into<RedisError> { ... } + fn xdel<R, K, S>( + &self, + key: K, + ids: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings> { ... } + fn xrange_values<Ri, Rk, Rv, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, + ) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>> + where Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError> { ... } + fn xrange<R, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError> { ... } + fn xrevrange_values<Ri, Rk, Rv, K, E, S>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, + ) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>> + where Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError> { ... } + fn xrevrange<R, K, S, E>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError> { ... } + fn xlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn xread_map<Rk1, Rk2, Rk3, Rv, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, + ) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>> + where Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs> { ... } + fn xread<R, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs> { ... } + fn xgroup_create<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, + mkstream: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID> { ... } + fn xgroup_createconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str> { ... } + fn xgroup_delconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str> { ... } + fn xgroup_destroy<R, K, S>( + &self, + key: K, + groupname: S, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + S: Into<Str> { ... } + fn xgroup_setid<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID> { ... } + fn xreadgroup_map<Rk1, Rk2, Rk3, Rv, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, + ) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>> + where Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs> { ... } + fn xreadgroup<R, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs> { ... } + fn xack<R, K, G, I>( + &self, + key: K, + group: G, + ids: I, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + I: Into<MultipleIDs> { ... } + fn xclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, + ) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>> + where Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs> { ... } + fn xclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs> { ... } + fn xautoclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, + ) -> impl Future<Output = RedisResult<(String, Vec<XReadValue<Ri, Rk, Rv>>)>> + where Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID> { ... } + fn xautoclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID> { ... } + fn xpending<R, K, G, A>( + &self, + key: K, + group: G, + args: A, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + A: Into<XPendingArgs> { ... } +
}
Available on crate feature i-streams only.
Expand description

Functions that implement the streams interface.

+

Note: Several of the stream commands can return types with verbose type declarations. Additionally, certain +commands can be parsed differently in RESP2 and RESP3 modes. Functions such as xread_map, +xreadgroup_map, xrange_values, etc exist to make this easier for +callers. These functions apply an additional layer of parsing logic that can make declaring response types easier, +as well as automatically handling any differences between RESP2 and RESP3 return value types.

+

Provided Methods§

source

fn xinfo_consumers<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

This command returns the list of consumers that belong to the groupname consumer group of the stream stored at +key.

+

https://redis.io/commands/xinfo-consumers

+
source

fn xinfo_groups<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns the list of all consumers groups of the stream stored at key.

+

https://redis.io/commands/xinfo-groups

+
source

fn xinfo_stream<R, K>( + &self, + key: K, + full: bool, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

This command returns information about the stream stored at key.

+

https://redis.io/commands/xinfo-stream

+
source

fn xadd<R, K, C, I, F>( + &self, + key: K, + nomkstream: bool, + cap: C, + id: I, + fields: F, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: Into<XID>, + F: TryInto<MultipleOrderedPairs>, + F::Error: Into<RedisError>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Appends the specified stream entry to the stream at the specified key. If the key does not exist, as a side +effect of running this command the key is created with a stream value. The creation of stream’s key can be +disabled with the NOMKSTREAM option.

+

https://redis.io/commands/xadd

+
source

fn xtrim<R, K, C>(&self, key: K, cap: C) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + C: TryInto<XCap>, + C::Error: Into<RedisError>,

Trims the stream by evicting older entries (entries with lower IDs) if needed.

+

https://redis.io/commands/xtrim

+
source

fn xdel<R, K, S>(&self, key: K, ids: S) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<MultipleStrings>,

Removes the specified entries from a stream, and returns the number of entries deleted.

+

https://redis.io/commands/xdel

+
source

fn xrange_values<Ri, Rk, Rv, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Return the stream entries matching the provided range of IDs, automatically converting to a less verbose type +definition.

+

https://redis.io/commands/xrange

+
source

fn xrange<R, K, S, E>( + &self, + key: K, + start: S, + end: E, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

The command returns the stream entries matching a given range of IDs. The range is specified by a minimum +and maximum ID. All the entries having an ID between the two specified or exactly one of the two IDs specified +(closed interval) are returned.

+

https://redis.io/commands/xrange

+

See xrange_values for a variation of this function that may be more useful.

+
source

fn xrevrange_values<Ri, Rk, Rv, K, E, S>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + S: TryInto<RedisValue>, + S::Error: Into<RedisError>, + E: TryInto<RedisValue>, + E::Error: Into<RedisError>,

Similar to XRANGE, but with the results returned in reverse order. The results will be automatically converted +to a less verbose type definition.

+

https://redis.io/commands/xrevrange

+
source

fn xrevrange<R, K, S, E>( + &self, + key: K, + end: E, + start: S, + count: Option<u64>, +) -> impl Future<Output = RedisResult<R>>

Similar to XRANGE, but with the results returned in reverse order.

+

https://redis.io/commands/xrevrange

+

See the xrevrange_values for a variation of this function that may be more useful.

+
source

fn xlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Returns the number of entries inside a stream.

+

https://redis.io/commands/xlen

+
source

fn xread_map<Rk1, Rk2, Rk3, Rv, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller.

+

https://redis.io/commands/xread

+

The XREAD and XREADGROUP commands return values that can be interpreted differently in RESP2 and RESP3 mode. +In many cases it is also easier to operate on the return values of these functions as a HashMap, but +manually declaring this type can be very verbose. This function will automatically convert the response to the +most common map representation while also handling the encoding differences +between RESP2 and RESP3.

+ +
async fn example(client: RedisClient) -> Result<(), RedisError> {
+  // borrowed from the tests. XREAD and XREADGROUP are very similar.
+  let result: XReadResponse<String, String, String, usize> = client  
+    .xreadgroup_map("group1", "consumer1", None, None, false, "foo", ">")
+    .await?;
+  println!("Result: {:?}", result);    
+  // Result: {"foo": [("1646240801081-0", {"count": 0}), ("1646240801082-0", {"count": 1}), ("1646240801082-1", {"count": 2})]}
+
+  assert_eq!(result.len(), 1);
+  for (idx, (id, record)) in result.get("foo").unwrap().into_iter().enumerate() {
+    let value = record.get("count").expect("Failed to read count");
+    assert_eq!(idx, *value);
+  }
+
+  Ok(())
+}
+
source

fn xread<R, K, I>( + &self, + count: Option<u64>, + block: Option<u64>, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

Read data from one or multiple streams, only returning entries with an ID greater than the last received ID +reported by the caller.

+

https://redis.io/commands/xread

+

See xread_map for more information on a variation of this function that might be more +useful.

+
source

fn xgroup_create<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, + mkstream: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

This command creates a new consumer group uniquely identified by groupname for the stream stored at key.

+

https://redis.io/commands/xgroup-create

+
source

fn xgroup_createconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Create a consumer named consumername in the consumer group groupname of the stream that’s stored at key.

+

https://redis.io/commands/xgroup-createconsumer

+
source

fn xgroup_delconsumer<R, K, G, C>( + &self, + key: K, + groupname: G, + consumername: C, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>,

Delete a consumer named consumername in the consumer group groupname of the stream that’s stored at key.

+

https://redis.io/commands/xgroup-delconsumer

+
source

fn xgroup_destroy<R, K, S>( + &self, + key: K, + groupname: S, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>,

Completely destroy a consumer group.

+

https://redis.io/commands/xgroup-destroy

+
source

fn xgroup_setid<R, K, S, I>( + &self, + key: K, + groupname: S, + id: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + S: Into<Str>, + I: Into<XID>,

Set the last delivered ID for a consumer group.

+

https://redis.io/commands/xgroup-setid

+
source

fn xreadgroup_map<Rk1, Rk2, Rk3, Rv, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>>
where + Rk1: FromRedisKey + Hash + Eq, + Rk2: FromRedis, + Rk3: FromRedisKey + Hash + Eq, + Rv: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups.

+

Declaring proper type declarations for this command can be complicated due to the complex nature of the response +values and the differences between RESP2 and RESP3. See the xread documentation for more +information.

+

https://redis.io/commands/xreadgroup

+

The XREAD and XREADGROUP commands return values that can be interpreted differently in RESP2 and RESP3 mode. +In many cases it is also easier to operate on the return values of these functions as a HashMap, but +manually declaring this type can be very verbose. This function will automatically convert the response to the +most common map representation while also handling the encoding differences +between RESP2 and RESP3.

+

See the xread_map documentation for more information.

+
source

fn xreadgroup<R, G, C, K, I>( + &self, + group: G, + consumer: C, + count: Option<u64>, + block: Option<u64>, + noack: bool, + keys: K, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + G: Into<Str>, + C: Into<Str>, + K: Into<MultipleKeys>, + I: Into<MultipleIDs>,

A special version of the XREAD command with support for consumer groups.

+

Declaring proper type declarations for this command can be complicated due to the complex nature of the response +values and the differences between RESP2 and RESP3. See the xread documentation for more +information.

+

https://redis.io/commands/xreadgroup

+

See xreadgroup_map for a variation of this function that might be more useful.

+
source

fn xack<R, K, G, I>( + &self, + key: K, + group: G, + ids: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + I: Into<MultipleIDs>,

Remove one or more messages from the Pending Entries List (PEL) of a stream consumer group.

+

https://redis.io/commands/xack

+
source

fn xclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

A variation of xclaim with a less verbose return type.

+
source

fn xclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + ids: I, + idle: Option<u64>, + time: Option<u64>, + retry_count: Option<u64>, + force: bool, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<MultipleIDs>,

In the context of a stream consumer group, this command changes the ownership of a pending message, +so that the new owner is the consumer specified as the command argument.

+

https://redis.io/commands/xclaim

+

See xclaim_values for a variation of this function that might be more useful.

+
source

fn xautoclaim_values<Ri, Rk, Rv, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<(String, Vec<XReadValue<Ri, Rk, Rv>>)>>
where + Ri: FromRedis, + Rk: FromRedisKey + Hash + Eq, + Rv: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria. It also converts +the response type to a less verbose type declaration and handles potential differences between RESP2 and RESP3.

+

https://redis.io/commands/xautoclaim

+
source

fn xautoclaim<R, K, G, C, I>( + &self, + key: K, + group: G, + consumer: C, + min_idle_time: u64, + start: I, + count: Option<u64>, + justid: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + C: Into<Str>, + I: Into<XID>,

This command transfers ownership of pending stream entries that match the specified criteria.

+

https://redis.io/commands/xautoclaim

+

Note: See xautoclaim_values for a variation of this function that may be more +useful.

+
source

fn xpending<R, K, G, A>( + &self, + key: K, + group: G, + args: A, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + G: Into<Str>, + A: Into<XPendingArgs>,

Inspect the list of pending messages in a consumer group.

+

https://redis.io/commands/xpending

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.TimeSeriesInterface.html b/doc/fred/interfaces/trait.TimeSeriesInterface.html new file mode 100644 index 00000000..ea1bf8fc --- /dev/null +++ b/doc/fred/interfaces/trait.TimeSeriesInterface.html @@ -0,0 +1,450 @@ +TimeSeriesInterface in fred::interfaces - Rust

Trait fred::interfaces::TimeSeriesInterface

source ·
pub trait TimeSeriesInterface: ClientLike {
+
Show 17 methods // Provided methods + fn ts_add<R, K, T, L>( + &self, + key: K, + timestamp: T, + value: f64, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + on_duplicate: Option<DuplicatePolicy>, + labels: L, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + T: TryInto<Timestamp>, + T::Error: Into<RedisError>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError> { ... } + fn ts_alter<R, K, L>( + &self, + key: K, + retention: Option<u64>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError> { ... } + fn ts_create<R, K, L>( + &self, + key: K, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError> { ... } + fn ts_createrule<R, S, D>( + &self, + src: S, + dest: D, + aggregation: (Aggregator, u64), + align_timestamp: Option<u64>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey> { ... } + fn ts_decrby<R, K, L>( + &self, + key: K, + subtrahend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError> { ... } + fn ts_del<R, K>( + &self, + key: K, + from: i64, + to: i64, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn ts_deleterule<R, S, D>( + &self, + src: S, + dest: D, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey> { ... } + fn ts_get<R, K>( + &self, + key: K, + latest: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn ts_incrby<R, K, L>( + &self, + key: K, + addend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError> { ... } + fn ts_info<R, K>( + &self, + key: K, + debug: bool, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey> { ... } + fn ts_madd<R, K, I>( + &self, + samples: I, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + I: IntoIterator<Item = (K, Timestamp, f64)> { ... } + fn ts_mget<R, L, S, I>( + &self, + latest: bool, + labels: Option<L>, + filters: I, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + L: Into<GetLabels>, + S: Into<Str>, + I: IntoIterator<Item = S> { ... } + fn ts_mrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S> { ... } + fn ts_mrevrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S> { ... } + fn ts_queryindex<R, S, I>( + &self, + filters: I, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + S: Into<Str>, + I: IntoIterator<Item = S> { ... } + fn ts_range<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64> { ... } + fn ts_revrange<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + ) -> impl Future<Output = RedisResult<R>> + where R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64> { ... } +
}
Available on crate feature i-time-series only.
Expand description

A Redis Timeseries interface.

+

Provided Methods§

source

fn ts_add<R, K, T, L>( + &self, + key: K, + timestamp: T, + value: f64, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + on_duplicate: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + T: TryInto<Timestamp>, + T::Error: Into<RedisError>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Append a sample to a time series.

+

https://redis.io/commands/ts.add/

+
source

fn ts_alter<R, K, L>( + &self, + key: K, + retention: Option<u64>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Update the retention, chunk size, duplicate policy, and labels of an existing time series.

+

https://redis.io/commands/ts.alter/

+
source

fn ts_create<R, K, L>( + &self, + key: K, + retention: Option<u64>, + encoding: Option<Encoding>, + chunk_size: Option<u64>, + duplicate_policy: Option<DuplicatePolicy>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Create a new time series.

+

https://redis.io/commands/ts.create/

+
source

fn ts_createrule<R, S, D>( + &self, + src: S, + dest: D, + aggregation: (Aggregator, u64), + align_timestamp: Option<u64>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Create a compaction rule.

+

https://redis.io/commands/ts.createrule/

+
source

fn ts_decrby<R, K, L>( + &self, + key: K, + subtrahend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given decrement.

+

https://redis.io/commands/ts.decrby/

+
source

fn ts_del<R, K>( + &self, + key: K, + from: i64, + to: i64, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Delete all samples between two timestamps for a given time series.

+

https://redis.io/commands/ts.del/

+
source

fn ts_deleterule<R, S, D>( + &self, + src: S, + dest: D, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<RedisKey>, + D: Into<RedisKey>,

Delete a compaction rule.

+

https://redis.io/commands/ts.deleterule/

+
source

fn ts_get<R, K>( + &self, + key: K, + latest: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Get the sample with the highest timestamp from a given time series.

+

https://redis.io/commands/ts.get/

+
source

fn ts_incrby<R, K, L>( + &self, + key: K, + addend: f64, + timestamp: Option<Timestamp>, + retention: Option<u64>, + uncompressed: bool, + chunk_size: Option<u64>, + labels: L, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + L: TryInto<RedisMap>, + L::Error: Into<RedisError>,

Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal +to the value of the sample with the maximum existing timestamp with a given increment.

+

https://redis.io/commands/ts.incrby/

+
source

fn ts_info<R, K>( + &self, + key: K, + debug: bool, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>,

Return information and statistics for a time series.

+

https://redis.io/commands/ts.info/

+
source

fn ts_madd<R, K, I>(&self, samples: I) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + I: IntoIterator<Item = (K, Timestamp, f64)>,

Append new samples to one or more time series.

+

https://redis.io/commands/ts.madd/

+
source

fn ts_mget<R, L, S, I>( + &self, + latest: bool, + labels: Option<L>, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + L: Into<GetLabels>, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get the sample with the highest timestamp from each time series matching a specific filter.

+

See Resp2TimeSeriesValues and +Resp3TimeSeriesValues for more information.

+

https://redis.io/commands/ts.mget/

+
source

fn ts_mrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the forward direction.

+

See Resp2TimeSeriesValues and +Resp3TimeSeriesValues for more information.

+

https://redis.io/commands/ts.mrange/

+
source

fn ts_mrevrange<R, F, T, I, S, J>( + &self, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + labels: Option<GetLabels>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, + filters: J, + group_by: Option<GroupBy>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + S: Into<Str>, + I: IntoIterator<Item = i64>, + J: IntoIterator<Item = S>,

Query a range across multiple time series by filters in the reverse direction.

+

See Resp2TimeSeriesValues and +Resp3TimeSeriesValues for more information.

+

https://redis.io/commands/ts.mrevrange/

+
source

fn ts_queryindex<R, S, I>( + &self, + filters: I, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + S: Into<Str>, + I: IntoIterator<Item = S>,

Get all time series keys matching a filter list.

+

https://redis.io/commands/ts.queryindex/

+
source

fn ts_range<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in forward direction.

+

https://redis.io/commands/ts.range/

+
source

fn ts_revrange<R, K, F, T, I>( + &self, + key: K, + from: F, + to: T, + latest: bool, + filter_by_ts: I, + filter_by_value: Option<(i64, i64)>, + count: Option<u64>, + aggregation: Option<RangeAggregation>, +) -> impl Future<Output = RedisResult<R>>
where + R: FromRedis, + K: Into<RedisKey>, + F: TryInto<GetTimestamp>, + F::Error: Into<RedisError>, + T: TryInto<GetTimestamp>, + T::Error: Into<RedisError>, + I: IntoIterator<Item = i64>,

Query a range in reverse direction.

+

https://redis.io/commands/ts.revrange/

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.TrackingInterface.html b/doc/fred/interfaces/trait.TrackingInterface.html new file mode 100644 index 00000000..de233d39 --- /dev/null +++ b/doc/fred/interfaces/trait.TrackingInterface.html @@ -0,0 +1,35 @@ +TrackingInterface in fred::interfaces - Rust

Trait fred::interfaces::TrackingInterface

source ·
pub trait TrackingInterface: ClientLike + Sized {
+    // Provided methods
+    fn start_tracking<P>(
+        &self,
+        prefixes: P,
+        bcast: bool,
+        optin: bool,
+        optout: bool,
+        noloop: bool,
+    ) -> impl Future<Output = RedisResult<()>>
+       where P: Into<MultipleStrings> { ... }
+    fn stop_tracking(&self) -> impl Future<Output = RedisResult<()>> { ... }
+    fn on_invalidation<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
+       where F: Fn(Invalidation) -> RedisResult<()> + 'static { ... }
+    fn invalidation_rx(&self) -> BroadcastReceiver<Invalidation> { ... }
+}
Available on crate feature i-tracking only.
Expand description

A high level interface that supports client side caching via the client tracking interface.

+

Provided Methods§

source

fn start_tracking<P>( + &self, + prefixes: P, + bcast: bool, + optin: bool, + optout: bool, + noloop: bool, +) -> impl Future<Output = RedisResult<()>>
where + P: Into<MultipleStrings>,

Send the CLIENT TRACKING command to all connected servers, subscribing to invalidation messages on the same connection.

+

This interface requires the RESP3 protocol mode and supports all server deployment types (centralized, +clustered, and sentinel).

+

See the basic client tracking function for more +information on the underlying commands.

+
source

fn stop_tracking(&self) -> impl Future<Output = RedisResult<()>>

Disable client tracking on all connections.

+
source

fn on_invalidation<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
where + F: Fn(Invalidation) -> RedisResult<()> + 'static,

Spawn a task that processes invalidation messages from the server.

+

See invalidation_rx for a more flexible variation of this function.

+
source

fn invalidation_rx(&self) -> BroadcastReceiver<Invalidation>

Subscribe to invalidation messages from the server(s).

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/trait.TransactionInterface.html b/doc/fred/interfaces/trait.TransactionInterface.html new file mode 100644 index 00000000..95a56154 --- /dev/null +++ b/doc/fred/interfaces/trait.TransactionInterface.html @@ -0,0 +1,8 @@ +TransactionInterface in fred::interfaces - Rust

Trait fred::interfaces::TransactionInterface

source ·
pub trait TransactionInterface: ClientLike + Sized {
+    // Provided method
+    fn multi(&self) -> Transaction { ... }
+}
Available on crate feature transactions only.
Expand description

Functions that implement the transactions interface.

+

See the Transaction client for more information;

+

Provided Methods§

source

fn multi(&self) -> Transaction

Enter a MULTI block, executing subsequent commands as a transaction.

+

https://redis.io/commands/multi

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/doc/fred/interfaces/type.RedisResult.html b/doc/fred/interfaces/type.RedisResult.html new file mode 100644 index 00000000..73af58a3 --- /dev/null +++ b/doc/fred/interfaces/type.RedisResult.html @@ -0,0 +1,7 @@ +RedisResult in fred::interfaces - Rust

Type Alias fred::interfaces::RedisResult

source ·
pub type RedisResult<T> = Result<T, RedisError>;
Expand description

Type alias for Result<T, RedisError>.

+

Aliased Type§

enum RedisResult<T> {
+    Ok(T),
+    Err(RedisError),
+}

Variants§

§1.0.0

Ok(T)

Contains the success value

+
§1.0.0

Err(RedisError)

Contains the error value

+
\ No newline at end of file diff --git a/doc/fred/macro.cmd!.html b/doc/fred/macro.cmd!.html new file mode 100644 index 00000000..05bc5577 --- /dev/null +++ b/doc/fred/macro.cmd!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.cmd.html...

+ + + \ No newline at end of file diff --git a/doc/fred/macro.cmd.html b/doc/fred/macro.cmd.html new file mode 100644 index 00000000..15b10251 --- /dev/null +++ b/doc/fred/macro.cmd.html @@ -0,0 +1,14 @@ +cmd in fred - Rust

Macro fred::cmd

source ·
macro_rules! cmd {
+    ($name:expr) => { ... };
+    ($name:expr, blocking: $blk:expr) => { ... };
+    ($name:expr, hash: $hash:expr) => { ... };
+    ($name:expr, hash: $hash:expr, blocking: $blk:expr) => { ... };
+}
Expand description

Shorthand to create a CustomCommand.

+ +
let _cmd = cmd!("FOO.BAR");
+let _cmd = cmd!("FOO.BAR", blocking: true);
+let _cmd = cmd!("FOO.BAR", hash: ClusterHash::FirstKey);
+let _cmd = cmd!("FOO.BAR", hash: ClusterHash::FirstKey, blocking: true);
+// which is shorthand for
+let _cmd = CustomCommand::new("FOO.BAR", ClusterHash::FirstKey, true);
+
\ No newline at end of file diff --git a/doc/fred/macro.json_quote!.html b/doc/fred/macro.json_quote!.html new file mode 100644 index 00000000..db13cc73 --- /dev/null +++ b/doc/fred/macro.json_quote!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.json_quote.html...

+ + + \ No newline at end of file diff --git a/doc/fred/macro.json_quote.html b/doc/fred/macro.json_quote.html new file mode 100644 index 00000000..e95b8f78 --- /dev/null +++ b/doc/fred/macro.json_quote.html @@ -0,0 +1,5 @@ +json_quote in fred - Rust

Macro fred::json_quote

source ·
macro_rules! json_quote {
+    ($($json:tt)+) => { ... };
+}
Available on crate feature i-redis-json only.
Expand description

A helper macro to wrap a string value in quotes via the json macro.

+

See the RedisJSON interface for more information.

+
\ No newline at end of file diff --git a/doc/fred/modules/metrics/struct.Stats.html b/doc/fred/modules/metrics/struct.Stats.html new file mode 100644 index 00000000..380daac7 --- /dev/null +++ b/doc/fred/modules/metrics/struct.Stats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.Stats.html...

+ + + \ No newline at end of file diff --git a/doc/fred/modules/response/trait.FromRedis.html b/doc/fred/modules/response/trait.FromRedis.html new file mode 100644 index 00000000..a3bb4862 --- /dev/null +++ b/doc/fred/modules/response/trait.FromRedis.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/trait.FromRedis.html...

+ + + \ No newline at end of file diff --git a/doc/fred/modules/response/trait.FromRedisKey.html b/doc/fred/modules/response/trait.FromRedisKey.html new file mode 100644 index 00000000..35a36a35 --- /dev/null +++ b/doc/fred/modules/response/trait.FromRedisKey.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/trait.FromRedisKey.html...

+ + + \ No newline at end of file diff --git a/doc/fred/monitor/fn.run.html b/doc/fred/monitor/fn.run.html new file mode 100644 index 00000000..bd73d043 --- /dev/null +++ b/doc/fred/monitor/fn.run.html @@ -0,0 +1,5 @@ +run in fred::monitor - Rust

Function fred::monitor::run

source ·
pub async fn run(
+    config: RedisConfig,
+) -> Result<impl Stream<Item = Command>, RedisError>
Available on crate feature monitor only.
Expand description

Run the MONITOR command against the provided server.

+

Currently only centralized configurations are supported.

+
\ No newline at end of file diff --git a/doc/fred/monitor/index.html b/doc/fred/monitor/index.html new file mode 100644 index 00000000..fa0345cd --- /dev/null +++ b/doc/fred/monitor/index.html @@ -0,0 +1,2 @@ +fred::monitor - Rust

Module fred::monitor

source ·
Available on crate feature monitor only.
Expand description

An interface to run the MONITOR command.

+

Structs§

Functions§

  • Run the MONITOR command against the provided server.
\ No newline at end of file diff --git a/doc/fred/monitor/sidebar-items.js b/doc/fred/monitor/sidebar-items.js new file mode 100644 index 00000000..e1e3fc6b --- /dev/null +++ b/doc/fred/monitor/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["run"],"struct":["Command"]}; \ No newline at end of file diff --git a/doc/fred/monitor/struct.Command.html b/doc/fred/monitor/struct.Command.html new file mode 100644 index 00000000..75482bca --- /dev/null +++ b/doc/fred/monitor/struct.Command.html @@ -0,0 +1,46 @@ +Command in fred::monitor - Rust

Struct fred::monitor::Command

source ·
pub struct Command {
+    pub command: String,
+    pub args: Vec<RedisValue>,
+    pub timestamp: f64,
+    pub db: u8,
+    pub client: String,
+}
Available on crate feature monitor only.
Expand description

A command parsed from a MONITOR stream.

+

Formatting with the Display trait will print the same output as redis-cli.

+

Fields§

§command: String

The command run by the server.

+
§args: Vec<RedisValue>

Arguments passed to the command.

+
§timestamp: f64

When the command was run on the server.

+
§db: u8

The database against which the command was run.

+
§client: String

The host and port of the client that ran the command, or lua when run from a script.

+

Trait Implementations§

source§

impl Clone for Command

source§

fn clone(&self) -> Command

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Command

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for Command

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Command

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for Command

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/prelude/index.html b/doc/fred/prelude/index.html new file mode 100644 index 00000000..6e4c5320 --- /dev/null +++ b/doc/fred/prelude/index.html @@ -0,0 +1,3 @@ +fred::prelude - Rust

Module fred::prelude

source ·
Expand description

Convenience module to import a RedisClient, all possible interfaces, error types, and common argument types or +return value types.

+

Re-exports§

\ No newline at end of file diff --git a/doc/fred/prelude/sidebar-items.js b/doc/fred/prelude/sidebar-items.js new file mode 100644 index 00000000..5244ce01 --- /dev/null +++ b/doc/fred/prelude/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {}; \ No newline at end of file diff --git a/doc/fred/protocol/hashers/enum.ClusterHash.html b/doc/fred/protocol/hashers/enum.ClusterHash.html new file mode 100644 index 00000000..94ad184c --- /dev/null +++ b/doc/fred/protocol/hashers/enum.ClusterHash.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ClusterHash.html...

+ + + \ No newline at end of file diff --git a/doc/fred/protocol/tls/enum.TlsConnector.html b/doc/fred/protocol/tls/enum.TlsConnector.html new file mode 100644 index 00000000..171d34e0 --- /dev/null +++ b/doc/fred/protocol/tls/enum.TlsConnector.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.TlsConnector.html...

+ + + \ No newline at end of file diff --git a/doc/fred/protocol/tls/enum.TlsHostMapping.html b/doc/fred/protocol/tls/enum.TlsHostMapping.html new file mode 100644 index 00000000..87ee90d6 --- /dev/null +++ b/doc/fred/protocol/tls/enum.TlsHostMapping.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.TlsHostMapping.html...

+ + + \ No newline at end of file diff --git a/doc/fred/protocol/tls/struct.TlsConfig.html b/doc/fred/protocol/tls/struct.TlsConfig.html new file mode 100644 index 00000000..408f1998 --- /dev/null +++ b/doc/fred/protocol/tls/struct.TlsConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.TlsConfig.html...

+ + + \ No newline at end of file diff --git a/doc/fred/protocol/tls/trait.HostMapping.html b/doc/fred/protocol/tls/trait.HostMapping.html new file mode 100644 index 00000000..7ae23a9d --- /dev/null +++ b/doc/fred/protocol/tls/trait.HostMapping.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/trait.HostMapping.html...

+ + + \ No newline at end of file diff --git a/doc/fred/protocol/types/enum.MessageKind.html b/doc/fred/protocol/types/enum.MessageKind.html new file mode 100644 index 00000000..0ac55ff3 --- /dev/null +++ b/doc/fred/protocol/types/enum.MessageKind.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.MessageKind.html...

+ + + \ No newline at end of file diff --git a/doc/fred/protocol/types/struct.ClusterRouting.html b/doc/fred/protocol/types/struct.ClusterRouting.html new file mode 100644 index 00000000..f0606157 --- /dev/null +++ b/doc/fred/protocol/types/struct.ClusterRouting.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.ClusterRouting.html...

+ + + \ No newline at end of file diff --git a/doc/fred/protocol/types/struct.Message.html b/doc/fred/protocol/types/struct.Message.html new file mode 100644 index 00000000..9a721589 --- /dev/null +++ b/doc/fred/protocol/types/struct.Message.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.Message.html...

+ + + \ No newline at end of file diff --git a/doc/fred/protocol/types/struct.Server.html b/doc/fred/protocol/types/struct.Server.html new file mode 100644 index 00000000..68548b7f --- /dev/null +++ b/doc/fred/protocol/types/struct.Server.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.Server.html...

+ + + \ No newline at end of file diff --git a/doc/fred/protocol/types/struct.SlotRange.html b/doc/fred/protocol/types/struct.SlotRange.html new file mode 100644 index 00000000..553ada47 --- /dev/null +++ b/doc/fred/protocol/types/struct.SlotRange.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.SlotRange.html...

+ + + \ No newline at end of file diff --git a/doc/fred/protocol/types/trait.Resolve.html b/doc/fred/protocol/types/trait.Resolve.html new file mode 100644 index 00000000..82a92bf8 --- /dev/null +++ b/doc/fred/protocol/types/trait.Resolve.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/trait.Resolve.html...

+ + + \ No newline at end of file diff --git a/doc/fred/router/replicas/struct.ReplicaConfig.html b/doc/fred/router/replicas/struct.ReplicaConfig.html new file mode 100644 index 00000000..75a28af1 --- /dev/null +++ b/doc/fred/router/replicas/struct.ReplicaConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.ReplicaConfig.html...

+ + + \ No newline at end of file diff --git a/doc/fred/router/replicas/trait.ReplicaFilter.html b/doc/fred/router/replicas/trait.ReplicaFilter.html new file mode 100644 index 00000000..86cbe641 --- /dev/null +++ b/doc/fred/router/replicas/trait.ReplicaFilter.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/trait.ReplicaFilter.html...

+ + + \ No newline at end of file diff --git a/doc/fred/sidebar-items.js b/doc/fred/sidebar-items.js new file mode 100644 index 00000000..53c466e9 --- /dev/null +++ b/doc/fred/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"externcrate":["bytes","bytes_utils","native_tls","rustls","rustls_native_certs","serde_json","socket2","tracing"],"macro":["cmd","json_quote"],"mod":["clients","error","interfaces","monitor","prelude","types","util"]}; \ No newline at end of file diff --git a/doc/fred/types/args/enum.RedisValue.html b/doc/fred/types/args/enum.RedisValue.html new file mode 100644 index 00000000..6b93c09b --- /dev/null +++ b/doc/fred/types/args/enum.RedisValue.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.RedisValue.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/args/enum.RedisValueKind.html b/doc/fred/types/args/enum.RedisValueKind.html new file mode 100644 index 00000000..9ef8af7a --- /dev/null +++ b/doc/fred/types/args/enum.RedisValueKind.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.RedisValueKind.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/args/enum.StringOrNumber.html b/doc/fred/types/args/enum.StringOrNumber.html new file mode 100644 index 00000000..b8a6a90e --- /dev/null +++ b/doc/fred/types/args/enum.StringOrNumber.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.StringOrNumber.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/args/struct.RedisKey.html b/doc/fred/types/args/struct.RedisKey.html new file mode 100644 index 00000000..e1445364 --- /dev/null +++ b/doc/fred/types/args/struct.RedisKey.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.RedisKey.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/args/struct.RedisMap.html b/doc/fred/types/args/struct.RedisMap.html new file mode 100644 index 00000000..cf0767b1 --- /dev/null +++ b/doc/fred/types/args/struct.RedisMap.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.RedisMap.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/builder/struct.Builder.html b/doc/fred/types/builder/struct.Builder.html new file mode 100644 index 00000000..80520838 --- /dev/null +++ b/doc/fred/types/builder/struct.Builder.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.Builder.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/client/enum.ClientKillFilter.html b/doc/fred/types/client/enum.ClientKillFilter.html new file mode 100644 index 00000000..666e2958 --- /dev/null +++ b/doc/fred/types/client/enum.ClientKillFilter.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ClientKillFilter.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/client/enum.ClientKillType.html b/doc/fred/types/client/enum.ClientKillType.html new file mode 100644 index 00000000..c6478333 --- /dev/null +++ b/doc/fred/types/client/enum.ClientKillType.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ClientKillType.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/client/enum.ClientPauseKind.html b/doc/fred/types/client/enum.ClientPauseKind.html new file mode 100644 index 00000000..154b33d1 --- /dev/null +++ b/doc/fred/types/client/enum.ClientPauseKind.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ClientPauseKind.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/client/enum.ClientReplyFlag.html b/doc/fred/types/client/enum.ClientReplyFlag.html new file mode 100644 index 00000000..6b1e36ff --- /dev/null +++ b/doc/fred/types/client/enum.ClientReplyFlag.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ClientReplyFlag.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/client/enum.Toggle.html b/doc/fred/types/client/enum.Toggle.html new file mode 100644 index 00000000..d8fc7877 --- /dev/null +++ b/doc/fred/types/client/enum.Toggle.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.Toggle.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/client/struct.Invalidation.html b/doc/fred/types/client/struct.Invalidation.html new file mode 100644 index 00000000..1ba45418 --- /dev/null +++ b/doc/fred/types/client/struct.Invalidation.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.Invalidation.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/cluster/enum.ClusterFailoverFlag.html b/doc/fred/types/cluster/enum.ClusterFailoverFlag.html new file mode 100644 index 00000000..e735214c --- /dev/null +++ b/doc/fred/types/cluster/enum.ClusterFailoverFlag.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ClusterFailoverFlag.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/cluster/enum.ClusterResetFlag.html b/doc/fred/types/cluster/enum.ClusterResetFlag.html new file mode 100644 index 00000000..7aec2d71 --- /dev/null +++ b/doc/fred/types/cluster/enum.ClusterResetFlag.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ClusterResetFlag.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/cluster/enum.ClusterSetSlotState.html b/doc/fred/types/cluster/enum.ClusterSetSlotState.html new file mode 100644 index 00000000..6693f128 --- /dev/null +++ b/doc/fred/types/cluster/enum.ClusterSetSlotState.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ClusterSetSlotState.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/cluster/enum.ClusterState.html b/doc/fred/types/cluster/enum.ClusterState.html new file mode 100644 index 00000000..772961ba --- /dev/null +++ b/doc/fred/types/cluster/enum.ClusterState.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ClusterState.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/cluster/struct.ClusterInfo.html b/doc/fred/types/cluster/struct.ClusterInfo.html new file mode 100644 index 00000000..c64ac2cd --- /dev/null +++ b/doc/fred/types/cluster/struct.ClusterInfo.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.ClusterInfo.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/constant.DEFAULT_JITTER_MS.html b/doc/fred/types/config/constant.DEFAULT_JITTER_MS.html new file mode 100644 index 00000000..6905b307 --- /dev/null +++ b/doc/fred/types/config/constant.DEFAULT_JITTER_MS.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/constant.DEFAULT_JITTER_MS.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/enum.BackpressurePolicy.html b/doc/fred/types/config/enum.BackpressurePolicy.html new file mode 100644 index 00000000..bf3a5b1d --- /dev/null +++ b/doc/fred/types/config/enum.BackpressurePolicy.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.BackpressurePolicy.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/enum.Blocking.html b/doc/fred/types/config/enum.Blocking.html new file mode 100644 index 00000000..b4dd8f70 --- /dev/null +++ b/doc/fred/types/config/enum.Blocking.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.Blocking.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/enum.ClusterDiscoveryPolicy.html b/doc/fred/types/config/enum.ClusterDiscoveryPolicy.html new file mode 100644 index 00000000..39ce589f --- /dev/null +++ b/doc/fred/types/config/enum.ClusterDiscoveryPolicy.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ClusterDiscoveryPolicy.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/enum.ReconnectError.html b/doc/fred/types/config/enum.ReconnectError.html new file mode 100644 index 00000000..02497a03 --- /dev/null +++ b/doc/fred/types/config/enum.ReconnectError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ReconnectError.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/enum.ReconnectPolicy.html b/doc/fred/types/config/enum.ReconnectPolicy.html new file mode 100644 index 00000000..1c482ef4 --- /dev/null +++ b/doc/fred/types/config/enum.ReconnectPolicy.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ReconnectPolicy.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/enum.ServerConfig.html b/doc/fred/types/config/enum.ServerConfig.html new file mode 100644 index 00000000..a745715e --- /dev/null +++ b/doc/fred/types/config/enum.ServerConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ServerConfig.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/struct.BackpressureConfig.html b/doc/fred/types/config/struct.BackpressureConfig.html new file mode 100644 index 00000000..c9baabb8 --- /dev/null +++ b/doc/fred/types/config/struct.BackpressureConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.BackpressureConfig.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/struct.ConnectionConfig.html b/doc/fred/types/config/struct.ConnectionConfig.html new file mode 100644 index 00000000..642055eb --- /dev/null +++ b/doc/fred/types/config/struct.ConnectionConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.ConnectionConfig.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/struct.Options.html b/doc/fred/types/config/struct.Options.html new file mode 100644 index 00000000..bad7677d --- /dev/null +++ b/doc/fred/types/config/struct.Options.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.Options.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/struct.PerformanceConfig.html b/doc/fred/types/config/struct.PerformanceConfig.html new file mode 100644 index 00000000..9074276b --- /dev/null +++ b/doc/fred/types/config/struct.PerformanceConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.PerformanceConfig.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/struct.RedisConfig.html b/doc/fred/types/config/struct.RedisConfig.html new file mode 100644 index 00000000..4966c236 --- /dev/null +++ b/doc/fred/types/config/struct.RedisConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.RedisConfig.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/struct.SentinelConfig.html b/doc/fred/types/config/struct.SentinelConfig.html new file mode 100644 index 00000000..d4134edb --- /dev/null +++ b/doc/fred/types/config/struct.SentinelConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.SentinelConfig.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/struct.TcpConfig.html b/doc/fred/types/config/struct.TcpConfig.html new file mode 100644 index 00000000..d41189d1 --- /dev/null +++ b/doc/fred/types/config/struct.TcpConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.TcpConfig.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/struct.TracingConfig.html b/doc/fred/types/config/struct.TracingConfig.html new file mode 100644 index 00000000..171a88a5 --- /dev/null +++ b/doc/fred/types/config/struct.TracingConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.TracingConfig.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/config/struct.UnresponsiveConfig.html b/doc/fred/types/config/struct.UnresponsiveConfig.html new file mode 100644 index 00000000..d87798c0 --- /dev/null +++ b/doc/fred/types/config/struct.UnresponsiveConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.UnresponsiveConfig.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/constant.DEFAULT_JITTER_MS.html b/doc/fred/types/constant.DEFAULT_JITTER_MS.html new file mode 100644 index 00000000..22f36bd2 --- /dev/null +++ b/doc/fred/types/constant.DEFAULT_JITTER_MS.html @@ -0,0 +1,2 @@ +DEFAULT_JITTER_MS in fred::types - Rust

Constant fred::types::DEFAULT_JITTER_MS

source ·
pub const DEFAULT_JITTER_MS: u32 = 100;
Expand description

The default amount of jitter when waiting to reconnect.

+
\ No newline at end of file diff --git a/doc/fred/types/enum.AggregateOperation.html b/doc/fred/types/enum.AggregateOperation.html new file mode 100644 index 00000000..e6930d91 --- /dev/null +++ b/doc/fred/types/enum.AggregateOperation.html @@ -0,0 +1,55 @@ +AggregateOperation in fred::types - Rust

Enum fred::types::AggregateOperation

source ·
pub enum AggregateOperation {
+    Filter {
+        expression: Str,
+    },
+    GroupBy {
+        fields: Vec<Str>,
+        reducers: Vec<SearchReducer>,
+    },
+    Apply {
+        expression: Str,
+        name: Str,
+    },
+    SortBy {
+        properties: Vec<(Str, SortOrder)>,
+        max: Option<u64>,
+    },
+    Limit {
+        offset: u64,
+        num: u64,
+    },
+}
Available on crate feature i-redisearch only.
Expand description

Variants§

§

Filter

Fields

§expression: Str
§

GroupBy

Fields

§fields: Vec<Str>

An empty array is equivalent to GROUPBY 0

+
§reducers: Vec<SearchReducer>
§

Apply

Fields

§expression: Str
§name: Str
§

SortBy

Fields

§properties: Vec<(Str, SortOrder)>
§

Limit

Fields

§offset: u64
§num: u64

Trait Implementations§

source§

impl Clone for AggregateOperation

source§

fn clone(&self) -> AggregateOperation

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for AggregateOperation

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for AggregateOperation

source§

fn eq(&self, other: &AggregateOperation) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for AggregateOperation

source§

impl StructuralPartialEq for AggregateOperation

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.AggregateOptions.html b/doc/fred/types/enum.AggregateOptions.html new file mode 100644 index 00000000..c55bb9d7 --- /dev/null +++ b/doc/fred/types/enum.AggregateOptions.html @@ -0,0 +1,34 @@ +AggregateOptions in fred::types - Rust

Enum fred::types::AggregateOptions

source ·
pub enum AggregateOptions {
+    Sum,
+    Min,
+    Max,
+}
Expand description

Aggregate options for the zinterstore (and related) commands.

+

Variants§

§

Sum

§

Min

§

Max

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.Aggregator.html b/doc/fred/types/enum.Aggregator.html new file mode 100644 index 00000000..4435f0f0 --- /dev/null +++ b/doc/fred/types/enum.Aggregator.html @@ -0,0 +1,47 @@ +Aggregator in fred::types - Rust

Enum fred::types::Aggregator

source ·
pub enum Aggregator {
+
Show 13 variants Avg, + Sum, + Min, + Max, + Range, + Count, + First, + Last, + StdP, + StdS, + VarP, + VarS, + TWA, +
}
Available on crate feature i-time-series only.
Expand description

An aggregation policy to use with certain timeseries commands.

+

Variants§

§

Avg

§

Sum

§

Min

§

Max

§

Range

§

Count

§

First

§

Last

§

StdP

§

StdS

§

VarP

§

VarS

§

TWA

Trait Implementations§

source§

impl Clone for Aggregator

source§

fn clone(&self) -> Aggregator

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Aggregator

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Aggregator

source§

fn eq(&self, other: &Aggregator) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for Aggregator

source§

impl StructuralPartialEq for Aggregator

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.BackpressurePolicy.html b/doc/fred/types/enum.BackpressurePolicy.html new file mode 100644 index 00000000..cb9bd245 --- /dev/null +++ b/doc/fred/types/enum.BackpressurePolicy.html @@ -0,0 +1,50 @@ +BackpressurePolicy in fred::types - Rust

Enum fred::types::BackpressurePolicy

source ·
pub enum BackpressurePolicy {
+    Sleep {
+        disable_backpressure_scaling: bool,
+        min_sleep_duration: Duration,
+    },
+    Drain,
+}
Expand description

Backpressure policies to apply when the max number of in-flight commands is reached on a connection.

+

Variants§

§

Sleep

Sleep for some amount of time before sending the next command.

+

Fields

§disable_backpressure_scaling: bool

Disable the backpressure scaling logic used to calculate the sleep duration when throttling commands.

+

If true the client will always wait a constant amount of time defined by min_sleep_duration_ms when +throttling commands. Otherwise the sleep duration will scale based on the number of in-flight commands.

+

Default: false

+
§min_sleep_duration: Duration

The minimum amount of time to wait when applying backpressure to a command.

+

If 0 then no backpressure will be applied, but backpressure errors will not be surfaced to callers unless +disable_auto_backpressure is true.

+

Default: 10 ms

+
§

Drain

Wait for all in-flight commands to finish before sending the next command.

+

Implementations§

source§

impl BackpressurePolicy

source

pub fn default_sleep() -> Self

Create a new Sleep policy with the legacy default values.

+

Trait Implementations§

source§

impl Clone for BackpressurePolicy

source§

fn clone(&self) -> BackpressurePolicy

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for BackpressurePolicy

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for BackpressurePolicy

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for BackpressurePolicy

source§

fn eq(&self, other: &BackpressurePolicy) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for BackpressurePolicy

source§

impl StructuralPartialEq for BackpressurePolicy

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.Blocking.html b/doc/fred/types/enum.Blocking.html new file mode 100644 index 00000000..037fc4bf --- /dev/null +++ b/doc/fred/types/enum.Blocking.html @@ -0,0 +1,41 @@ +Blocking in fred::types - Rust

Enum fred::types::Blocking

source ·
pub enum Blocking {
+    Block,
+    Error,
+    Interrupt,
+}
Expand description

Describes how the client should respond when a command is sent while the client is in a blocked state from a +blocking command.

+

Variants§

§

Block

Wait to send the command until the blocked command finishes. (Default)

+
§

Error

Return an error to the caller.

+
§

Interrupt

Interrupt the blocked command by automatically sending CLIENT UNBLOCK for the blocked connection.

+

Trait Implementations§

source§

impl Clone for Blocking

source§

fn clone(&self) -> Blocking

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Blocking

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Blocking

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for Blocking

source§

fn eq(&self, other: &Blocking) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for Blocking

source§

impl StructuralPartialEq for Blocking

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.BucketTimestamp.html b/doc/fred/types/enum.BucketTimestamp.html new file mode 100644 index 00000000..2afea67f --- /dev/null +++ b/doc/fred/types/enum.BucketTimestamp.html @@ -0,0 +1,37 @@ +BucketTimestamp in fred::types - Rust

Enum fred::types::BucketTimestamp

source ·
pub enum BucketTimestamp {
+    Start,
+    End,
+    Mid,
+}
Available on crate feature i-time-series only.
Expand description

A BUCKETTIMESTAMP argument in commands such as TS.MRANGE.

+

Variants§

§

Start

§

End

§

Mid

Trait Implementations§

source§

impl Clone for BucketTimestamp

source§

fn clone(&self) -> BucketTimestamp

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for BucketTimestamp

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for BucketTimestamp

source§

fn eq(&self, other: &BucketTimestamp) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl TryFrom<&str> for BucketTimestamp

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: &str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for BucketTimestamp

source§

impl StructuralPartialEq for BucketTimestamp

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ClientKillFilter.html b/doc/fred/types/enum.ClientKillFilter.html new file mode 100644 index 00000000..3e354230 --- /dev/null +++ b/doc/fred/types/enum.ClientKillFilter.html @@ -0,0 +1,41 @@ +ClientKillFilter in fred::types - Rust

Enum fred::types::ClientKillFilter

source ·
pub enum ClientKillFilter {
+    ID(String),
+    Type(ClientKillType),
+    User(String),
+    Addr(String),
+    LAddr(String),
+    SkipMe(bool),
+}
Available on crate feature i-client only.
Expand description

Filters provided to the CLIENT KILL command.

+

https://redis.io/commands/client-kill

+

Variants§

§

ID(String)

§

Type(ClientKillType)

§

User(String)

§

Addr(String)

§

LAddr(String)

§

SkipMe(bool)

Trait Implementations§

source§

impl Clone for ClientKillFilter

source§

fn clone(&self) -> ClientKillFilter

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClientKillFilter

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ClientKillFilter

source§

fn eq(&self, other: &ClientKillFilter) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ClientKillFilter

source§

impl StructuralPartialEq for ClientKillFilter

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ClientKillType.html b/doc/fred/types/enum.ClientKillType.html new file mode 100644 index 00000000..4cb21985 --- /dev/null +++ b/doc/fred/types/enum.ClientKillType.html @@ -0,0 +1,39 @@ +ClientKillType in fred::types - Rust

Enum fred::types::ClientKillType

source ·
pub enum ClientKillType {
+    Normal,
+    Master,
+    Replica,
+    Pubsub,
+}
Available on crate feature i-client only.
Expand description

The type of clients to close.

+

https://redis.io/commands/client-kill

+

Variants§

§

Normal

§

Master

§

Replica

§

Pubsub

Trait Implementations§

source§

impl Clone for ClientKillType

source§

fn clone(&self) -> ClientKillType

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClientKillType

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ClientKillType

source§

fn eq(&self, other: &ClientKillType) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ClientKillType

source§

impl StructuralPartialEq for ClientKillType

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ClientPauseKind.html b/doc/fred/types/enum.ClientPauseKind.html new file mode 100644 index 00000000..4caf7f14 --- /dev/null +++ b/doc/fred/types/enum.ClientPauseKind.html @@ -0,0 +1,37 @@ +ClientPauseKind in fred::types - Rust

Enum fred::types::ClientPauseKind

source ·
pub enum ClientPauseKind {
+    Write,
+    All,
+}
Available on crate feature i-client only.
Expand description

Filters for the CLIENT PAUSE command.

+

https://redis.io/commands/client-pause

+

Variants§

§

Write

§

All

Trait Implementations§

source§

impl Clone for ClientPauseKind

source§

fn clone(&self) -> ClientPauseKind

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClientPauseKind

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ClientPauseKind

source§

fn eq(&self, other: &ClientPauseKind) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ClientPauseKind

source§

impl StructuralPartialEq for ClientPauseKind

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ClientReplyFlag.html b/doc/fred/types/enum.ClientReplyFlag.html new file mode 100644 index 00000000..1bf4a1bf --- /dev/null +++ b/doc/fred/types/enum.ClientReplyFlag.html @@ -0,0 +1,38 @@ +ClientReplyFlag in fred::types - Rust

Enum fred::types::ClientReplyFlag

source ·
pub enum ClientReplyFlag {
+    On,
+    Off,
+    Skip,
+}
Available on crate feature i-client only.
Expand description

Arguments for the CLIENT REPLY command.

+

https://redis.io/commands/client-reply

+

Variants§

§

On

§

Off

§

Skip

Trait Implementations§

source§

impl Clone for ClientReplyFlag

source§

fn clone(&self) -> ClientReplyFlag

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClientReplyFlag

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ClientReplyFlag

source§

fn eq(&self, other: &ClientReplyFlag) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ClientReplyFlag

source§

impl StructuralPartialEq for ClientReplyFlag

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ClientState.html b/doc/fred/types/enum.ClientState.html new file mode 100644 index 00000000..bd0b1e4a --- /dev/null +++ b/doc/fred/types/enum.ClientState.html @@ -0,0 +1,39 @@ +ClientState in fred::types - Rust

Enum fred::types::ClientState

source ·
pub enum ClientState {
+    Disconnected,
+    Disconnecting,
+    Connected,
+    Connecting,
+}
Expand description

The state of the underlying connection to the Redis server.

+

Variants§

§

Disconnected

§

Disconnecting

§

Connected

§

Connecting

Trait Implementations§

source§

impl Clone for ClientState

source§

fn clone(&self) -> ClientState

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClientState

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for ClientState

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ClientState

source§

fn eq(&self, other: &ClientState) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ClientState

source§

impl StructuralPartialEq for ClientState

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ClientUnblockFlag.html b/doc/fred/types/enum.ClientUnblockFlag.html new file mode 100644 index 00000000..bb99390a --- /dev/null +++ b/doc/fred/types/enum.ClientUnblockFlag.html @@ -0,0 +1,36 @@ +ClientUnblockFlag in fred::types - Rust

Enum fred::types::ClientUnblockFlag

source ·
pub enum ClientUnblockFlag {
+    Timeout,
+    Error,
+}
Expand description

Arguments to the CLIENT UNBLOCK command.

+

Variants§

§

Timeout

§

Error

Trait Implementations§

source§

impl Clone for ClientUnblockFlag

source§

fn clone(&self) -> ClientUnblockFlag

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClientUnblockFlag

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ClientUnblockFlag

source§

fn eq(&self, other: &ClientUnblockFlag) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ClientUnblockFlag

source§

impl StructuralPartialEq for ClientUnblockFlag

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ClusterDiscoveryPolicy.html b/doc/fred/types/enum.ClusterDiscoveryPolicy.html new file mode 100644 index 00000000..3af7923f --- /dev/null +++ b/doc/fred/types/enum.ClusterDiscoveryPolicy.html @@ -0,0 +1,42 @@ +ClusterDiscoveryPolicy in fred::types - Rust

Enum fred::types::ClusterDiscoveryPolicy

source ·
pub enum ClusterDiscoveryPolicy {
+    ConfigEndpoint,
+    UseCache,
+}
Expand description

A policy that determines how clustered clients initially connect to and discover other cluster nodes.

+

Variants§

§

ConfigEndpoint

Always use the endpoint(s) provided in the client’s ServerConfig.

+

This is generally recommended with managed services, Kubernetes, or other systems that provide client routing +or cluster discovery interfaces.

+

Default.

+
§

UseCache

Try connecting to nodes specified in both the client’s ServerConfig and the most recently +cached routing table.

+

Trait Implementations§

source§

impl Clone for ClusterDiscoveryPolicy

source§

fn clone(&self) -> ClusterDiscoveryPolicy

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClusterDiscoveryPolicy

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for ClusterDiscoveryPolicy

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for ClusterDiscoveryPolicy

source§

fn eq(&self, other: &ClusterDiscoveryPolicy) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ClusterDiscoveryPolicy

source§

impl StructuralPartialEq for ClusterDiscoveryPolicy

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ClusterFailoverFlag.html b/doc/fred/types/enum.ClusterFailoverFlag.html new file mode 100644 index 00000000..4f03a041 --- /dev/null +++ b/doc/fred/types/enum.ClusterFailoverFlag.html @@ -0,0 +1,36 @@ +ClusterFailoverFlag in fred::types - Rust

Enum fred::types::ClusterFailoverFlag

source ·
pub enum ClusterFailoverFlag {
+    Force,
+    Takeover,
+}
Available on crate feature i-cluster only.
Expand description

Options for the CLUSTER FAILOVER command.

+

Variants§

§

Force

§

Takeover

Trait Implementations§

source§

impl Clone for ClusterFailoverFlag

source§

fn clone(&self) -> ClusterFailoverFlag

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClusterFailoverFlag

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ClusterFailoverFlag

source§

fn eq(&self, other: &ClusterFailoverFlag) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ClusterFailoverFlag

source§

impl StructuralPartialEq for ClusterFailoverFlag

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ClusterHash.html b/doc/fred/types/enum.ClusterHash.html new file mode 100644 index 00000000..96c2a2f2 --- /dev/null +++ b/doc/fred/types/enum.ClusterHash.html @@ -0,0 +1,46 @@ +ClusterHash in fred::types - Rust

Enum fred::types::ClusterHash

source ·
pub enum ClusterHash {
+    FirstKey,
+    FirstValue,
+    Random,
+    Offset(usize),
+    Custom(u16),
+}
Expand description

A cluster hashing policy.

+

Variants§

§

FirstKey

Hash the first string or bytes value in the arguments. (Default)

+
§

FirstValue

Hash the first argument regardless of type.

+
§

Random

Use a random node in the cluster.

+
§

Offset(usize)

Hash the value with the provided offset in the arguments array.

+
§

Custom(u16)

Provide a custom hash slot value.

+

Implementations§

source§

impl ClusterHash

source

pub fn hash(&self, args: &[RedisValue]) -> Option<u16>

Hash the provided arguments.

+
source

pub fn find_key<'a>(&self, args: &'a [RedisValue]) -> Option<&'a [u8]>

Find the key to hash with the provided arguments.

+

Trait Implementations§

source§

impl Clone for ClusterHash

source§

fn clone(&self) -> ClusterHash

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClusterHash

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for ClusterHash

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl From<&[u8]> for ClusterHash

source§

fn from(d: &[u8]) -> Self

Converts to this type from the input type.
source§

impl From<&str> for ClusterHash

source§

fn from(d: &str) -> Self

Converts to this type from the input type.
source§

impl From<Option<u16>> for ClusterHash

source§

fn from(hash_slot: Option<u16>) -> Self

Converts to this type from the input type.
source§

impl From<u16> for ClusterHash

source§

fn from(slot: u16) -> Self

Converts to this type from the input type.
source§

impl PartialEq for ClusterHash

source§

fn eq(&self, other: &ClusterHash) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ClusterHash

source§

impl StructuralPartialEq for ClusterHash

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ClusterResetFlag.html b/doc/fred/types/enum.ClusterResetFlag.html new file mode 100644 index 00000000..b8bbc983 --- /dev/null +++ b/doc/fred/types/enum.ClusterResetFlag.html @@ -0,0 +1,37 @@ +ClusterResetFlag in fred::types - Rust

Enum fred::types::ClusterResetFlag

source ·
pub enum ClusterResetFlag {
+    Hard,
+    Soft,
+}
Available on crate feature i-cluster only.
Expand description

Flags for the CLUSTER RESET command.

+

https://redis.io/commands/cluster-reset

+

Variants§

§

Hard

§

Soft

Trait Implementations§

source§

impl Clone for ClusterResetFlag

source§

fn clone(&self) -> ClusterResetFlag

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClusterResetFlag

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ClusterResetFlag

source§

fn eq(&self, other: &ClusterResetFlag) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ClusterResetFlag

source§

impl StructuralPartialEq for ClusterResetFlag

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ClusterSetSlotState.html b/doc/fred/types/enum.ClusterSetSlotState.html new file mode 100644 index 00000000..273f2fad --- /dev/null +++ b/doc/fred/types/enum.ClusterSetSlotState.html @@ -0,0 +1,39 @@ +ClusterSetSlotState in fred::types - Rust

Enum fred::types::ClusterSetSlotState

source ·
pub enum ClusterSetSlotState {
+    Importing,
+    Migrating,
+    Stable,
+    Node(String),
+}
Available on crate feature i-cluster only.
Expand description

Flags for the CLUSTER SETSLOT command.

+

https://redis.io/commands/cluster-setslot

+

Variants§

§

Importing

§

Migrating

§

Stable

§

Node(String)

Trait Implementations§

source§

impl Clone for ClusterSetSlotState

source§

fn clone(&self) -> ClusterSetSlotState

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClusterSetSlotState

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ClusterSetSlotState

source§

fn eq(&self, other: &ClusterSetSlotState) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ClusterSetSlotState

source§

impl StructuralPartialEq for ClusterSetSlotState

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ClusterState.html b/doc/fred/types/enum.ClusterState.html new file mode 100644 index 00000000..4d253650 --- /dev/null +++ b/doc/fred/types/enum.ClusterState.html @@ -0,0 +1,36 @@ +ClusterState in fred::types - Rust

Enum fred::types::ClusterState

source ·
pub enum ClusterState {
+    Ok,
+    Fail,
+}
Available on crate feature i-cluster only.
Expand description

The state of the cluster from the CLUSTER INFO command.

+

Variants§

§

Ok

§

Fail

Trait Implementations§

source§

impl Clone for ClusterState

source§

fn clone(&self) -> ClusterState

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClusterState

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for ClusterState

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for ClusterState

source§

fn eq(&self, other: &ClusterState) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ClusterState

source§

impl StructuralPartialEq for ClusterState

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ClusterStateChange.html b/doc/fred/types/enum.ClusterStateChange.html new file mode 100644 index 00000000..43e23514 --- /dev/null +++ b/doc/fred/types/enum.ClusterStateChange.html @@ -0,0 +1,43 @@ +ClusterStateChange in fred::types - Rust

Enum fred::types::ClusterStateChange

source ·
pub enum ClusterStateChange {
+    Add(Server),
+    Remove(Server),
+    Rebalance,
+}
Expand description

An enum describing the possible ways in which a Redis cluster can change state.

+

See on_cluster_change for more information.

+

Variants§

§

Add(Server)

A node was added to the cluster.

+

This implies that hash slots were also probably rebalanced.

+
§

Remove(Server)

A node was removed from the cluster.

+

This implies that hash slots were also probably rebalanced.

+
§

Rebalance

Hash slots were rebalanced across the cluster and/or local routing state was updated.

+

Trait Implementations§

source§

impl Clone for ClusterStateChange

source§

fn clone(&self) -> ClusterStateChange

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClusterStateChange

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ClusterStateChange

source§

fn eq(&self, other: &ClusterStateChange) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ClusterStateChange

source§

impl StructuralPartialEq for ClusterStateChange

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.DuplicatePolicy.html b/doc/fred/types/enum.DuplicatePolicy.html new file mode 100644 index 00000000..1d29896c --- /dev/null +++ b/doc/fred/types/enum.DuplicatePolicy.html @@ -0,0 +1,40 @@ +DuplicatePolicy in fred::types - Rust

Enum fred::types::DuplicatePolicy

source ·
pub enum DuplicatePolicy {
+    Block,
+    First,
+    Last,
+    Min,
+    Max,
+    Sum,
+}
Available on crate feature i-time-series only.
Expand description

The duplicate policy used with certain timeseries commands.

+

Variants§

§

Block

§

First

§

Last

§

Min

§

Max

§

Sum

Trait Implementations§

source§

impl Clone for DuplicatePolicy

source§

fn clone(&self) -> DuplicatePolicy

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for DuplicatePolicy

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for DuplicatePolicy

source§

fn eq(&self, other: &DuplicatePolicy) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for DuplicatePolicy

source§

impl StructuralPartialEq for DuplicatePolicy

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.Encoding.html b/doc/fred/types/enum.Encoding.html new file mode 100644 index 00000000..19026455 --- /dev/null +++ b/doc/fred/types/enum.Encoding.html @@ -0,0 +1,36 @@ +Encoding in fred::types - Rust

Enum fred::types::Encoding

source ·
pub enum Encoding {
+    Compressed,
+    Uncompressed,
+}
Available on crate feature i-time-series only.
Expand description

Encoding arguments for certain timeseries commands.

+

Variants§

§

Compressed

§

Uncompressed

Trait Implementations§

source§

impl Clone for Encoding

source§

fn clone(&self) -> Encoding

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Encoding

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Encoding

source§

fn eq(&self, other: &Encoding) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for Encoding

source§

impl StructuralPartialEq for Encoding

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.Expiration.html b/doc/fred/types/enum.Expiration.html new file mode 100644 index 00000000..ebc110d6 --- /dev/null +++ b/doc/fred/types/enum.Expiration.html @@ -0,0 +1,44 @@ +Expiration in fred::types - Rust

Enum fred::types::Expiration

source ·
pub enum Expiration {
+    EX(i64),
+    PX(i64),
+    EXAT(i64),
+    PXAT(i64),
+    KEEPTTL,
+}
Expand description

Expiration options for the set command.

+

Variants§

§

EX(i64)

Expiration in seconds.

+
§

PX(i64)

Expiration in milliseconds.

+
§

EXAT(i64)

Expiration time, in seconds.

+
§

PXAT(i64)

Expiration time, in milliseconds.

+
§

KEEPTTL

Do not reset the TTL.

+

Trait Implementations§

source§

impl Clone for Expiration

source§

fn clone(&self) -> Expiration

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Expiration

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Expiration

source§

fn eq(&self, other: &Expiration) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for Expiration

source§

impl StructuralPartialEq for Expiration

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ExpireOptions.html b/doc/fred/types/enum.ExpireOptions.html new file mode 100644 index 00000000..20a4463c --- /dev/null +++ b/doc/fred/types/enum.ExpireOptions.html @@ -0,0 +1,38 @@ +ExpireOptions in fred::types - Rust

Enum fred::types::ExpireOptions

source ·
pub enum ExpireOptions {
+    NX,
+    XX,
+    GT,
+    LT,
+}
Expand description

Options for certain expiration commands (PEXPIRE, etc).

+

Variants§

§

NX

§

XX

§

GT

§

LT

Trait Implementations§

source§

impl Clone for ExpireOptions

source§

fn clone(&self) -> ExpireOptions

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ExpireOptions

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ExpireOptions

source§

fn eq(&self, other: &ExpireOptions) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ExpireOptions

source§

impl StructuralPartialEq for ExpireOptions

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.FnPolicy.html b/doc/fred/types/enum.FnPolicy.html new file mode 100644 index 00000000..b1126529 --- /dev/null +++ b/doc/fred/types/enum.FnPolicy.html @@ -0,0 +1,37 @@ +FnPolicy in fred::types - Rust

Enum fred::types::FnPolicy

source ·
pub enum FnPolicy {
+    Flush,
+    Append,
+    Replace,
+}
Expand description

The policy type for the FUNCTION RESTORE command.

+

Variants§

§

Flush

§

Append

§

Replace

Trait Implementations§

source§

impl Clone for FnPolicy

source§

fn clone(&self) -> FnPolicy

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for FnPolicy

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for FnPolicy

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for FnPolicy

source§

fn eq(&self, other: &FnPolicy) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl TryFrom<&StrInner<Bytes>> for FnPolicy

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: &Str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<&String> for FnPolicy

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: &String) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<&str> for FnPolicy

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: &str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<StrInner<Bytes>> for FnPolicy

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: Str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<String> for FnPolicy

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: String) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for FnPolicy

source§

impl StructuralPartialEq for FnPolicy

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.FunctionFlag.html b/doc/fred/types/enum.FunctionFlag.html new file mode 100644 index 00000000..c4f028be --- /dev/null +++ b/doc/fred/types/enum.FunctionFlag.html @@ -0,0 +1,52 @@ +FunctionFlag in fred::types - Rust

Enum fred::types::FunctionFlag

source ·
pub enum FunctionFlag {
+    NoWrites,
+    AllowOOM,
+    NoCluster,
+    AllowCrossSlotKeys,
+    AllowStale,
+}
Available on crate feature i-scripts only.
Expand description

Possible flags associated with a Function.

+

Variants§

§

NoWrites

§

AllowOOM

§

NoCluster

§

AllowCrossSlotKeys

§

AllowStale

Implementations§

source§

impl FunctionFlag

source

pub fn from_str(s: &str) -> Option<Self>

Parse the string representation of the flag.

+
source

pub fn to_str(&self) -> &'static str

Convert to the string representation of the flag.

+

Trait Implementations§

source§

impl Clone for FunctionFlag

source§

fn clone(&self) -> FunctionFlag

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for FunctionFlag

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Hash for FunctionFlag

source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl Ord for FunctionFlag

source§

fn cmp(&self, other: &FunctionFlag) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized + PartialOrd,

Restrict a value to a certain interval. Read more
source§

impl PartialEq for FunctionFlag

source§

fn eq(&self, other: &FunctionFlag) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl PartialOrd for FunctionFlag

source§

fn partial_cmp(&self, other: &FunctionFlag) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
source§

impl Eq for FunctionFlag

source§

impl StructuralPartialEq for FunctionFlag

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CallHasher for T
where + T: Hash + ?Sized,

§

default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64
where + H: Hash + ?Sized, + B: BuildHasher,

source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.GeoUnit.html b/doc/fred/types/enum.GeoUnit.html new file mode 100644 index 00000000..842e20aa --- /dev/null +++ b/doc/fred/types/enum.GeoUnit.html @@ -0,0 +1,38 @@ +GeoUnit in fred::types - Rust

Enum fred::types::GeoUnit

source ·
pub enum GeoUnit {
+    Meters,
+    Kilometers,
+    Miles,
+    Feet,
+}
Available on crate feature i-geo only.
Expand description

Units for the GEO DIST command.

+

Variants§

§

Meters

§

Kilometers

§

Miles

§

Feet

Trait Implementations§

source§

impl Clone for GeoUnit

source§

fn clone(&self) -> GeoUnit

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for GeoUnit

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for GeoUnit

source§

fn eq(&self, other: &GeoUnit) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for GeoUnit

source§

impl StructuralPartialEq for GeoUnit

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.GetLabels.html b/doc/fred/types/enum.GetLabels.html new file mode 100644 index 00000000..bd126af0 --- /dev/null +++ b/doc/fred/types/enum.GetLabels.html @@ -0,0 +1,39 @@ +GetLabels in fred::types - Rust

Enum fred::types::GetLabels

source ·
pub enum GetLabels {
+    WithLabels,
+    SelectedLabels(Vec<Str>),
+}
Available on crate feature i-time-series only.
Expand description

Arguments equivalent to WITHLABELS | SELECTED_LABELS label... in various time series GET functions.

+

Variants§

§

WithLabels

§

SelectedLabels(Vec<Str>)

Trait Implementations§

source§

impl Clone for GetLabels

source§

fn clone(&self) -> GetLabels

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for GetLabels

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<S, const N: usize> From<[S; N]> for GetLabels
where + S: Into<Str>,

source§

fn from(value: [S; N]) -> Self

Converts to this type from the input type.
source§

impl<S> From<Vec<S>> for GetLabels
where + S: Into<Str>,

source§

fn from(value: Vec<S>) -> Self

Converts to this type from the input type.
source§

impl<S> FromIterator<S> for GetLabels
where + S: Into<Str>,

source§

fn from_iter<I: IntoIterator<Item = S>>(iter: I) -> Self

Creates a value from an iterator. Read more
source§

impl PartialEq for GetLabels

source§

fn eq(&self, other: &GetLabels) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for GetLabels

source§

impl StructuralPartialEq for GetLabels

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.GetTimestamp.html b/doc/fred/types/enum.GetTimestamp.html new file mode 100644 index 00000000..4b8031e7 --- /dev/null +++ b/doc/fred/types/enum.GetTimestamp.html @@ -0,0 +1,39 @@ +GetTimestamp in fred::types - Rust

Enum fred::types::GetTimestamp

source ·
pub enum GetTimestamp {
+    Earliest,
+    Latest,
+    Custom(i64),
+}
Available on crate feature i-time-series only.
Expand description

A timestamp query used in commands such as TS.MRANGE.

+

Variants§

§

Earliest

Equivalent to -.

+
§

Latest

Equivalent to +

+
§

Custom(i64)

Trait Implementations§

source§

impl Clone for GetTimestamp

source§

fn clone(&self) -> GetTimestamp

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for GetTimestamp

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<i64> for GetTimestamp

source§

fn from(value: i64) -> Self

Converts to this type from the input type.
source§

impl PartialEq for GetTimestamp

source§

fn eq(&self, other: &GetTimestamp) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl TryFrom<&str> for GetTimestamp

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: &str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for GetTimestamp

source§

impl StructuralPartialEq for GetTimestamp

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.IndexKind.html b/doc/fred/types/enum.IndexKind.html new file mode 100644 index 00000000..532177d6 --- /dev/null +++ b/doc/fred/types/enum.IndexKind.html @@ -0,0 +1,36 @@ +IndexKind in fred::types - Rust

Enum fred::types::IndexKind

source ·
pub enum IndexKind {
+    Hash,
+    JSON,
+}
Available on crate feature i-redisearch only.
Expand description

Index arguments for FT.CREATE.

+

Variants§

§

Hash

§

JSON

Trait Implementations§

source§

impl Clone for IndexKind

source§

fn clone(&self) -> IndexKind

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for IndexKind

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for IndexKind

source§

fn eq(&self, other: &IndexKind) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for IndexKind

source§

impl StructuralPartialEq for IndexKind

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.InfoKind.html b/doc/fred/types/enum.InfoKind.html new file mode 100644 index 00000000..57fb4f7b --- /dev/null +++ b/doc/fred/types/enum.InfoKind.html @@ -0,0 +1,46 @@ +InfoKind in fred::types - Rust

Enum fred::types::InfoKind

source ·
pub enum InfoKind {
+    Default,
+    All,
+    Keyspace,
+    Cluster,
+    CommandStats,
+    Cpu,
+    Replication,
+    Stats,
+    Persistence,
+    Memory,
+    Clients,
+    Server,
+}
Expand description

Options for the info command.

+

Variants§

§

Default

§

All

§

Keyspace

§

Cluster

§

CommandStats

§

Cpu

§

Replication

§

Stats

§

Persistence

§

Memory

§

Clients

§

Server

Trait Implementations§

source§

impl Clone for InfoKind

source§

fn clone(&self) -> InfoKind

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for InfoKind

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for InfoKind

source§

fn eq(&self, other: &InfoKind) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for InfoKind

source§

impl StructuralPartialEq for InfoKind

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.LMoveDirection.html b/doc/fred/types/enum.LMoveDirection.html new file mode 100644 index 00000000..4f78418c --- /dev/null +++ b/doc/fred/types/enum.LMoveDirection.html @@ -0,0 +1,37 @@ +LMoveDirection in fred::types - Rust

Enum fred::types::LMoveDirection

source ·
pub enum LMoveDirection {
+    Left,
+    Right,
+}
Available on crate feature i-lists only.
Expand description

The direction to move elements in a *LMOVE command.

+

https://redis.io/commands/blmove

+

Variants§

§

Left

§

Right

Trait Implementations§

source§

impl Clone for LMoveDirection

source§

fn clone(&self) -> LMoveDirection

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for LMoveDirection

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for LMoveDirection

source§

fn eq(&self, other: &LMoveDirection) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for LMoveDirection

source§

impl StructuralPartialEq for LMoveDirection

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ListLocation.html b/doc/fred/types/enum.ListLocation.html new file mode 100644 index 00000000..9ceefa7a --- /dev/null +++ b/doc/fred/types/enum.ListLocation.html @@ -0,0 +1,36 @@ +ListLocation in fred::types - Rust

Enum fred::types::ListLocation

source ·
pub enum ListLocation {
+    Before,
+    After,
+}
Available on crate feature i-lists only.
Expand description

Location flag for the LINSERT command.

+

Variants§

§

Before

§

After

Trait Implementations§

source§

impl Clone for ListLocation

source§

fn clone(&self) -> ListLocation

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ListLocation

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ListLocation

source§

fn eq(&self, other: &ListLocation) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ListLocation

source§

impl StructuralPartialEq for ListLocation

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.Load.html b/doc/fred/types/enum.Load.html new file mode 100644 index 00000000..b8174918 --- /dev/null +++ b/doc/fred/types/enum.Load.html @@ -0,0 +1,36 @@ +Load in fred::types - Rust

Enum fred::types::Load

source ·
pub enum Load {
+    All,
+    Some(Vec<SearchField>),
+}
Available on crate feature i-redisearch only.
Expand description

Arguments to LOAD in FT.AGGREGATE.

+

Variants§

Trait Implementations§

source§

impl Clone for Load

source§

fn clone(&self) -> Load

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Load

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Load

source§

fn eq(&self, other: &Load) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for Load

source§

impl StructuralPartialEq for Load

Auto Trait Implementations§

§

impl Freeze for Load

§

impl RefUnwindSafe for Load

§

impl Send for Load

§

impl Sync for Load

§

impl Unpin for Load

§

impl UnwindSafe for Load

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.MessageKind.html b/doc/fred/types/enum.MessageKind.html new file mode 100644 index 00000000..8ba360e6 --- /dev/null +++ b/doc/fred/types/enum.MessageKind.html @@ -0,0 +1,40 @@ +MessageKind in fred::types - Rust

Enum fred::types::MessageKind

source ·
pub enum MessageKind {
+    Message,
+    PMessage,
+    SMessage,
+}
Expand description

The kind of pubsub message.

+

Variants§

§

Message

A message from a subscribe command.

+
§

PMessage

A message from a pattern psubscribe command.

+
§

SMessage

A message from a sharded ssubscribe command.

+

Trait Implementations§

source§

impl Clone for MessageKind

source§

fn clone(&self) -> MessageKind

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for MessageKind

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for MessageKind

source§

fn eq(&self, other: &MessageKind) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for MessageKind

source§

impl StructuralPartialEq for MessageKind

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.Ordering.html b/doc/fred/types/enum.Ordering.html new file mode 100644 index 00000000..8ee7882c --- /dev/null +++ b/doc/fred/types/enum.Ordering.html @@ -0,0 +1,36 @@ +Ordering in fred::types - Rust

Enum fred::types::Ordering

source ·
pub enum Ordering {
+    GreaterThan,
+    LessThan,
+}
Available on crate feature i-sorted-sets only.
Expand description

Ordering options for the ZADD (and related) commands.

+

Variants§

§

GreaterThan

§

LessThan

Trait Implementations§

source§

impl Clone for Ordering

source§

fn clone(&self) -> Ordering

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Ordering

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Ordering

source§

fn eq(&self, other: &Ordering) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for Ordering

source§

impl StructuralPartialEq for Ordering

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ReconnectError.html b/doc/fred/types/enum.ReconnectError.html new file mode 100644 index 00000000..b2188446 --- /dev/null +++ b/doc/fred/types/enum.ReconnectError.html @@ -0,0 +1,53 @@ +ReconnectError in fred::types - Rust

Enum fred::types::ReconnectError

source ·
pub enum ReconnectError {
+    ClusterDown,
+    Loading,
+    MasterDown,
+    ReadOnly,
+    Misconf,
+    Busy,
+    NoReplicas,
+    Custom(&'static str),
+}
Available on crate feature custom-reconnect-errors only.
Expand description

Special errors that can trigger reconnection logic, which can also retry the failing command if possible.

+

MOVED, ASK, and NOAUTH errors are handled separately by the client.

+

Variants§

§

ClusterDown

The CLUSTERDOWN prefix.

+
§

Loading

The LOADING prefix.

+
§

MasterDown

The MASTERDOWN prefix.

+
§

ReadOnly

The READONLY prefix, which can happen if a primary node is switched to a replica without any connection +interruption.

+
§

Misconf

The MISCONF prefix.

+
§

Busy

The BUSY prefix.

+
§

NoReplicas

The NOREPLICAS prefix.

+
§

Custom(&'static str)

A case-sensitive prefix on an error message.

+

See the source for examples.

+

Trait Implementations§

source§

impl Clone for ReconnectError

source§

fn clone(&self) -> ReconnectError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ReconnectError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ReconnectError

source§

fn eq(&self, other: &ReconnectError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ReconnectError

source§

impl StructuralPartialEq for ReconnectError

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ReconnectPolicy.html b/doc/fred/types/enum.ReconnectPolicy.html new file mode 100644 index 00000000..d564c7cd --- /dev/null +++ b/doc/fred/types/enum.ReconnectPolicy.html @@ -0,0 +1,75 @@ +ReconnectPolicy in fred::types - Rust

Enum fred::types::ReconnectPolicy

source ·
pub enum ReconnectPolicy {
+    Constant {
+        attempts: u32,
+        max_attempts: u32,
+        delay: u32,
+        jitter: u32,
+    },
+    Linear {
+        attempts: u32,
+        max_attempts: u32,
+        max_delay: u32,
+        delay: u32,
+        jitter: u32,
+    },
+    Exponential {
+        attempts: u32,
+        max_attempts: u32,
+        min_delay: u32,
+        max_delay: u32,
+        mult: u32,
+        jitter: u32,
+    },
+}
Expand description

The type of reconnection policy to use. This will apply to every connection used by the client.

+

Use a max_attempts value of 0 to retry forever.

+

Variants§

§

Constant

Wait a constant amount of time between reconnect attempts, in ms.

+

Fields

§attempts: u32
§max_attempts: u32
§delay: u32
§jitter: u32
§

Linear

Backoff reconnection attempts linearly, adding delay each time.

+

Fields

§attempts: u32
§max_attempts: u32
§max_delay: u32
§delay: u32
§jitter: u32
§

Exponential

Backoff reconnection attempts exponentially, multiplying the last delay by mult each time.

+

Fields

§attempts: u32
§max_attempts: u32
§min_delay: u32
§max_delay: u32
§mult: u32
§jitter: u32

Implementations§

source§

impl ReconnectPolicy

source

pub fn new_constant(max_attempts: u32, delay: u32) -> ReconnectPolicy

Create a new reconnect policy with a constant backoff.

+
source

pub fn new_linear( + max_attempts: u32, + max_delay: u32, + delay: u32, +) -> ReconnectPolicy

Create a new reconnect policy with a linear backoff.

+
source

pub fn new_exponential( + max_attempts: u32, + min_delay: u32, + max_delay: u32, + mult: u32, +) -> ReconnectPolicy

Create a new reconnect policy with an exponential backoff.

+
source

pub fn set_jitter(&mut self, jitter_ms: u32)

Set the amount of jitter to add to each reconnect delay.

+

Default: 50 ms

+
source

pub fn attempts(&self) -> u32

Read the number of reconnection attempts.

+
source

pub fn next_delay(&mut self) -> Option<u64>

Calculate the next delay, incrementing attempts in the process.

+

Trait Implementations§

source§

impl Clone for ReconnectPolicy

source§

fn clone(&self) -> ReconnectPolicy

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ReconnectPolicy

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for ReconnectPolicy

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for ReconnectPolicy

source§

fn eq(&self, other: &ReconnectPolicy) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ReconnectPolicy

source§

impl StructuralPartialEq for ReconnectPolicy

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.RedisValue.html b/doc/fred/types/enum.RedisValue.html new file mode 100644 index 00000000..216b7f31 --- /dev/null +++ b/doc/fred/types/enum.RedisValue.html @@ -0,0 +1,176 @@ +RedisValue in fred::types - Rust

Enum fred::types::RedisValue

source ·
pub enum RedisValue {
+    Boolean(bool),
+    Integer(i64),
+    Double(f64),
+    String(Str),
+    Bytes(Bytes),
+    Null,
+    Queued,
+    Map(RedisMap),
+    Array(Vec<RedisValue>),
+}
Expand description

A value used in a Redis command.

+

Variants§

§

Boolean(bool)

A boolean value.

+
§

Integer(i64)

An integer value.

+
§

Double(f64)

A double floating point number.

+
§

String(Str)

A string value.

+
§

Bytes(Bytes)

A byte array value.

+
§

Null

A nil value.

+
§

Queued

A special value used to indicate a MULTI block command was received by the server.

+
§

Map(RedisMap)

A map of key/value pairs, primarily used in RESP3 mode.

+
§

Array(Vec<RedisValue>)

An ordered list of values.

+

In RESP2 mode the server usually sends map structures as an array of key/value pairs.

+

Implementations§

source§

impl RedisValue

source

pub fn from_static(b: &'static [u8]) -> Self

Create a new RedisValue::Bytes from a static byte slice without copying.

+
source

pub fn from_static_str(s: &'static str) -> Self

Create a new RedisValue::String from a static str without copying.

+
source

pub fn new_ok() -> Self

Create a new RedisValue with the OK status.

+
source

pub fn is_ok(&self) -> bool

Whether the value is a simple string OK value.

+
source

pub fn into_integer(self) -> Result<RedisValue, RedisValue>

Attempt to convert the value into an integer, returning the original string as an error if the parsing fails.

+
source

pub fn kind(&self) -> RedisValueKind

Read the type of the value without any associated data.

+
source

pub fn is_null(&self) -> bool

Check if the value is null.

+
source

pub fn is_integer(&self) -> bool

Check if the value is an integer.

+
source

pub fn is_string(&self) -> bool

Check if the value is a string.

+
source

pub fn is_bytes(&self) -> bool

Check if the value is an array of bytes.

+
source

pub fn is_boolean(&self) -> bool

Whether the value is a boolean value or can be parsed as a boolean value.

+
source

pub fn is_double(&self) -> bool

Whether the inner value is a double or can be parsed as a double.

+
source

pub fn is_queued(&self) -> bool

Check if the value is a QUEUED response.

+
source

pub fn is_aggregate_type(&self) -> bool

Whether the value is an array or map.

+
source

pub fn is_map(&self) -> bool

Whether the value is a RedisMap.

+

See is_maybe_map for a function that also checks for arrays that likely represent a map in +RESP2 mode.

+
source

pub fn is_maybe_map(&self) -> bool

Whether the value is a RedisMap or an array with an even number of elements where each even-numbered +element is not an aggregate type.

+

RESP2 and RESP3 encode maps differently, and this function can be used to duck-type maps across protocol +versions.

+
source

pub fn is_array(&self) -> bool

Whether the value is an array.

+
source

pub fn as_u64(&self) -> Option<u64>

Read and return the inner value as a u64, if possible.

+
source

pub fn as_i64(&self) -> Option<i64>

Read and return the inner value as a i64, if possible.

+
source

pub fn as_usize(&self) -> Option<usize>

Read and return the inner value as a usize, if possible.

+
source

pub fn as_f64(&self) -> Option<f64>

Read and return the inner value as a f64, if possible.

+
source

pub fn into_string(self) -> Option<String>

Read and return the inner String if the value is a string or scalar value.

+
source

pub fn into_bytes_str(self) -> Option<Str>

Read and return the inner data as a Str from the bytes crate.

+
source

pub fn as_bytes_str(&self) -> Option<Str>

Read the inner value as a Str.

+
source

pub fn as_string(&self) -> Option<String>

Read and return the inner String if the value is a string or scalar value.

+

Note: this will cast integers and doubles to strings.

+
source

pub fn as_str(&self) -> Option<Cow<'_, str>>

Read the inner value as a string slice.

+

Null is returned as "nil" and scalar values are cast to a string.

+
source

pub fn as_str_lossy(&self) -> Option<Cow<'_, str>>

Read the inner value as a string, using String::from_utf8_lossy on byte slices.

+
source

pub fn as_bytes(&self) -> Option<&[u8]>

Read the inner value as an array of bytes, if possible.

+
source

pub fn as_bool(&self) -> Option<bool>

Attempt to convert the value to a bool.

+
source

pub fn into_map(self) -> Result<RedisMap, RedisError>

Attempt to convert this value to a Redis map if it’s an array with an even number of elements.

+
source

pub fn into_set(self) -> Result<HashSet<RedisValue>, RedisError>

Convert the array value to a set, if possible.

+
source

pub fn into_zset_result(self) -> Result<Vec<(RedisValue, f64)>, RedisError>

Convert a RedisValue to Vec<(RedisValue, f64)>, if possible.

+
source

pub fn into_array(self) -> Vec<RedisValue>

Convert this value to an array if it’s an array or map.

+

If the value is not an array or map this returns a single-element array containing the original value.

+
source

pub fn into_owned_bytes(self) -> Option<Vec<u8>>

Convert the value to an array of bytes, if possible.

+
source

pub fn into_bytes(self) -> Option<Bytes>

Convert the value into a Bytes view.

+
source

pub fn array_len(&self) -> Option<usize>

Return the length of the inner array if the value is an array.

+
source

pub fn flatten_array_values(self, depth: usize) -> Self

Flatten adjacent nested arrays to the provided depth.

+

See the XREAD documentation for an example of when this might be +useful.

+
source

pub fn into_xread_response<K1, I, K2, V>( + self, +) -> Result<XReadResponse<K1, I, K2, V>, RedisError>
where + K1: FromRedisKey + Hash + Eq, + K2: FromRedisKey + Hash + Eq, + I: FromRedis, + V: FromRedis,

Available on crate feature i-streams only.

A utility function to convert the response from XREAD or XREADGROUP into a type with a less verbose type +declaration.

+

This function supports responses in both RESP2 and RESP3 formats.

+

See the XREAD (or XREADGROUP) documentation for more +information.

+
source

pub fn into_xread_value<I, K, V>( + self, +) -> Result<Vec<XReadValue<I, K, V>>, RedisError>
where + K: FromRedisKey + Hash + Eq, + I: FromRedis, + V: FromRedis,

Available on crate feature i-streams only.

A utility function to convert the response from XCLAIM, etc into a type with a less verbose type declaration.

+

This function supports responses in both RESP2 and RESP3 formats.

+
source

pub fn into_xautoclaim_values<I, K, V>( + self, +) -> Result<(String, Vec<XReadValue<I, K, V>>), RedisError>
where + K: FromRedisKey + Hash + Eq, + I: FromRedis, + V: FromRedis,

Available on crate feature i-streams only.

A utility function to convert the response from XAUTOCLAIM into a type with a less verbose type declaration.

+

This function supports responses in both RESP2 and RESP3 formats.

+

Note: the new (as of Redis 7.x) return value containing message PIDs that were deleted from the PEL are dropped. +Callers should use xautoclaim instead if this data is needed.

+
source

pub fn as_functions(&self, name: &str) -> Result<Vec<Function>, RedisError>

Available on crate feature i-scripts only.

Parse the value as the response from FUNCTION LIST, including only functions with the provided library name.

+
source

pub fn as_geo_position(&self) -> Result<Option<GeoPosition>, RedisError>

Available on crate feature i-geo only.

Convert the value into a GeoPosition, if possible.

+

Null values are returned as None to work more easily with the result of the GEOPOS command.

+
source

pub fn into_geo_radius_result( + self, + withcoord: bool, + withdist: bool, + withhash: bool, +) -> Result<Vec<GeoRadiusInfo>, RedisError>

Available on crate feature i-geo only.

Parse the value as the response to any of the relevant GEO commands that return an array of +GeoRadiusInfo values, such as GEOSEARCH, GEORADIUS`, etc.

+
source

pub fn take(&mut self) -> RedisValue

Replace this value with RedisValue::Null, returning the original value.

+
source

pub fn convert<R>(self) -> Result<R, RedisError>
where + R: FromRedis,

Attempt to convert this value to any value that implements the FromRedis trait.

+
source

pub fn can_hash(&self) -> bool

Whether the value can be hashed.

+

Some use cases require using RedisValue types as keys in a HashMap, etc. Trying to do so with an aggregate +type can panic, and this function can be used to more gracefully handle this situation.

+
source

pub fn into_json(self) -> Result<Value, RedisError>

Available on crate feature serde-json only.

Convert the value to JSON.

+

Trait Implementations§

source§

impl Clone for RedisValue

source§

fn clone(&self) -> RedisValue

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for RedisValue

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'a> From<&'a [u8]> for RedisValue

source§

fn from(b: &'a [u8]) -> Self

Converts to this type from the input type.
source§

impl<'a> From<&'a String> for RedisValue

source§

fn from(d: &'a String) -> Self

Converts to this type from the input type.
source§

impl<'a> From<&'a str> for RedisValue

source§

fn from(d: &'a str) -> Self

Converts to this type from the input type.
source§

impl From<()> for RedisValue

source§

fn from(_: ()) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>> From<(A0, A1)> for RedisValue

source§

fn from(value: (A0, A1)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>> From<(A0, A1, A2)> for RedisValue

source§

fn from(value: (A0, A1, A2)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>> From<(A0, A1, A2, A3)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>> From<(A0, A1, A2, A3, A4)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>, A13: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)> for RedisValue

source§

fn from( + value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13), +) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>, A13: Into<RedisValue>, A14: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)> for RedisValue

source§

fn from( + value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14), +) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>, A13: Into<RedisValue>, A14: Into<RedisValue>, A15: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)> for RedisValue

source§

fn from( + value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15), +) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>, A13: Into<RedisValue>, A14: Into<RedisValue>, A15: Into<RedisValue>, A16: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16)> for RedisValue

source§

fn from( + value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16), +) -> Self

Converts to this type from the input type.
source§

impl From<Box<[u8]>> for RedisValue

source§

fn from(b: Box<[u8]>) -> Self

Converts to this type from the input type.
source§

impl From<Bytes> for RedisValue

source§

fn from(b: Bytes) -> Self

Converts to this type from the input type.
source§

impl From<RedisKey> for RedisValue

source§

fn from(d: RedisKey) -> Self

Converts to this type from the input type.
source§

impl From<RedisMap> for RedisValue

source§

fn from(m: RedisMap) -> Self

Converts to this type from the input type.
source§

impl From<StrInner<Bytes>> for RedisValue

source§

fn from(s: Str) -> Self

Converts to this type from the input type.
source§

impl From<String> for RedisValue

source§

fn from(d: String) -> Self

Converts to this type from the input type.
source§

impl From<bool> for RedisValue

source§

fn from(d: bool) -> Self

Converts to this type from the input type.
source§

impl From<f32> for RedisValue

source§

fn from(f: f32) -> Self

Converts to this type from the input type.
source§

impl From<f64> for RedisValue

source§

fn from(f: f64) -> Self

Converts to this type from the input type.
source§

impl From<i16> for RedisValue

source§

fn from(d: i16) -> Self

Converts to this type from the input type.
source§

impl From<i32> for RedisValue

source§

fn from(d: i32) -> Self

Converts to this type from the input type.
source§

impl From<i64> for RedisValue

source§

fn from(d: i64) -> Self

Converts to this type from the input type.
source§

impl From<i8> for RedisValue

source§

fn from(d: i8) -> Self

Converts to this type from the input type.
source§

impl From<u16> for RedisValue

source§

fn from(d: u16) -> Self

Converts to this type from the input type.
source§

impl From<u32> for RedisValue

source§

fn from(d: u32) -> Self

Converts to this type from the input type.
source§

impl From<u8> for RedisValue

source§

fn from(d: u8) -> Self

Converts to this type from the input type.
source§

impl<V> FromIterator<V> for RedisValue
where + V: Into<RedisValue>,

source§

fn from_iter<I: IntoIterator<Item = V>>(iter: I) -> Self

Creates a value from an iterator. Read more
source§

impl FromRedis for RedisValue

source§

impl FromRedisKey for RedisValue

source§

impl Hash for RedisValue

source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl PartialEq for RedisValue

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl<'a, T, const N: usize> TryFrom<&'a [T; N]> for RedisValue
where + T: TryInto<RedisValue> + Clone, + T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: &'a [T; N]) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<T, const N: usize> TryFrom<[T; N]> for RedisValue
where + T: TryInto<RedisValue> + Clone, + T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: [T; N]) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<K, V> TryFrom<BTreeMap<K, V>> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: BTreeMap<K, V>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<BytesFrame> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: Resp3Frame) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<K, V> TryFrom<HashMap<K, V>> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: HashMap<K, V>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<T> TryFrom<Option<T>> for RedisValue
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: Option<T>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<RedisValue> for ClusterInfo

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<RedisValue> for DatabaseMemoryStats

Available on crate feature i-memory only.
§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<RedisValue> for GeoPosition

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<RedisValue> for MemoryStats

Available on crate feature i-memory only.
§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<RedisValue> for RedisKey

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<RedisValue> for SlowlogEntry

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<RedisValue> for StringOrNumber

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<T> TryFrom<Vec<T>> for RedisValue
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: Vec<T>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<T> TryFrom<VecDeque<T>> for RedisValue
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: VecDeque<T>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<i128> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: i128) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<u128> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: u128) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<u64> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: u64) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<usize> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: usize) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for RedisValue

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CallHasher for T
where + T: Hash + ?Sized,

§

default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64
where + H: Hash + ?Sized, + B: BuildHasher,

source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.RedisValueKind.html b/doc/fred/types/enum.RedisValueKind.html new file mode 100644 index 00000000..663bb101 --- /dev/null +++ b/doc/fred/types/enum.RedisValueKind.html @@ -0,0 +1,44 @@ +RedisValueKind in fred::types - Rust

Enum fred::types::RedisValueKind

source ·
pub enum RedisValueKind {
+    Boolean,
+    Integer,
+    Double,
+    String,
+    Bytes,
+    Null,
+    Queued,
+    Map,
+    Array,
+}
Expand description

The kind of value from Redis.

+

Variants§

§

Boolean

§

Integer

§

Double

§

String

§

Bytes

§

Null

§

Queued

§

Map

§

Array

Trait Implementations§

source§

impl Clone for RedisValueKind

source§

fn clone(&self) -> RedisValueKind

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for RedisValueKind

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for RedisValueKind

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for RedisValueKind

source§

fn eq(&self, other: &RedisValueKind) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for RedisValueKind

source§

impl StructuralPartialEq for RedisValueKind

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.Reducer.html b/doc/fred/types/enum.Reducer.html new file mode 100644 index 00000000..edc33e19 --- /dev/null +++ b/doc/fred/types/enum.Reducer.html @@ -0,0 +1,44 @@ +Reducer in fred::types - Rust

Enum fred::types::Reducer

source ·
pub enum Reducer {
+    Avg,
+    Sum,
+    Min,
+    Max,
+    Range,
+    Count,
+    StdP,
+    StdS,
+    VarP,
+    VarS,
+}
Available on crate feature i-time-series only.
Expand description

A REDUCER argument in commands such as TS.MRANGE.

+

Variants§

§

Avg

§

Sum

§

Min

§

Max

§

Range

§

Count

§

StdP

§

StdS

§

VarP

§

VarS

Trait Implementations§

source§

impl Clone for Reducer

source§

fn clone(&self) -> Reducer

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Reducer

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Reducer

source§

fn eq(&self, other: &Reducer) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for Reducer

source§

impl StructuralPartialEq for Reducer

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ReducerFunc.html b/doc/fred/types/enum.ReducerFunc.html new file mode 100644 index 00000000..0fb153da --- /dev/null +++ b/doc/fred/types/enum.ReducerFunc.html @@ -0,0 +1,47 @@ +ReducerFunc in fred::types - Rust

Enum fred::types::ReducerFunc

source ·
pub enum ReducerFunc {
+
Show 13 variants Count, + CountDistinct, + CountDistinctIsh, + Sum, + Min, + Max, + Avg, + StdDev, + Quantile, + ToList, + FirstValue, + RandomSample, + Custom(&'static str), +
}
Available on crate feature i-redisearch only.
Expand description

GROUPBY reducer functions.

+

Variants§

§

Count

§

CountDistinct

§

CountDistinctIsh

§

Sum

§

Min

§

Max

§

Avg

§

StdDev

§

Quantile

§

ToList

§

FirstValue

§

RandomSample

§

Custom(&'static str)

Trait Implementations§

source§

impl Clone for ReducerFunc

source§

fn clone(&self) -> ReducerFunc

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ReducerFunc

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ReducerFunc

source§

fn eq(&self, other: &ReducerFunc) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ReducerFunc

source§

impl StructuralPartialEq for ReducerFunc

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.Resp3Frame.html b/doc/fred/types/enum.Resp3Frame.html new file mode 100644 index 00000000..03c5d341 --- /dev/null +++ b/doc/fred/types/enum.Resp3Frame.html @@ -0,0 +1,138 @@ +Resp3Frame in fred::types - Rust

Enum fred::types::Resp3Frame

pub enum Resp3Frame {
+
Show 16 variants BlobString { + data: Bytes, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + BlobError { + data: Bytes, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + SimpleString { + data: Bytes, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + SimpleError { + data: StrInner<Bytes>, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Boolean { + data: bool, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Null, + Number { + data: i64, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Double { + data: f64, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + BigNumber { + data: Bytes, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + VerbatimString { + data: Bytes, + format: VerbatimStringFormat, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Array { + data: Vec<BytesFrame>, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Map { + data: HashMap<BytesFrame, BytesFrame>, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Set { + data: HashSet<BytesFrame>, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Push { + data: Vec<BytesFrame>, + attributes: Option<HashMap<BytesFrame, BytesFrame>>, + }, + Hello { + version: RespVersion, + auth: Option<(StrInner<Bytes>, StrInner<Bytes>)>, + setname: Option<StrInner<Bytes>>, + }, + ChunkedString(Bytes), +
}
Expand description

A RESP3 frame that uses Bytes and Str as the underlying buffer type.

+

https://github.com/antirez/RESP3/blob/master/spec.md

+

Variants§

§

BlobString

A blob of bytes.

+

Fields

§data: Bytes
§

BlobError

A blob representing an error.

+

Fields

§data: Bytes
§

SimpleString

A small string.

+

The internal data type is Bytes in order to support callers that use this interface to parse a MONITOR +stream.

+

Fields

§data: Bytes
§

SimpleError

A small string representing an error.

+

Fields

§data: StrInner<Bytes>
§

Boolean

A boolean type.

+

Fields

§data: bool
§

Null

A null type.

+
§

Number

A signed 64-bit integer.

+

Fields

§data: i64
§

Double

A signed 64-bit floating point number.

+

Fields

§data: f64
§

BigNumber

A large number not representable as a Number or Double.

+

This library does not attempt to parse this.

+

Fields

§data: Bytes
§

VerbatimString

A string to be displayed without any escaping or filtering.

+

Fields

§data: Bytes
§format: VerbatimStringFormat
§

Array

An array of frames.

+
§

Map

An unordered map of key-value pairs.

+

According to the spec keys can be any other RESP3 data type. However, it doesn’t make sense to implement Hash +for certain aggregate types. The can_hash function can be used to +detect this.

+
§

Set

An unordered collection of other frames with a uniqueness constraint.

+
§

Push

Out-of-band data.

+
§

Hello

A special frame type used when first connecting to the server to describe the protocol version and optional +credentials.

+

Fields

§version: RespVersion
§auth: Option<(StrInner<Bytes>, StrInner<Bytes>)>
§setname: Option<StrInner<Bytes>>
§

ChunkedString(Bytes)

One chunk of a streaming blob.

+

Implementations§

§

impl BytesFrame

pub fn to_owned_frame(&self) -> OwnedFrame

Copy the frame contents into a new [OwnedFrame].

+

Trait Implementations§

§

impl Clone for BytesFrame

§

fn clone(&self) -> BytesFrame

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for BytesFrame

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl From<bool> for BytesFrame

§

fn from(value: bool) -> BytesFrame

Converts to this type from the input type.
§

impl From<f64> for BytesFrame

§

fn from(value: f64) -> BytesFrame

Converts to this type from the input type.
§

impl From<i64> for BytesFrame

§

fn from(value: i64) -> BytesFrame

Converts to this type from the input type.
§

impl Hash for BytesFrame

§

fn hash<H>(&self, state: &mut H)
where + H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
§

impl PartialEq for BytesFrame

§

fn eq(&self, other: &BytesFrame) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
§

impl Resp3Frame for BytesFrame

§

fn add_attributes( + &mut self, + attributes: <BytesFrame as Resp3Frame>::Attributes, +) -> Result<(), RedisProtocolError>

Attempt to add attributes to the frame, extending the existing attributes if needed.

+
§

type Attributes = HashMap<BytesFrame, BytesFrame>

§

fn from_buffer( + target: FrameKind, + buf: impl IntoIterator<Item = BytesFrame>, + attributes: Option<<BytesFrame as Resp3Frame>::Attributes>, +) -> Result<BytesFrame, RedisProtocolError>

Create the target aggregate type based on a buffered set of chunked frames.
§

fn attributes(&self) -> Option<&<BytesFrame as Resp3Frame>::Attributes>

Read the attributes attached to the frame.
§

fn take_attributes(&mut self) -> Option<<BytesFrame as Resp3Frame>::Attributes>

Take the attributes off this frame.
§

fn attributes_mut( + &mut self, +) -> Option<&mut <BytesFrame as Resp3Frame>::Attributes>

Read a mutable reference to any attributes attached to the frame.
§

fn new_empty() -> BytesFrame

Create a new empty frame with attribute support.
§

fn new_end_stream() -> BytesFrame

Create a new frame that terminates a stream.
§

fn len(&self) -> usize

A context-aware length function that returns the length of the inner frame contents. Read more
§

fn take(&mut self) -> BytesFrame

Replace self with Null, returning the original value.
§

fn kind(&self) -> FrameKind

Read the associated FrameKind.
§

fn is_end_stream_frame(&self) -> bool

Whether the frame is an empty chunked string, signifying the end of a chunked string stream.
§

fn verbatim_string_format(&self) -> Option<&VerbatimStringFormat>

If the frame is a verbatim string then read the associated format.
§

fn as_str(&self) -> Option<&str>

Read the frame as a string slice if it can be parsed as a UTF-8 string without allocating.
§

fn as_bool(&self) -> Option<bool>

Attempt to convert the frame to a bool.
§

fn to_string(&self) -> Option<String>

Read the frame as a String if it can be parsed as a UTF-8 string.
§

fn as_bytes(&self) -> Option<&[u8]>

Attempt to read the frame as a byte slice.
§

fn encode_len(&self) -> usize

Read the number of bytes necessary to represent the frame and any associated attributes.
§

fn is_normal_pubsub_message(&self) -> bool

Whether the frame is a message from a subscribe call.
§

fn is_pattern_pubsub_message(&self) -> bool

Whether the frame is a message from a psubscribe call.
§

fn is_shard_pubsub_message(&self) -> bool

Whether the frame is a message from a ssubscribe call.
§

fn is_redirection(&self) -> bool

Whether the frame is a MOVED or ASK redirection.
§

fn as_f64(&self) -> Option<f64>

Convert the frame to a double.
§

impl<B> TryFrom<(FrameKind, B)> for BytesFrame
where + B: Into<Bytes>,

§

type Error = RedisProtocolError

The type returned in the event of a conversion error.
§

fn try_from( + _: (FrameKind, B), +) -> Result<BytesFrame, <BytesFrame as TryFrom<(FrameKind, B)>>::Error>

Performs the conversion.
source§

impl TryFrom<BytesFrame> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: Resp3Frame) -> Result<Self, Self::Error>

Performs the conversion.
§

impl Eq for BytesFrame

§

impl StructuralPartialEq for BytesFrame

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CallHasher for T
where + T: Hash + ?Sized,

§

default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64
where + H: Hash + ?Sized, + B: BuildHasher,

source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.RespVersion.html b/doc/fred/types/enum.RespVersion.html new file mode 100644 index 00000000..91f19b8e --- /dev/null +++ b/doc/fred/types/enum.RespVersion.html @@ -0,0 +1,36 @@ +RespVersion in fred::types - Rust

Enum fred::types::RespVersion

pub enum RespVersion {
+    RESP2,
+    RESP3,
+}
Expand description

The RESP version used in the HELLO request.

+

Variants§

§

RESP2

§

RESP3

Implementations§

§

impl RespVersion

pub fn to_byte(&self) -> u8

Trait Implementations§

§

impl Clone for RespVersion

§

fn clone(&self) -> RespVersion

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for RespVersion

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl PartialEq for RespVersion

§

fn eq(&self, other: &RespVersion) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
§

impl Eq for RespVersion

§

impl StructuralPartialEq for RespVersion

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ScanType.html b/doc/fred/types/enum.ScanType.html new file mode 100644 index 00000000..29e226b7 --- /dev/null +++ b/doc/fred/types/enum.ScanType.html @@ -0,0 +1,40 @@ +ScanType in fred::types - Rust

Enum fred::types::ScanType

source ·
pub enum ScanType {
+    Set,
+    String,
+    ZSet,
+    List,
+    Hash,
+    Stream,
+}
Expand description

The types of values supported by the type command.

+

Variants§

§

Set

§

String

§

ZSet

§

List

§

Hash

§

Stream

Trait Implementations§

source§

impl Clone for ScanType

source§

fn clone(&self) -> ScanType

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ScanType

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ScanType

source§

fn eq(&self, other: &ScanType) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ScanType

source§

impl StructuralPartialEq for ScanType

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ScriptDebugFlag.html b/doc/fred/types/enum.ScriptDebugFlag.html new file mode 100644 index 00000000..40351a8b --- /dev/null +++ b/doc/fred/types/enum.ScriptDebugFlag.html @@ -0,0 +1,37 @@ +ScriptDebugFlag in fred::types - Rust

Enum fred::types::ScriptDebugFlag

source ·
pub enum ScriptDebugFlag {
+    Yes,
+    No,
+    Sync,
+}
Expand description

Flags for the SCRIPT DEBUG command.

+

Variants§

§

Yes

§

No

§

Sync

Trait Implementations§

source§

impl Clone for ScriptDebugFlag

source§

fn clone(&self) -> ScriptDebugFlag

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ScriptDebugFlag

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ScriptDebugFlag

source§

fn eq(&self, other: &ScriptDebugFlag) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ScriptDebugFlag

source§

impl StructuralPartialEq for ScriptDebugFlag

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.SearchSchemaKind.html b/doc/fred/types/enum.SearchSchemaKind.html new file mode 100644 index 00000000..afd8efd3 --- /dev/null +++ b/doc/fred/types/enum.SearchSchemaKind.html @@ -0,0 +1,70 @@ +SearchSchemaKind in fred::types - Rust

Enum fred::types::SearchSchemaKind

source ·
pub enum SearchSchemaKind {
+    Text {
+        sortable: bool,
+        unf: bool,
+        nostem: bool,
+        phonetic: Option<Str>,
+        weight: Option<i64>,
+        withsuffixtrie: bool,
+        noindex: bool,
+    },
+    Tag {
+        sortable: bool,
+        unf: bool,
+        separator: Option<char>,
+        casesensitive: bool,
+        withsuffixtrie: bool,
+        noindex: bool,
+    },
+    Numeric {
+        sortable: bool,
+        unf: bool,
+        noindex: bool,
+    },
+    Geo {
+        sortable: bool,
+        unf: bool,
+        noindex: bool,
+    },
+    Vector {
+        noindex: bool,
+    },
+    GeoShape {
+        noindex: bool,
+    },
+    Custom {
+        name: Str,
+        arguments: Vec<RedisValue>,
+    },
+}
Available on crate feature i-redisearch only.
Expand description

One of the available schema types used with FT.CREATE or FT.ALTER.

+

Variants§

§

Text

Fields

§sortable: bool
§unf: bool
§nostem: bool
§phonetic: Option<Str>
§weight: Option<i64>
§withsuffixtrie: bool
§noindex: bool
§

Tag

Fields

§sortable: bool
§unf: bool
§separator: Option<char>
§casesensitive: bool
§withsuffixtrie: bool
§noindex: bool
§

Numeric

Fields

§sortable: bool
§unf: bool
§noindex: bool
§

Geo

Fields

§sortable: bool
§unf: bool
§noindex: bool
§

Vector

Fields

§noindex: bool
§

GeoShape

Fields

§noindex: bool
§

Custom

Fields

§name: Str
§arguments: Vec<RedisValue>

Trait Implementations§

source§

impl Clone for SearchSchemaKind

source§

fn clone(&self) -> SearchSchemaKind

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SearchSchemaKind

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.SentinelFailureKind.html b/doc/fred/types/enum.SentinelFailureKind.html new file mode 100644 index 00000000..2d424cfa --- /dev/null +++ b/doc/fred/types/enum.SentinelFailureKind.html @@ -0,0 +1,37 @@ +SentinelFailureKind in fred::types - Rust

Enum fred::types::SentinelFailureKind

source ·
pub enum SentinelFailureKind {
+    CrashAfterElection,
+    CrashAfterPromotion,
+    Help,
+}
Available on crate feature sentinel-client only.
Expand description

Arguments for the SENTINEL SIMULATE-FAILURE command.

+

Variants§

§

CrashAfterElection

§

CrashAfterPromotion

§

Help

Trait Implementations§

source§

impl Clone for SentinelFailureKind

source§

fn clone(&self) -> SentinelFailureKind

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SentinelFailureKind

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for SentinelFailureKind

source§

fn eq(&self, other: &SentinelFailureKind) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for SentinelFailureKind

source§

impl StructuralPartialEq for SentinelFailureKind

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ServerConfig.html b/doc/fred/types/enum.ServerConfig.html new file mode 100644 index 00000000..8120d576 --- /dev/null +++ b/doc/fred/types/enum.ServerConfig.html @@ -0,0 +1,73 @@ +ServerConfig in fred::types - Rust

Enum fred::types::ServerConfig

source ·
pub enum ServerConfig {
+    Centralized {
+        server: Server,
+    },
+    Clustered {
+        hosts: Vec<Server>,
+        policy: ClusterDiscoveryPolicy,
+    },
+    Sentinel {
+        hosts: Vec<Server>,
+        service_name: String,
+    },
+}
Expand description

Connection configuration for the Redis server.

+

Variants§

§

Centralized

Fields

§server: Server

The Server identifier.

+
§

Clustered

Fields

§hosts: Vec<Server>

The known cluster node Server identifiers.

+

Only one node in the cluster needs to be provided here, the rest will be discovered via the CLUSTER SLOTS +command.

+
§policy: ClusterDiscoveryPolicy

The cluster discovery policy to use when connecting or following redirections.

+
§

Sentinel

Fields

§hosts: Vec<Server>

An array of Server identifiers for each known sentinel instance.

+
§service_name: String

The service name for primary/main instances.

+

Implementations§

source§

impl ServerConfig

source

pub fn new_centralized<S>(host: S, port: u16) -> ServerConfig
where + S: Into<String>,

Create a new centralized config with the provided host and port.

+
source

pub fn new_clustered<S>(hosts: Vec<(S, u16)>) -> ServerConfig
where + S: Into<String>,

Create a new clustered config with the provided set of hosts and ports.

+

Only one valid host in the cluster needs to be provided here. The client will use CLUSTER NODES to discover +the other nodes.

+
source

pub fn new_sentinel<H, N>(hosts: Vec<(H, u16)>, service_name: N) -> ServerConfig
where + H: Into<String>, + N: Into<String>,

Create a new sentinel config with the provided set of hosts and the name of the service.

+

This library will connect using the details from the Redis documentation.

+
source

pub fn default_centralized() -> ServerConfig

Create a centralized config with default settings for a local deployment.

+
source

pub fn default_clustered() -> ServerConfig

Create a clustered config with the same defaults as specified in the create-cluster script provided by Redis.

+
source

pub fn is_clustered(&self) -> bool

Whether the config uses a clustered deployment.

+
source

pub fn is_sentinel(&self) -> bool

Whether the config is for a centralized server behind a sentinel node(s).

+
source

pub fn is_centralized(&self) -> bool

Whether the config is for a centralized server.

+
source

pub fn is_unix_socket(&self) -> bool

Whether the config uses a Unix socket.

+
source

pub fn hosts(&self) -> Vec<Server>

Read the server hosts or sentinel hosts if using the sentinel interface.

+
source

pub fn set_cluster_discovery_policy( + &mut self, + new_policy: ClusterDiscoveryPolicy, +) -> Result<(), RedisError>

Set the ClusterDiscoveryPolicy, if possible.

+

Trait Implementations§

source§

impl Clone for ServerConfig

source§

fn clone(&self) -> ServerConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ServerConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for ServerConfig

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for ServerConfig

source§

fn eq(&self, other: &ServerConfig) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ServerConfig

source§

impl StructuralPartialEq for ServerConfig

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.SetOptions.html b/doc/fred/types/enum.SetOptions.html new file mode 100644 index 00000000..c628c379 --- /dev/null +++ b/doc/fred/types/enum.SetOptions.html @@ -0,0 +1,37 @@ +SetOptions in fred::types - Rust

Enum fred::types::SetOptions

source ·
pub enum SetOptions {
+    NX,
+    XX,
+}
Expand description

Options for the set command.

+

https://redis.io/commands/set

+

Variants§

§

NX

§

XX

Trait Implementations§

source§

impl Clone for SetOptions

source§

fn clone(&self) -> SetOptions

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SetOptions

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for SetOptions

source§

fn eq(&self, other: &SetOptions) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for SetOptions

source§

impl StructuralPartialEq for SetOptions

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ShutdownFlags.html b/doc/fred/types/enum.ShutdownFlags.html new file mode 100644 index 00000000..63076d48 --- /dev/null +++ b/doc/fred/types/enum.ShutdownFlags.html @@ -0,0 +1,37 @@ +ShutdownFlags in fred::types - Rust

Enum fred::types::ShutdownFlags

source ·
pub enum ShutdownFlags {
+    Save,
+    NoSave,
+}
Expand description

Arguments passed to the SHUTDOWN command.

+

https://redis.io/commands/shutdown

+

Variants§

§

Save

§

NoSave

Trait Implementations§

source§

impl Clone for ShutdownFlags

source§

fn clone(&self) -> ShutdownFlags

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ShutdownFlags

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ShutdownFlags

source§

fn eq(&self, other: &ShutdownFlags) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ShutdownFlags

source§

impl StructuralPartialEq for ShutdownFlags

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.SortOrder.html b/doc/fred/types/enum.SortOrder.html new file mode 100644 index 00000000..8336b231 --- /dev/null +++ b/doc/fred/types/enum.SortOrder.html @@ -0,0 +1,36 @@ +SortOrder in fred::types - Rust

Enum fred::types::SortOrder

source ·
pub enum SortOrder {
+    Asc,
+    Desc,
+}
Expand description

The sort order for redis commands that take or return a sorted list.

+

Variants§

§

Asc

§

Desc

Trait Implementations§

source§

impl Clone for SortOrder

source§

fn clone(&self) -> SortOrder

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SortOrder

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for SortOrder

source§

fn eq(&self, other: &SortOrder) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for SortOrder

source§

impl StructuralPartialEq for SortOrder

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.SpellcheckTerms.html b/doc/fred/types/enum.SpellcheckTerms.html new file mode 100644 index 00000000..d29ee424 --- /dev/null +++ b/doc/fred/types/enum.SpellcheckTerms.html @@ -0,0 +1,41 @@ +SpellcheckTerms in fred::types - Rust

Enum fred::types::SpellcheckTerms

source ·
pub enum SpellcheckTerms {
+    Include {
+        dictionary: Str,
+        terms: Vec<Str>,
+    },
+    Exclude {
+        dictionary: Str,
+        terms: Vec<Str>,
+    },
+}
Available on crate feature i-redisearch only.
Expand description

Arguments to TERMS in FT.SPELLCHECK,

+

Variants§

§

Include

Fields

§dictionary: Str
§terms: Vec<Str>
§

Exclude

Fields

§dictionary: Str
§terms: Vec<Str>

Trait Implementations§

source§

impl Clone for SpellcheckTerms

source§

fn clone(&self) -> SpellcheckTerms

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SpellcheckTerms

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.StringOrNumber.html b/doc/fred/types/enum.StringOrNumber.html new file mode 100644 index 00000000..6443800d --- /dev/null +++ b/doc/fred/types/enum.StringOrNumber.html @@ -0,0 +1,38 @@ +StringOrNumber in fred::types - Rust

Enum fred::types::StringOrNumber

source ·
pub enum StringOrNumber {
+    String(Str),
+    Number(i64),
+    Double(f64),
+}
Expand description

An argument representing a string or number.

+

Variants§

§

String(Str)

§

Number(i64)

§

Double(f64)

Implementations§

source§

impl StringOrNumber

source

pub fn from_static_str(s: &'static str) -> Self

An optimized way to convert from &'static str that avoids copying or moving the underlying bytes.

+

Trait Implementations§

source§

impl Clone for StringOrNumber

source§

fn clone(&self) -> StringOrNumber

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for StringOrNumber

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'a> From<&'a str> for StringOrNumber

source§

fn from(s: &'a str) -> Self

Converts to this type from the input type.
source§

impl From<StrInner<Bytes>> for StringOrNumber

source§

fn from(s: Str) -> Self

Converts to this type from the input type.
source§

impl From<String> for StringOrNumber

source§

fn from(s: String) -> Self

Converts to this type from the input type.
source§

impl From<f32> for StringOrNumber

source§

fn from(f: f32) -> Self

Converts to this type from the input type.
source§

impl From<f64> for StringOrNumber

source§

fn from(f: f64) -> Self

Converts to this type from the input type.
source§

impl From<i16> for StringOrNumber

source§

fn from(val: i16) -> Self

Converts to this type from the input type.
source§

impl From<i32> for StringOrNumber

source§

fn from(val: i32) -> Self

Converts to this type from the input type.
source§

impl From<i64> for StringOrNumber

source§

fn from(val: i64) -> Self

Converts to this type from the input type.
source§

impl From<i8> for StringOrNumber

source§

fn from(val: i8) -> Self

Converts to this type from the input type.
source§

impl From<isize> for StringOrNumber

source§

fn from(val: isize) -> Self

Converts to this type from the input type.
source§

impl From<u16> for StringOrNumber

source§

fn from(val: u16) -> Self

Converts to this type from the input type.
source§

impl From<u32> for StringOrNumber

source§

fn from(val: u32) -> Self

Converts to this type from the input type.
source§

impl From<u64> for StringOrNumber

source§

fn from(val: u64) -> Self

Converts to this type from the input type.
source§

impl From<u8> for StringOrNumber

source§

fn from(val: u8) -> Self

Converts to this type from the input type.
source§

impl From<usize> for StringOrNumber

source§

fn from(val: usize) -> Self

Converts to this type from the input type.
source§

impl PartialEq for StringOrNumber

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl TryFrom<RedisValue> for StringOrNumber

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for StringOrNumber

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.Timestamp.html b/doc/fred/types/enum.Timestamp.html new file mode 100644 index 00000000..4cc6d7c9 --- /dev/null +++ b/doc/fred/types/enum.Timestamp.html @@ -0,0 +1,38 @@ +Timestamp in fred::types - Rust

Enum fred::types::Timestamp

source ·
pub enum Timestamp {
+    Custom(i64),
+    Now,
+}
Available on crate feature i-time-series only.
Expand description

A timestamp used in most timeseries commands.

+

Variants§

§

Custom(i64)

Unix time (milliseconds since epoch).

+
§

Now

The server’s current time, equivalent to “*”.

+

Trait Implementations§

source§

impl Clone for Timestamp

source§

fn clone(&self) -> Timestamp

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Timestamp

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Timestamp

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl From<i64> for Timestamp

source§

fn from(value: i64) -> Self

Converts to this type from the input type.
source§

impl PartialEq for Timestamp

source§

fn eq(&self, other: &Timestamp) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl TryFrom<&str> for Timestamp

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: &str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<StrInner<Bytes>> for Timestamp

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: Str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<String> for Timestamp

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: String) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for Timestamp

source§

impl StructuralPartialEq for Timestamp

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.TlsConnector.html b/doc/fred/types/enum.TlsConnector.html new file mode 100644 index 00000000..33dbaa8b --- /dev/null +++ b/doc/fred/types/enum.TlsConnector.html @@ -0,0 +1,38 @@ +TlsConnector in fred::types - Rust

Enum fred::types::TlsConnector

source ·
pub enum TlsConnector {
+    Native(TlsConnector),
+    Rustls(TlsConnector),
+}
Available on crate features enable-rustls or enable-native-tls or enable-rustls-ring only.
Expand description

An enum for interacting with various TLS libraries and interfaces.

+

Variants§

§

Native(TlsConnector)

Available on crate feature enable-native-tls only.
§

Rustls(TlsConnector)

Available on crate features enable-rustls or enable-rustls-ring only.

Implementations§

source§

impl TlsConnector

source

pub fn default_native_tls() -> Result<Self, RedisError>

Available on (crate features enable-native-tls or enable-rustls) and crate feature enable-native-tls only.

Create a default TLS connector from the native-tls module.

+
source

pub fn default_rustls() -> Result<Self, RedisError>

Available on (crate features enable-native-tls or enable-rustls) and (crate features enable-rustls or enable-rustls-ring) only.

Create a default TLS connector with the rustls module with safe defaults and system certs via rustls-native-certs.

+

Trait Implementations§

source§

impl Clone for TlsConnector

source§

fn clone(&self) -> TlsConnector

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for TlsConnector

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<ClientConfig> for TlsConnector

Available on crate features enable-rustls or enable-rustls-ring only.
source§

fn from(config: RustlsClientConfig) -> Self

Converts to this type from the input type.
source§

impl From<TlsConnector> for TlsConnector

Available on crate feature enable-native-tls only.
source§

fn from(connector: NativeTlsConnector) -> Self

Converts to this type from the input type.
source§

impl From<TlsConnector> for TlsConnector

Available on crate feature enable-native-tls only.
source§

fn from(connector: TokioNativeTlsConnector) -> Self

Converts to this type from the input type.
source§

impl From<TlsConnector> for TlsConnector

Available on crate features enable-rustls or enable-rustls-ring only.
source§

fn from(connector: RustlsConnector) -> Self

Converts to this type from the input type.
source§

impl PartialEq for TlsConnector

source§

fn eq(&self, _: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl TryFrom<TlsConnectorBuilder> for TlsConnector

Available on crate feature enable-native-tls only.
§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(builder: NativeTlsConnectorBuilder) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for TlsConnector

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.TlsHostMapping.html b/doc/fred/types/enum.TlsHostMapping.html new file mode 100644 index 00000000..80dbddfb --- /dev/null +++ b/doc/fred/types/enum.TlsHostMapping.html @@ -0,0 +1,48 @@ +TlsHostMapping in fred::types - Rust

Enum fred::types::TlsHostMapping

source ·
pub enum TlsHostMapping {
+    None,
+    DefaultHost,
+    Custom(Arc<dyn HostMapping>),
+}
Available on crate features enable-rustls or enable-native-tls or enable-rustls-ring only.
Expand description

An optional enum used to describe how the client should modify or map IP addresses and hostnames in a clustered +deployment.

+

This is only necessary to use with a clustered deployment. Centralized or sentinel deployments should use None.

+

More information can be found here and here.

+

Variants§

§

None

Do not modify or replace hostnames or IP addresses in the CLUSTER SLOTS response.

+

Default

+
§

DefaultHost

Replace any IP addresses in the CLUSTER SLOTS response with the hostname of the node that returned +the CLUSTER SLOTS response.

+

If the CLUSTER SLOTS response contains hostnames alongside IP addresses (via the metadata block) then +those hostnames will be used instead. However, this is a relatively new Redis feature and it’s likely some +configurations will not expose this information.

+
§

Custom(Arc<dyn HostMapping>)

Provide a custom mapping from IP address to hostname to be used in a manner similar to a reverse DNS lookup.

+

Trait Implementations§

source§

impl Clone for TlsHostMapping

source§

fn clone(&self) -> TlsHostMapping

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for TlsHostMapping

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for TlsHostMapping

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for TlsHostMapping

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.Toggle.html b/doc/fred/types/enum.Toggle.html new file mode 100644 index 00000000..c90452b6 --- /dev/null +++ b/doc/fred/types/enum.Toggle.html @@ -0,0 +1,36 @@ +Toggle in fred::types - Rust

Enum fred::types::Toggle

source ·
pub enum Toggle {
+    On,
+    Off,
+}
Available on crate features i-client and i-tracking only.
Expand description

An ON|OFF flag used with client tracking commands.

+

Variants§

§

On

§

Off

Trait Implementations§

source§

impl Clone for Toggle

source§

fn clone(&self) -> Toggle

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Toggle

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<bool> for Toggle

source§

fn from(value: bool) -> Self

Converts to this type from the input type.
source§

impl PartialEq for Toggle

source§

fn eq(&self, other: &Toggle) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl TryFrom<&String> for Toggle

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: &String) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<&str> for Toggle

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: &str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<String> for Toggle

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: String) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for Toggle

source§

impl StructuralPartialEq for Toggle

Auto Trait Implementations§

§

impl Freeze for Toggle

§

impl RefUnwindSafe for Toggle

§

impl Send for Toggle

§

impl Sync for Toggle

§

impl Unpin for Toggle

§

impl UnwindSafe for Toggle

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.XCapKind.html b/doc/fred/types/enum.XCapKind.html new file mode 100644 index 00000000..9b409519 --- /dev/null +++ b/doc/fred/types/enum.XCapKind.html @@ -0,0 +1,36 @@ +XCapKind in fred::types - Rust

Enum fred::types::XCapKind

source ·
pub enum XCapKind {
+    MaxLen,
+    MinID,
+}
Available on crate feature i-streams only.
Expand description

The MAXLEN or MINID argument for a stream cap.

+

Variants§

§

MaxLen

§

MinID

Trait Implementations§

source§

impl Clone for XCapKind

source§

fn clone(&self) -> XCapKind

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for XCapKind

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for XCapKind

source§

fn eq(&self, other: &XCapKind) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl<'a> TryFrom<&'a str> for XCapKind

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: &'a str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for XCapKind

source§

impl StructuralPartialEq for XCapKind

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.XCapTrim.html b/doc/fred/types/enum.XCapTrim.html new file mode 100644 index 00000000..6fe3a7ec --- /dev/null +++ b/doc/fred/types/enum.XCapTrim.html @@ -0,0 +1,36 @@ +XCapTrim in fred::types - Rust

Enum fred::types::XCapTrim

source ·
pub enum XCapTrim {
+    Exact,
+    AlmostExact,
+}
Available on crate feature i-streams only.
Expand description

Representation for the “=” or “~” operator in XADD, etc.

+

Variants§

§

Exact

§

AlmostExact

Trait Implementations§

source§

impl Clone for XCapTrim

source§

fn clone(&self) -> XCapTrim

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for XCapTrim

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for XCapTrim

source§

fn eq(&self, other: &XCapTrim) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl<'a> TryFrom<&'a str> for XCapTrim

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(s: &'a str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for XCapTrim

source§

impl StructuralPartialEq for XCapTrim

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.XID.html b/doc/fred/types/enum.XID.html new file mode 100644 index 00000000..8bd4580f --- /dev/null +++ b/doc/fred/types/enum.XID.html @@ -0,0 +1,42 @@ +XID in fred::types - Rust

Enum fred::types::XID

source ·
pub enum XID {
+    Auto,
+    Manual(Str),
+    Max,
+    NewInGroup,
+}
Available on crate feature i-streams only.
Expand description

Stream ID arguments for XADD, XREAD, etc.

+

Variants§

§

Auto

The auto-generated key symbol “*”.

+
§

Manual(Str)

An ID specified by the user such as “12345-0”.

+
§

Max

The highest ID in a stream (“$”).

+
§

NewInGroup

For XREADGROUP, only return new IDs (“>”).

+

Trait Implementations§

source§

impl Clone for XID

source§

fn clone(&self) -> XID

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for XID

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'a> From<&'a String> for XID

source§

fn from(value: &'a String) -> Self

Converts to this type from the input type.
source§

impl<'a> From<&'a str> for XID

source§

fn from(value: &'a str) -> Self

Converts to this type from the input type.
source§

impl From<StrInner<Bytes>> for XID

source§

fn from(value: Str) -> Self

Converts to this type from the input type.
source§

impl From<String> for XID

source§

fn from(value: String) -> Self

Converts to this type from the input type.
source§

impl PartialEq for XID

source§

fn eq(&self, other: &XID) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for XID

source§

impl StructuralPartialEq for XID

Auto Trait Implementations§

§

impl !Freeze for XID

§

impl RefUnwindSafe for XID

§

impl Send for XID

§

impl Sync for XID

§

impl Unpin for XID

§

impl UnwindSafe for XID

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ZCmp.html b/doc/fred/types/enum.ZCmp.html new file mode 100644 index 00000000..f9221cb1 --- /dev/null +++ b/doc/fred/types/enum.ZCmp.html @@ -0,0 +1,36 @@ +ZCmp in fred::types - Rust

Enum fred::types::ZCmp

source ·
pub enum ZCmp {
+    Min,
+    Max,
+}
Available on crate feature i-sorted-sets only.
Expand description

MIN|MAX arguments for BZMPOP, etc.

+

Variants§

§

Min

§

Max

Trait Implementations§

source§

impl Clone for ZCmp

source§

fn clone(&self) -> ZCmp

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ZCmp

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ZCmp

source§

fn eq(&self, other: &ZCmp) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ZCmp

source§

impl StructuralPartialEq for ZCmp

Auto Trait Implementations§

§

impl Freeze for ZCmp

§

impl RefUnwindSafe for ZCmp

§

impl Send for ZCmp

§

impl Sync for ZCmp

§

impl Unpin for ZCmp

§

impl UnwindSafe for ZCmp

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ZRangeBound.html b/doc/fred/types/enum.ZRangeBound.html new file mode 100644 index 00000000..4b695d0a --- /dev/null +++ b/doc/fred/types/enum.ZRangeBound.html @@ -0,0 +1,47 @@ +ZRangeBound in fred::types - Rust

Enum fred::types::ZRangeBound

source ·
pub enum ZRangeBound {
+    Index(i64),
+    Score(f64),
+    Lex(String),
+    InfiniteLex,
+    NegInfinityLex,
+    InfiniteScore,
+    NegInfiniteScore,
+}
Available on crate feature i-sorted-sets only.
Expand description

An index, score, lexicographical, or +|-|+inf|-inf range bound for the ZRANGE command.

+

Variants§

§

Index(i64)

§

Score(f64)

§

Lex(String)

§

InfiniteLex

Shortcut for the + character.

+
§

NegInfinityLex

Shortcut for the - character.

+
§

InfiniteScore

Shortcut for the +inf range bound.

+
§

NegInfiniteScore

Shortcut for the -inf range bound.

+

Trait Implementations§

source§

impl Clone for ZRangeBound

source§

fn clone(&self) -> ZRangeBound

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ZRangeBound

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'a> From<&'a String> for ZRangeBound

source§

fn from(s: &'a String) -> Self

Converts to this type from the input type.
source§

impl<'a> From<&'a str> for ZRangeBound

source§

fn from(s: &'a str) -> Self

Converts to this type from the input type.
source§

impl From<String> for ZRangeBound

source§

fn from(s: String) -> Self

Converts to this type from the input type.
source§

impl From<i64> for ZRangeBound

source§

fn from(i: i64) -> Self

Converts to this type from the input type.
source§

impl TryFrom<f64> for ZRangeBound

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(f: f64) -> Result<Self, Self::Error>

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ZRangeKind.html b/doc/fred/types/enum.ZRangeKind.html new file mode 100644 index 00000000..e72a90c2 --- /dev/null +++ b/doc/fred/types/enum.ZRangeKind.html @@ -0,0 +1,36 @@ +ZRangeKind in fred::types - Rust

Enum fred::types::ZRangeKind

source ·
pub enum ZRangeKind {
+    Inclusive,
+    Exclusive,
+}
Available on crate feature i-sorted-sets only.
Expand description

The type of range interval bound.

+

Variants§

§

Inclusive

§

Exclusive

Trait Implementations§

source§

impl Clone for ZRangeKind

source§

fn clone(&self) -> ZRangeKind

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ZRangeKind

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for ZRangeKind

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for ZRangeKind

source§

fn eq(&self, other: &ZRangeKind) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ZRangeKind

source§

impl StructuralPartialEq for ZRangeKind

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/enum.ZSort.html b/doc/fred/types/enum.ZSort.html new file mode 100644 index 00000000..85cf5b54 --- /dev/null +++ b/doc/fred/types/enum.ZSort.html @@ -0,0 +1,36 @@ +ZSort in fred::types - Rust

Enum fred::types::ZSort

source ·
pub enum ZSort {
+    ByScore,
+    ByLex,
+}
Available on crate feature i-sorted-sets only.
Expand description

Options for the ZRANGE (and related) commands.

+

Variants§

§

ByScore

§

ByLex

Trait Implementations§

source§

impl Clone for ZSort

source§

fn clone(&self) -> ZSort

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ZSort

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for ZSort

source§

fn eq(&self, other: &ZSort) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ZSort

source§

impl StructuralPartialEq for ZSort

Auto Trait Implementations§

§

impl Freeze for ZSort

§

impl RefUnwindSafe for ZSort

§

impl Send for ZSort

§

impl Sync for ZSort

§

impl Unpin for ZSort

§

impl UnwindSafe for ZSort

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/geo/enum.GeoUnit.html b/doc/fred/types/geo/enum.GeoUnit.html new file mode 100644 index 00000000..45f95c88 --- /dev/null +++ b/doc/fred/types/geo/enum.GeoUnit.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.GeoUnit.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/geo/struct.GeoPosition.html b/doc/fred/types/geo/struct.GeoPosition.html new file mode 100644 index 00000000..c77657cf --- /dev/null +++ b/doc/fred/types/geo/struct.GeoPosition.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.GeoPosition.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/geo/struct.GeoRadiusInfo.html b/doc/fred/types/geo/struct.GeoRadiusInfo.html new file mode 100644 index 00000000..6eccb146 --- /dev/null +++ b/doc/fred/types/geo/struct.GeoRadiusInfo.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.GeoRadiusInfo.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/geo/struct.GeoValue.html b/doc/fred/types/geo/struct.GeoValue.html new file mode 100644 index 00000000..58619f9a --- /dev/null +++ b/doc/fred/types/geo/struct.GeoValue.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.GeoValue.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/geo/struct.MultipleGeoValues.html b/doc/fred/types/geo/struct.MultipleGeoValues.html new file mode 100644 index 00000000..40701385 --- /dev/null +++ b/doc/fred/types/geo/struct.MultipleGeoValues.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.MultipleGeoValues.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/index.html b/doc/fred/types/index.html new file mode 100644 index 00000000..69668d36 --- /dev/null +++ b/doc/fred/types/index.html @@ -0,0 +1,5 @@ +fred::types - Rust

Module fred::types

source ·
Expand description

The structs and enums used by the Redis client.

+

Structs§

  • Configuration options for backpressure features in the client.
  • A client and pool builder interface.
  • ClusterInfoi-cluster
    A parsed response from the CLUSTER INFO command.
  • The cached view of the cluster used by the client to route commands to the correct cluster nodes.
  • Configuration options related to the creation or management of TCP connection.
  • Configuration for custom redis commands, primarily used for interacting with third party modules or extensions.
  • The parsed result of the MEMORY STATS command for a specific database.
  • FtAggregateOptionsi-redisearch
    Arguments to the FT.AGGREGATE command.
  • FtAlterOptionsi-redisearch
    Arguments to FT.ALTER.
  • FtCreateOptionsi-redisearch
    Arguments for FT.CREATE.
  • FtSearchOptionsi-redisearch
    Arguments to FT.SEARCH.
  • Functioni-scripts
    An individual function within a Library.
  • A struct describing the longitude and latitude coordinates of a GEO command.
  • A typed struct representing the full output of the GEORADIUS (or similar) command.
  • A struct describing the value inside a GEO data structure.
  • GroupByi-time-series
    A struct representing GROUPBY label REDUCE reducer in commands such as TS.MRANGE.
  • The result of a HSCAN operation.
  • Invalidationi-client and i-tracking
    A client tracking invalidation message from the provided server.
  • An event on the publish-subscribe interface describing a keyspace notification.
  • Libraryi-scripts
    A helper struct for interacting with libraries and functions.
  • MemoryStatsi-memory
    The parsed result of the MEMORY STATS command.
  • A convenience struct for commands that take one or more GEO values.
  • A convenience struct for functions that take one or more hash slot values.
  • MultipleIDsi-streams
    One or more IDs for elements in a stream.
  • Convenience struct for commands that take 1 or more keys.
  • One or more ordered key-value pairs, typically used as an argument for XADD.
  • MultipleWeightsi-sorted-sets
    Convenience struct for ZINTERSTORE and ZUNIONSTORE when accepting 1 or more weights arguments.
  • MultipleZaddValuesi-sorted-sets
    Convenience struct for the ZADD command to accept 1 or more (score, value) arguments.
  • Options to configure or overwrite for individual commands.
  • Configuration options that can affect the performance of the client.
  • RangeAggregationi-time-series
    A struct representing [ALIGN align] AGGREGATION aggregator bucketDuration [BUCKETTIMESTAMP bt] [EMPTY] in +commands such as TS.MRANGE.
  • Configuration options for a RedisClient.
  • A key in Redis.
  • A map of (RedisKey, RedisValue) pairs.
  • Configuration options for replica node connections.
  • The result of a SSCAN operation.
  • The result of a SCAN operation.
  • Scripti-scripts
    An interface for caching and running lua scripts.
  • SearchFieldi-redisearch
    A search field with an optional property.
  • SearchFilteri-redisearch
    Arguments for FILTER in FT.SEARCH.
  • SearchGeoFilteri-redisearch
    Arguments for GEOFILTER in FT.SEARCH.
  • SearchHighlighti-redisearch
    Arguments used in HIGHLIGHT values.
  • SearchParameteri-redisearch
    Arguments for PARAMS in FT.AGGREGATE.
  • SearchReduceri-redisearch
    REDUCE arguments in FT.AGGREGATE.
  • SearchSchemai-redisearch
    Arguments for SCHEMA in FT.CREATE.
  • SearchSortByi-redisearch
    Arguments for SORTBY in FT.SEARCH.
  • SearchSummarizei-redisearch
    Arguments used in SUMMARIZE values.
  • SentinelConfigsentinel-client
    Configuration options for sentinel clients.
  • State necessary to identify or connect to a server.
  • A slot range and associated cluster node information from the CLUSTER SLOTS command.
  • The output of an entry in the slow queries log.
  • Statsmetrics
    Stats describing a distribution of samples.
  • TCP configuration options.
  • TlsConfigenable-rustls or enable-native-tls or enable-rustls-ring
    TLS configuration for a client.
  • TracingConfigpartial-tracing
    Configuration options for tracing.
  • Configuration options used to detect potentially unresponsive connections.
  • SemVer version as defined by https://semver.org.
  • WithCursori-redisearch
    Arguments for WITHCURSOR in FT.AGGREGATE.
  • XCapi-streams
    Stream cap arguments for XADD, XTRIM, etc.
  • XPendingArgsi-streams
    A struct representing the trailing optional arguments to XPENDING.
  • ZRangei-sorted-sets
    A wrapper struct for a range bound in a sorted set command.
  • The result of a ZSCAN operation.

Enums§

  • AggregateOperationi-redisearch
    An aggregation operation used in FT.AGGREGATE.
  • Aggregate options for the zinterstore (and related) commands.
  • Aggregatori-time-series
    An aggregation policy to use with certain timeseries commands.
  • Backpressure policies to apply when the max number of in-flight commands is reached on a connection.
  • Describes how the client should respond when a command is sent while the client is in a blocked state from a +blocking command.
  • BucketTimestampi-time-series
    A BUCKETTIMESTAMP argument in commands such as TS.MRANGE.
  • Filters provided to the CLIENT KILL command.
  • The type of clients to close.
  • Filters for the CLIENT PAUSE command.
  • Arguments for the CLIENT REPLY command.
  • The state of the underlying connection to the Redis server.
  • Arguments to the CLIENT UNBLOCK command.
  • A policy that determines how clustered clients initially connect to and discover other cluster nodes.
  • Options for the CLUSTER FAILOVER command.
  • A cluster hashing policy.
  • Flags for the CLUSTER RESET command.
  • Flags for the CLUSTER SETSLOT command.
  • ClusterStatei-cluster
    The state of the cluster from the CLUSTER INFO command.
  • An enum describing the possible ways in which a Redis cluster can change state.
  • DuplicatePolicyi-time-series
    The duplicate policy used with certain timeseries commands.
  • Encodingi-time-series
    Encoding arguments for certain timeseries commands.
  • Expiration options for the set command.
  • Options for certain expiration commands (PEXPIRE, etc).
  • The policy type for the FUNCTION RESTORE command.
  • FunctionFlagi-scripts
    Possible flags associated with a Function.
  • GeoUniti-geo
    Units for the GEO DIST command.
  • GetLabelsi-time-series
    Arguments equivalent to WITHLABELS | SELECTED_LABELS label... in various time series GET functions.
  • GetTimestampi-time-series
    A timestamp query used in commands such as TS.MRANGE.
  • IndexKindi-redisearch
    Index arguments for FT.CREATE.
  • Options for the info command.
  • The direction to move elements in a *LMOVE command.
  • Location flag for the LINSERT command.
  • Loadi-redisearch
    Arguments to LOAD in FT.AGGREGATE.
  • The kind of pubsub message.
  • Orderingi-sorted-sets
    Ordering options for the ZADD (and related) commands.
  • ReconnectErrorcustom-reconnect-errors
    Special errors that can trigger reconnection logic, which can also retry the failing command if possible.
  • The type of reconnection policy to use. This will apply to every connection used by the client.
  • A value used in a Redis command.
  • The kind of value from Redis.
  • Reduceri-time-series
    A REDUCER argument in commands such as TS.MRANGE.
  • ReducerFunci-redisearch
    GROUPBY reducer functions.
  • A RESP3 frame that uses Bytes and Str as the underlying buffer type.
  • The RESP version used in the HELLO request.
  • The types of values supported by the type command.
  • Flags for the SCRIPT DEBUG command.
  • SearchSchemaKindi-redisearch
    One of the available schema types used with FT.CREATE or FT.ALTER.
  • SentinelFailureKindsentinel-client
    Arguments for the SENTINEL SIMULATE-FAILURE command.
  • Connection configuration for the Redis server.
  • Options for the set command.
  • Arguments passed to the SHUTDOWN command.
  • The sort order for redis commands that take or return a sorted list.
  • SpellcheckTermsi-redisearch
    Arguments to TERMS in FT.SPELLCHECK,
  • An argument representing a string or number.
  • Timestampi-time-series
    A timestamp used in most timeseries commands.
  • TlsConnectorenable-rustls or enable-native-tls or enable-rustls-ring
    An enum for interacting with various TLS libraries and interfaces.
  • TlsHostMappingenable-rustls or enable-native-tls or enable-rustls-ring
    An optional enum used to describe how the client should modify or map IP addresses and hostnames in a clustered +deployment.
  • Togglei-client and i-tracking
    An ON|OFF flag used with client tracking commands.
  • XCapKindi-streams
    The MAXLEN or MINID argument for a stream cap.
  • XCapTrimi-streams
    Representation for the “=” or “~” operator in XADD, etc.
  • XIDi-streams
    Stream ID arguments for XADD, XREAD, etc.
  • ZCmpi-sorted-sets
    MIN|MAX arguments for BZMPOP, etc.
  • ZRangeBoundi-sorted-sets
    An index, score, lexicographical, or +|-|+inf|-inf range bound for the ZRANGE command.
  • ZRangeKindi-sorted-sets
    The type of range interval bound.
  • ZSorti-sorted-sets
    Options for the ZRANGE (and related) commands.

Constants§

Traits§

  • A trait used to convert various forms of RedisValue into different types.
  • A trait used to convert RedisKey values to various types.
  • HostMappingenable-rustls or enable-native-tls or enable-rustls-ring
    A trait used for mapping IP addresses to hostnames when processing the CLUSTER SLOTS response.
  • An interface used to filter the list of available replica nodes.
  • A trait that can be used to override DNS resolution logic.
  • An interface for interacting with the results of a scan operation.

Type Aliases§

  • The ANY flag used on certain GEO commands.
  • The result from any of the connect functions showing the error that closed the connection, if any.
  • A tuple of (offset, count) values for commands that allow paging through results.
  • An argument type equivalent to “[LIMIT count]”.
  • Convenience interface for commands that take 1 or more strings.
  • Convenience interface for commands that take 1 or more values.
  • Shorthand for the result of commands such as MGET, MRANGE, etc.
  • The RESP3 equivalent of Resp2TimeSeriesValues.
  • XReadResponsei-streams
    A generic helper type describing the top level response from XREAD or XREADGROUP.
  • XReadValuei-streams
    A generic helper type describing the ID and associated map for each record in a stream.
\ No newline at end of file diff --git a/doc/fred/types/lists/enum.LMoveDirection.html b/doc/fred/types/lists/enum.LMoveDirection.html new file mode 100644 index 00000000..d896c077 --- /dev/null +++ b/doc/fred/types/lists/enum.LMoveDirection.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.LMoveDirection.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/lists/enum.ListLocation.html b/doc/fred/types/lists/enum.ListLocation.html new file mode 100644 index 00000000..3cbe53e2 --- /dev/null +++ b/doc/fred/types/lists/enum.ListLocation.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ListLocation.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/enum.AggregateOptions.html b/doc/fred/types/misc/enum.AggregateOptions.html new file mode 100644 index 00000000..e7816b6e --- /dev/null +++ b/doc/fred/types/misc/enum.AggregateOptions.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.AggregateOptions.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/enum.ClientState.html b/doc/fred/types/misc/enum.ClientState.html new file mode 100644 index 00000000..d2e78d51 --- /dev/null +++ b/doc/fred/types/misc/enum.ClientState.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ClientState.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/enum.ClientUnblockFlag.html b/doc/fred/types/misc/enum.ClientUnblockFlag.html new file mode 100644 index 00000000..f0d96d74 --- /dev/null +++ b/doc/fred/types/misc/enum.ClientUnblockFlag.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ClientUnblockFlag.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/enum.ClusterStateChange.html b/doc/fred/types/misc/enum.ClusterStateChange.html new file mode 100644 index 00000000..684f9830 --- /dev/null +++ b/doc/fred/types/misc/enum.ClusterStateChange.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ClusterStateChange.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/enum.Expiration.html b/doc/fred/types/misc/enum.Expiration.html new file mode 100644 index 00000000..8fa61b1f --- /dev/null +++ b/doc/fred/types/misc/enum.Expiration.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.Expiration.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/enum.ExpireOptions.html b/doc/fred/types/misc/enum.ExpireOptions.html new file mode 100644 index 00000000..441667d2 --- /dev/null +++ b/doc/fred/types/misc/enum.ExpireOptions.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ExpireOptions.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/enum.FnPolicy.html b/doc/fred/types/misc/enum.FnPolicy.html new file mode 100644 index 00000000..dc6521ab --- /dev/null +++ b/doc/fred/types/misc/enum.FnPolicy.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.FnPolicy.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/enum.InfoKind.html b/doc/fred/types/misc/enum.InfoKind.html new file mode 100644 index 00000000..62dc9469 --- /dev/null +++ b/doc/fred/types/misc/enum.InfoKind.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.InfoKind.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/enum.ScriptDebugFlag.html b/doc/fred/types/misc/enum.ScriptDebugFlag.html new file mode 100644 index 00000000..6974c17d --- /dev/null +++ b/doc/fred/types/misc/enum.ScriptDebugFlag.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ScriptDebugFlag.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/enum.SentinelFailureKind.html b/doc/fred/types/misc/enum.SentinelFailureKind.html new file mode 100644 index 00000000..a373efe7 --- /dev/null +++ b/doc/fred/types/misc/enum.SentinelFailureKind.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.SentinelFailureKind.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/enum.SetOptions.html b/doc/fred/types/misc/enum.SetOptions.html new file mode 100644 index 00000000..3ec35dd5 --- /dev/null +++ b/doc/fred/types/misc/enum.SetOptions.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.SetOptions.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/enum.ShutdownFlags.html b/doc/fred/types/misc/enum.ShutdownFlags.html new file mode 100644 index 00000000..986fda45 --- /dev/null +++ b/doc/fred/types/misc/enum.ShutdownFlags.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ShutdownFlags.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/enum.SortOrder.html b/doc/fred/types/misc/enum.SortOrder.html new file mode 100644 index 00000000..ff99f0eb --- /dev/null +++ b/doc/fred/types/misc/enum.SortOrder.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.SortOrder.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/struct.CustomCommand.html b/doc/fred/types/misc/struct.CustomCommand.html new file mode 100644 index 00000000..1fc783bf --- /dev/null +++ b/doc/fred/types/misc/struct.CustomCommand.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.CustomCommand.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/struct.DatabaseMemoryStats.html b/doc/fred/types/misc/struct.DatabaseMemoryStats.html new file mode 100644 index 00000000..7b2e9551 --- /dev/null +++ b/doc/fred/types/misc/struct.DatabaseMemoryStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.DatabaseMemoryStats.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/struct.KeyspaceEvent.html b/doc/fred/types/misc/struct.KeyspaceEvent.html new file mode 100644 index 00000000..4fad781a --- /dev/null +++ b/doc/fred/types/misc/struct.KeyspaceEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.KeyspaceEvent.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/struct.MemoryStats.html b/doc/fred/types/misc/struct.MemoryStats.html new file mode 100644 index 00000000..db277ca1 --- /dev/null +++ b/doc/fred/types/misc/struct.MemoryStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.MemoryStats.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/misc/struct.SlowlogEntry.html b/doc/fred/types/misc/struct.SlowlogEntry.html new file mode 100644 index 00000000..579139c0 --- /dev/null +++ b/doc/fred/types/misc/struct.SlowlogEntry.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.SlowlogEntry.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/multiple/struct.MultipleHashSlots.html b/doc/fred/types/multiple/struct.MultipleHashSlots.html new file mode 100644 index 00000000..bcdc195b --- /dev/null +++ b/doc/fred/types/multiple/struct.MultipleHashSlots.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.MultipleHashSlots.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/multiple/struct.MultipleKeys.html b/doc/fred/types/multiple/struct.MultipleKeys.html new file mode 100644 index 00000000..c1006ebe --- /dev/null +++ b/doc/fred/types/multiple/struct.MultipleKeys.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.MultipleKeys.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/multiple/type.MultipleStrings.html b/doc/fred/types/multiple/type.MultipleStrings.html new file mode 100644 index 00000000..8ff15c3d --- /dev/null +++ b/doc/fred/types/multiple/type.MultipleStrings.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/type.MultipleStrings.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/multiple/type.MultipleValues.html b/doc/fred/types/multiple/type.MultipleValues.html new file mode 100644 index 00000000..a22c5726 --- /dev/null +++ b/doc/fred/types/multiple/type.MultipleValues.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/type.MultipleValues.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/enum.AggregateOperation.html b/doc/fred/types/redisearch/enum.AggregateOperation.html new file mode 100644 index 00000000..fadb6762 --- /dev/null +++ b/doc/fred/types/redisearch/enum.AggregateOperation.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.AggregateOperation.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/enum.IndexKind.html b/doc/fred/types/redisearch/enum.IndexKind.html new file mode 100644 index 00000000..1a7b51ae --- /dev/null +++ b/doc/fred/types/redisearch/enum.IndexKind.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.IndexKind.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/enum.Load.html b/doc/fred/types/redisearch/enum.Load.html new file mode 100644 index 00000000..42b9bf73 --- /dev/null +++ b/doc/fred/types/redisearch/enum.Load.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.Load.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/enum.ReducerFunc.html b/doc/fred/types/redisearch/enum.ReducerFunc.html new file mode 100644 index 00000000..a35e9a98 --- /dev/null +++ b/doc/fred/types/redisearch/enum.ReducerFunc.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ReducerFunc.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/enum.SearchSchemaKind.html b/doc/fred/types/redisearch/enum.SearchSchemaKind.html new file mode 100644 index 00000000..351c1231 --- /dev/null +++ b/doc/fred/types/redisearch/enum.SearchSchemaKind.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.SearchSchemaKind.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/enum.SpellcheckTerms.html b/doc/fred/types/redisearch/enum.SpellcheckTerms.html new file mode 100644 index 00000000..18b4c4d3 --- /dev/null +++ b/doc/fred/types/redisearch/enum.SpellcheckTerms.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.SpellcheckTerms.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.FtAggregateOptions.html b/doc/fred/types/redisearch/struct.FtAggregateOptions.html new file mode 100644 index 00000000..78639d47 --- /dev/null +++ b/doc/fred/types/redisearch/struct.FtAggregateOptions.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.FtAggregateOptions.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.FtAlterOptions.html b/doc/fred/types/redisearch/struct.FtAlterOptions.html new file mode 100644 index 00000000..30680cc0 --- /dev/null +++ b/doc/fred/types/redisearch/struct.FtAlterOptions.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.FtAlterOptions.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.FtCreateOptions.html b/doc/fred/types/redisearch/struct.FtCreateOptions.html new file mode 100644 index 00000000..aa61910c --- /dev/null +++ b/doc/fred/types/redisearch/struct.FtCreateOptions.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.FtCreateOptions.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.FtSearchOptions.html b/doc/fred/types/redisearch/struct.FtSearchOptions.html new file mode 100644 index 00000000..fd41af97 --- /dev/null +++ b/doc/fred/types/redisearch/struct.FtSearchOptions.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.FtSearchOptions.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.SearchField.html b/doc/fred/types/redisearch/struct.SearchField.html new file mode 100644 index 00000000..82ea548f --- /dev/null +++ b/doc/fred/types/redisearch/struct.SearchField.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.SearchField.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.SearchFilter.html b/doc/fred/types/redisearch/struct.SearchFilter.html new file mode 100644 index 00000000..c0bcae05 --- /dev/null +++ b/doc/fred/types/redisearch/struct.SearchFilter.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.SearchFilter.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.SearchGeoFilter.html b/doc/fred/types/redisearch/struct.SearchGeoFilter.html new file mode 100644 index 00000000..c5c1dd79 --- /dev/null +++ b/doc/fred/types/redisearch/struct.SearchGeoFilter.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.SearchGeoFilter.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.SearchHighlight.html b/doc/fred/types/redisearch/struct.SearchHighlight.html new file mode 100644 index 00000000..10c39697 --- /dev/null +++ b/doc/fred/types/redisearch/struct.SearchHighlight.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.SearchHighlight.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.SearchParameter.html b/doc/fred/types/redisearch/struct.SearchParameter.html new file mode 100644 index 00000000..b45c78b8 --- /dev/null +++ b/doc/fred/types/redisearch/struct.SearchParameter.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.SearchParameter.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.SearchReducer.html b/doc/fred/types/redisearch/struct.SearchReducer.html new file mode 100644 index 00000000..9114a570 --- /dev/null +++ b/doc/fred/types/redisearch/struct.SearchReducer.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.SearchReducer.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.SearchSchema.html b/doc/fred/types/redisearch/struct.SearchSchema.html new file mode 100644 index 00000000..b213ea12 --- /dev/null +++ b/doc/fred/types/redisearch/struct.SearchSchema.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.SearchSchema.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.SearchSortBy.html b/doc/fred/types/redisearch/struct.SearchSortBy.html new file mode 100644 index 00000000..78982d15 --- /dev/null +++ b/doc/fred/types/redisearch/struct.SearchSortBy.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.SearchSortBy.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.SearchSummarize.html b/doc/fred/types/redisearch/struct.SearchSummarize.html new file mode 100644 index 00000000..c2554c62 --- /dev/null +++ b/doc/fred/types/redisearch/struct.SearchSummarize.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.SearchSummarize.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/redisearch/struct.WithCursor.html b/doc/fred/types/redisearch/struct.WithCursor.html new file mode 100644 index 00000000..3b0a8f9a --- /dev/null +++ b/doc/fred/types/redisearch/struct.WithCursor.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.WithCursor.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/scan/enum.ScanType.html b/doc/fred/types/scan/enum.ScanType.html new file mode 100644 index 00000000..dfd08755 --- /dev/null +++ b/doc/fred/types/scan/enum.ScanType.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ScanType.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/scan/struct.HScanResult.html b/doc/fred/types/scan/struct.HScanResult.html new file mode 100644 index 00000000..e3acd044 --- /dev/null +++ b/doc/fred/types/scan/struct.HScanResult.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.HScanResult.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/scan/struct.SScanResult.html b/doc/fred/types/scan/struct.SScanResult.html new file mode 100644 index 00000000..05f02710 --- /dev/null +++ b/doc/fred/types/scan/struct.SScanResult.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.SScanResult.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/scan/struct.ScanResult.html b/doc/fred/types/scan/struct.ScanResult.html new file mode 100644 index 00000000..7e024863 --- /dev/null +++ b/doc/fred/types/scan/struct.ScanResult.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.ScanResult.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/scan/struct.ZScanResult.html b/doc/fred/types/scan/struct.ZScanResult.html new file mode 100644 index 00000000..3298c649 --- /dev/null +++ b/doc/fred/types/scan/struct.ZScanResult.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.ZScanResult.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/scan/trait.Scanner.html b/doc/fred/types/scan/trait.Scanner.html new file mode 100644 index 00000000..0f9aea23 --- /dev/null +++ b/doc/fred/types/scan/trait.Scanner.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/trait.Scanner.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/scripts/enum.FunctionFlag.html b/doc/fred/types/scripts/enum.FunctionFlag.html new file mode 100644 index 00000000..3e51ff9f --- /dev/null +++ b/doc/fred/types/scripts/enum.FunctionFlag.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.FunctionFlag.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/scripts/struct.Function.html b/doc/fred/types/scripts/struct.Function.html new file mode 100644 index 00000000..b7dc123e --- /dev/null +++ b/doc/fred/types/scripts/struct.Function.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.Function.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/scripts/struct.Library.html b/doc/fred/types/scripts/struct.Library.html new file mode 100644 index 00000000..9c450e48 --- /dev/null +++ b/doc/fred/types/scripts/struct.Library.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.Library.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/scripts/struct.Script.html b/doc/fred/types/scripts/struct.Script.html new file mode 100644 index 00000000..e30b86eb --- /dev/null +++ b/doc/fred/types/scripts/struct.Script.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.Script.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/sidebar-items.js b/doc/fred/types/sidebar-items.js new file mode 100644 index 00000000..a657aa67 --- /dev/null +++ b/doc/fred/types/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"constant":["DEFAULT_JITTER_MS"],"enum":["AggregateOperation","AggregateOptions","Aggregator","BackpressurePolicy","Blocking","BucketTimestamp","ClientKillFilter","ClientKillType","ClientPauseKind","ClientReplyFlag","ClientState","ClientUnblockFlag","ClusterDiscoveryPolicy","ClusterFailoverFlag","ClusterHash","ClusterResetFlag","ClusterSetSlotState","ClusterState","ClusterStateChange","DuplicatePolicy","Encoding","Expiration","ExpireOptions","FnPolicy","FunctionFlag","GeoUnit","GetLabels","GetTimestamp","IndexKind","InfoKind","LMoveDirection","ListLocation","Load","MessageKind","Ordering","ReconnectError","ReconnectPolicy","RedisValue","RedisValueKind","Reducer","ReducerFunc","Resp3Frame","RespVersion","ScanType","ScriptDebugFlag","SearchSchemaKind","SentinelFailureKind","ServerConfig","SetOptions","ShutdownFlags","SortOrder","SpellcheckTerms","StringOrNumber","Timestamp","TlsConnector","TlsHostMapping","Toggle","XCapKind","XCapTrim","XID","ZCmp","ZRangeBound","ZRangeKind","ZSort"],"struct":["BackpressureConfig","Builder","ClusterInfo","ClusterRouting","ConnectionConfig","CustomCommand","DatabaseMemoryStats","FtAggregateOptions","FtAlterOptions","FtCreateOptions","FtSearchOptions","Function","GeoPosition","GeoRadiusInfo","GeoValue","GroupBy","HScanResult","Invalidation","KeyspaceEvent","Library","MemoryStats","Message","MultipleGeoValues","MultipleHashSlots","MultipleIDs","MultipleKeys","MultipleOrderedPairs","MultipleWeights","MultipleZaddValues","Options","PerformanceConfig","RangeAggregation","RedisConfig","RedisKey","RedisMap","ReplicaConfig","SScanResult","ScanResult","Script","SearchField","SearchFilter","SearchGeoFilter","SearchHighlight","SearchParameter","SearchReducer","SearchSchema","SearchSortBy","SearchSummarize","SentinelConfig","Server","SlotRange","SlowlogEntry","Stats","TcpConfig","TlsConfig","TracingConfig","UnresponsiveConfig","Version","WithCursor","XCap","XPendingArgs","ZRange","ZScanResult"],"trait":["FromRedis","FromRedisKey","HostMapping","ReplicaFilter","Resolve","Scanner"],"type":["Any","ConnectHandle","Limit","LimitCount","MultipleStrings","MultipleValues","Resp2TimeSeriesValues","Resp3TimeSeriesValues","XReadResponse","XReadValue"]}; \ No newline at end of file diff --git a/doc/fred/types/sorted_sets/enum.Ordering.html b/doc/fred/types/sorted_sets/enum.Ordering.html new file mode 100644 index 00000000..7630374b --- /dev/null +++ b/doc/fred/types/sorted_sets/enum.Ordering.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.Ordering.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/sorted_sets/enum.ZCmp.html b/doc/fred/types/sorted_sets/enum.ZCmp.html new file mode 100644 index 00000000..f7df7f1b --- /dev/null +++ b/doc/fred/types/sorted_sets/enum.ZCmp.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ZCmp.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/sorted_sets/enum.ZRangeBound.html b/doc/fred/types/sorted_sets/enum.ZRangeBound.html new file mode 100644 index 00000000..6afddae7 --- /dev/null +++ b/doc/fred/types/sorted_sets/enum.ZRangeBound.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ZRangeBound.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/sorted_sets/enum.ZRangeKind.html b/doc/fred/types/sorted_sets/enum.ZRangeKind.html new file mode 100644 index 00000000..8402070d --- /dev/null +++ b/doc/fred/types/sorted_sets/enum.ZRangeKind.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ZRangeKind.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/sorted_sets/enum.ZSort.html b/doc/fred/types/sorted_sets/enum.ZSort.html new file mode 100644 index 00000000..25496c33 --- /dev/null +++ b/doc/fred/types/sorted_sets/enum.ZSort.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.ZSort.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/sorted_sets/struct.MultipleWeights.html b/doc/fred/types/sorted_sets/struct.MultipleWeights.html new file mode 100644 index 00000000..e2385fbf --- /dev/null +++ b/doc/fred/types/sorted_sets/struct.MultipleWeights.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.MultipleWeights.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/sorted_sets/struct.MultipleZaddValues.html b/doc/fred/types/sorted_sets/struct.MultipleZaddValues.html new file mode 100644 index 00000000..ba2ffad3 --- /dev/null +++ b/doc/fred/types/sorted_sets/struct.MultipleZaddValues.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.MultipleZaddValues.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/sorted_sets/struct.ZRange.html b/doc/fred/types/sorted_sets/struct.ZRange.html new file mode 100644 index 00000000..fc819f19 --- /dev/null +++ b/doc/fred/types/sorted_sets/struct.ZRange.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.ZRange.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/streams/enum.XCapKind.html b/doc/fred/types/streams/enum.XCapKind.html new file mode 100644 index 00000000..ae56d10d --- /dev/null +++ b/doc/fred/types/streams/enum.XCapKind.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.XCapKind.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/streams/enum.XCapTrim.html b/doc/fred/types/streams/enum.XCapTrim.html new file mode 100644 index 00000000..32833c51 --- /dev/null +++ b/doc/fred/types/streams/enum.XCapTrim.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.XCapTrim.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/streams/enum.XID.html b/doc/fred/types/streams/enum.XID.html new file mode 100644 index 00000000..3ac1e3e4 --- /dev/null +++ b/doc/fred/types/streams/enum.XID.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.XID.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/streams/struct.MultipleIDs.html b/doc/fred/types/streams/struct.MultipleIDs.html new file mode 100644 index 00000000..ab9cce6e --- /dev/null +++ b/doc/fred/types/streams/struct.MultipleIDs.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.MultipleIDs.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/streams/struct.MultipleOrderedPairs.html b/doc/fred/types/streams/struct.MultipleOrderedPairs.html new file mode 100644 index 00000000..c44a1997 --- /dev/null +++ b/doc/fred/types/streams/struct.MultipleOrderedPairs.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.MultipleOrderedPairs.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/streams/struct.XCap.html b/doc/fred/types/streams/struct.XCap.html new file mode 100644 index 00000000..c6b1bdba --- /dev/null +++ b/doc/fred/types/streams/struct.XCap.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.XCap.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/streams/struct.XPendingArgs.html b/doc/fred/types/streams/struct.XPendingArgs.html new file mode 100644 index 00000000..bf333acf --- /dev/null +++ b/doc/fred/types/streams/struct.XPendingArgs.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.XPendingArgs.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/streams/type.XReadResponse.html b/doc/fred/types/streams/type.XReadResponse.html new file mode 100644 index 00000000..0d3b4797 --- /dev/null +++ b/doc/fred/types/streams/type.XReadResponse.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/type.XReadResponse.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/streams/type.XReadValue.html b/doc/fred/types/streams/type.XReadValue.html new file mode 100644 index 00000000..60449c18 --- /dev/null +++ b/doc/fred/types/streams/type.XReadValue.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/type.XReadValue.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/struct.BackpressureConfig.html b/doc/fred/types/struct.BackpressureConfig.html new file mode 100644 index 00000000..727fc6f5 --- /dev/null +++ b/doc/fred/types/struct.BackpressureConfig.html @@ -0,0 +1,45 @@ +BackpressureConfig in fred::types - Rust

Struct fred::types::BackpressureConfig

source ·
pub struct BackpressureConfig {
+    pub disable_auto_backpressure: bool,
+    pub max_in_flight_commands: u64,
+    pub policy: BackpressurePolicy,
+}
Expand description

Configuration options for backpressure features in the client.

+

Fields§

§disable_auto_backpressure: bool

Whether to disable the automatic backpressure features when pipelining is enabled.

+

If true then RedisErrorKind::Backpressure errors may be surfaced to callers. Callers can set this to true +and max_in_flight_commands to 0 to effectively disable the backpressure logic.

+

Default: false

+
§max_in_flight_commands: u64

The maximum number of in-flight commands (per connection) before backpressure will be applied.

+

Default: 10_000

+
§policy: BackpressurePolicy

The backpressure policy to apply when the max number of in-flight commands is reached.

+

Default: Drain.

+

Trait Implementations§

source§

impl Clone for BackpressureConfig

source§

fn clone(&self) -> BackpressureConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for BackpressureConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for BackpressureConfig

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for BackpressureConfig

source§

fn eq(&self, other: &BackpressureConfig) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for BackpressureConfig

source§

impl StructuralPartialEq for BackpressureConfig

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.Builder.html b/doc/fred/types/struct.Builder.html new file mode 100644 index 00000000..70c5b22c --- /dev/null +++ b/doc/fred/types/struct.Builder.html @@ -0,0 +1,96 @@ +Builder in fred::types - Rust

Struct fred::types::Builder

source ·
pub struct Builder { /* private fields */ }
Expand description

A client and pool builder interface.

+ +
fn example() -> Result<(), RedisError> {
+  // use default values
+  let client = Builder::default_centralized().build()?;
+
+  // or initialize from a URL or config
+  let config = RedisConfig::from_url("redis://localhost:6379/1")?;
+  let mut builder = Builder::from_config(config);
+  // or modify values in place (creating defaults if needed)
+  builder
+    .with_performance_config(|config| {
+      config.auto_pipeline = true;
+    })
+    .with_config(|config| {
+      config.version = RespVersion::RESP3;
+      config.fail_fast = true;
+    })
+    .with_connection_config(|config| {
+      config.tcp = TcpConfig {
+        nodelay: Some(true),
+        ..Default::default()
+      };
+      config.internal_command_timeout = Duration::from_secs(10);
+    });
+  // or overwrite configuration structs in place
+  builder.set_policy(ReconnectPolicy::new_exponential(0, 100, 30_000, 2));
+  builder.set_performance_config(PerformanceConfig::default());
+
+  // reuse the builder as needed to create any kind of client
+  let client = builder.build()?;
+  let pool = builder.build_pool(3)?;
+  let subscriber = builder.build_subscriber_client()?;
+
+  // ...
+
+  Ok(())
+}
+

Implementations§

source§

impl Builder

source

pub fn default_centralized() -> Self

Create a new builder instance with default config values for a centralized deployment.

+
source

pub fn default_clustered() -> Self

Create a new builder instance with default config values for a clustered deployment.

+
source

pub fn from_config(config: RedisConfig) -> Self

Create a new builder instance from the provided client config.

+
source

pub fn get_config(&self) -> Option<&RedisConfig>

Read the client config.

+
source

pub fn get_policy(&self) -> Option<&ReconnectPolicy>

Read the reconnection policy.

+
source

pub fn get_performance_config(&self) -> &PerformanceConfig

Read the performance config.

+
source

pub fn get_connection_config(&self) -> &ConnectionConfig

Read the connection config.

+
source

pub fn get_sentinel_config(&self) -> Option<&RedisConfig>

Available on crate feature sentinel-client only.

Read the sentinel client config.

+
source

pub fn set_config(&mut self, config: RedisConfig) -> &mut Self

Overwrite the client config on the builder.

+
source

pub fn set_policy(&mut self, policy: ReconnectPolicy) -> &mut Self

Overwrite the reconnection policy on the builder.

+
source

pub fn set_performance_config(&mut self, config: PerformanceConfig) -> &mut Self

Overwrite the performance config on the builder.

+
source

pub fn set_connection_config(&mut self, config: ConnectionConfig) -> &mut Self

Overwrite the connection config on the builder.

+
source

pub fn set_sentinel_config(&mut self, config: SentinelConfig) -> &mut Self

Available on crate feature sentinel-client only.

Overwrite the sentinel config on the builder.

+
source

pub fn with_config<F>(&mut self, func: F) -> &mut Self
where + F: FnOnce(&mut RedisConfig),

Modify the client config in place, creating a new one with default centralized values first if needed.

+
source

pub fn with_performance_config<F>(&mut self, func: F) -> &mut Self
where + F: FnOnce(&mut PerformanceConfig),

Modify the performance config in place, creating a new one with default values first if needed.

+
source

pub fn with_connection_config<F>(&mut self, func: F) -> &mut Self
where + F: FnOnce(&mut ConnectionConfig),

Modify the connection config in place, creating a new one with default values first if needed.

+
source

pub fn with_sentinel_config<F>(&mut self, func: F) -> &mut Self
where + F: FnOnce(&mut SentinelConfig),

Available on crate feature sentinel-client only.

Modify the sentinel config in place, creating a new one with default values first if needed.

+
source

pub fn build(&self) -> Result<RedisClient, RedisError>

Create a new client.

+
source

pub fn build_pool(&self, size: usize) -> Result<RedisPool, RedisError>

Create a new client pool.

+
source

pub fn build_subscriber_client(&self) -> Result<SubscriberClient, RedisError>

Available on crate feature subscriber-client only.

Create a new subscriber client.

+
source

pub fn build_sentinel_client(&self) -> Result<SentinelClient, RedisError>

Available on crate feature sentinel-client only.

Create a new sentinel client.

+

This is only necessary if callers need to communicate directly with sentinel nodes. Use a +ServerConfig::Sentinel to interact with Redis servers behind a sentinel layer.

+

Trait Implementations§

source§

impl Clone for Builder

source§

fn clone(&self) -> Builder

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Builder

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Builder

source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl !Freeze for Builder

§

impl !RefUnwindSafe for Builder

§

impl !Send for Builder

§

impl !Sync for Builder

§

impl Unpin for Builder

§

impl !UnwindSafe for Builder

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.ClusterInfo.html b/doc/fred/types/struct.ClusterInfo.html new file mode 100644 index 00000000..6e8bc886 --- /dev/null +++ b/doc/fred/types/struct.ClusterInfo.html @@ -0,0 +1,46 @@ +ClusterInfo in fred::types - Rust

Struct fred::types::ClusterInfo

source ·
pub struct ClusterInfo {
+    pub cluster_state: ClusterState,
+    pub cluster_slots_assigned: u16,
+    pub cluster_slots_ok: u16,
+    pub cluster_slots_pfail: u16,
+    pub cluster_slots_fail: u16,
+    pub cluster_known_nodes: u16,
+    pub cluster_size: u32,
+    pub cluster_current_epoch: u64,
+    pub cluster_my_epoch: u64,
+    pub cluster_stats_messages_sent: u64,
+    pub cluster_stats_messages_received: u64,
+}
Available on crate feature i-cluster only.
Expand description

A parsed response from the CLUSTER INFO command.

+

https://redis.io/commands/cluster-info

+

Fields§

§cluster_state: ClusterState§cluster_slots_assigned: u16§cluster_slots_ok: u16§cluster_slots_pfail: u16§cluster_slots_fail: u16§cluster_known_nodes: u16§cluster_size: u32§cluster_current_epoch: u64§cluster_my_epoch: u64§cluster_stats_messages_sent: u64§cluster_stats_messages_received: u64

Trait Implementations§

source§

impl Clone for ClusterInfo

source§

fn clone(&self) -> ClusterInfo

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClusterInfo

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for ClusterInfo

source§

fn default() -> ClusterInfo

Returns the “default value” for a type. Read more
source§

impl FromRedis for ClusterInfo

source§

impl PartialEq for ClusterInfo

source§

fn eq(&self, other: &ClusterInfo) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl TryFrom<RedisValue> for ClusterInfo

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for ClusterInfo

source§

impl StructuralPartialEq for ClusterInfo

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.ClusterRouting.html b/doc/fred/types/struct.ClusterRouting.html new file mode 100644 index 00000000..55c3a5b0 --- /dev/null +++ b/doc/fred/types/struct.ClusterRouting.html @@ -0,0 +1,48 @@ +ClusterRouting in fred::types - Rust

Struct fred::types::ClusterRouting

source ·
pub struct ClusterRouting { /* private fields */ }
Expand description

The cached view of the cluster used by the client to route commands to the correct cluster nodes.

+

Implementations§

source§

impl ClusterRouting

source

pub fn new() -> Self

Create a new empty routing table.

+
source

pub fn from_cluster_slots<S: Into<Str>>( + value: RedisValue, + default_host: S, +) -> Result<Self, RedisError>

Create a new routing table from the result of the CLUSTER SLOTS command.

+

The default_host value refers to the server that provided the response.

+
source

pub fn unique_hash_slots(&self) -> Vec<u16>

Read a set of unique hash slots that each map to a different primary/main node in the cluster.

+
source

pub fn unique_primary_nodes(&self) -> Vec<Server>

Read the set of unique primary nodes in the cluster.

+
source

pub fn hash_key(key: &[u8]) -> u16

Calculate the cluster hash slot for the provided key.

+
source

pub fn get_server(&self, slot: u16) -> Option<&Server>

Find the primary server that owns the provided hash slot.

+
source

pub fn replicas(&self, primary: &Server) -> Vec<Server>

Available on crate feature replicas only.

Read the replicas associated with the provided primary node based on the cached CLUSTER SLOTS response.

+
source

pub fn len(&self) -> usize

Read the number of hash slot ranges in the cluster.

+
source

pub fn slots(&self) -> &[SlotRange]

Read the hash slot ranges in the cluster.

+
source

pub fn random_slot(&self) -> Option<&SlotRange>

Read a random primary node hash slot range from the cluster cache.

+
source

pub fn random_node(&self) -> Option<&Server>

Read a random primary node from the cluster cache.

+
source

pub fn pretty(&self) -> BTreeMap<Server, (Vec<(u16, u16)>, BTreeSet<Server>)>

Print the contents of the routing table as a human-readable map.

+

Trait Implementations§

source§

impl Clone for ClusterRouting

source§

fn clone(&self) -> ClusterRouting

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ClusterRouting

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.ConnectionConfig.html b/doc/fred/types/struct.ConnectionConfig.html new file mode 100644 index 00000000..a527ad8a --- /dev/null +++ b/doc/fred/types/struct.ConnectionConfig.html @@ -0,0 +1,80 @@ +ConnectionConfig in fred::types - Rust

Struct fred::types::ConnectionConfig

source ·
pub struct ConnectionConfig {
Show 15 fields + pub connection_timeout: Duration, + pub internal_command_timeout: Duration, + pub cluster_cache_update_delay: Duration, + pub max_command_attempts: u32, + pub max_redirections: u32, + pub unresponsive: UnresponsiveConfig, + pub reconnect_on_auth_error: bool, + pub auto_client_setname: bool, + pub max_command_buffer_len: usize, + pub disable_cluster_health_check: bool, + pub replica: ReplicaConfig, + pub tcp: TcpConfig, + pub reconnect_errors: Vec<ReconnectError>, + pub router_task_queue: Option<TaskQueueHandle>, + pub connection_task_queue: Option<TaskQueueHandle>, +
}
Expand description

Configuration options related to the creation or management of TCP connection.

+

Fields§

§connection_timeout: Duration

The timeout to apply when attempting to create a new TCP connection.

+

This also includes the TLS handshake if using any of the TLS features.

+

Default: 10 sec

+
§internal_command_timeout: Duration

The timeout to apply when sending internal commands such as AUTH, SELECT, CLUSTER SLOTS, READONLY, etc.

+

Default: 10 sec

+
§cluster_cache_update_delay: Duration

The amount of time to wait after a MOVED error is received before the client will update the cached cluster +state.

+

Default: 0

+
§max_command_attempts: u32

The maximum number of times the client will attempt to send a command.

+

This value be incremented whenever the connection closes while the command is in-flight.

+

Default: 3

+
§max_redirections: u32

The maximum number of times the client will attempt to follow a MOVED or ASK redirection per command.

+

Default: 5

+
§unresponsive: UnresponsiveConfig

Unresponsive connection configuration options.

+
§reconnect_on_auth_error: bool

An unexpected NOAUTH error is treated the same as a general connection failure, causing the client to +reconnect based on the ReconnectPolicy. This is recommended if callers are using ElastiCache.

+

Default: false

+
§auto_client_setname: bool

Automatically send CLIENT SETNAME on each connection associated with a client instance.

+

Default: false

+
§max_command_buffer_len: usize

Limit the size of the internal in-memory command queue.

+

Commands that exceed this limit will receive a RedisErrorKind::Backpressure error.

+

See command_queue_len for more information.

+

Default: 0 (unlimited)

+
§disable_cluster_health_check: bool

Disable the CLUSTER INFO health check when initializing cluster connections.

+

Default: false

+
§replica: ReplicaConfig
Available on crate feature replicas only.

Configuration options for replica nodes.

+

Default: None

+
§tcp: TcpConfig

TCP connection options.

+
§reconnect_errors: Vec<ReconnectError>
Available on crate feature custom-reconnect-errors only.

Errors that should trigger reconnection logic.

+
§router_task_queue: Option<TaskQueueHandle>
Available on crate feature glommio only.

The task queue onto which routing tasks will be spawned.

+
§connection_task_queue: Option<TaskQueueHandle>
Available on crate feature glommio only.

The task queue onto which connection reader tasks will be spawned.

+

Trait Implementations§

source§

impl Clone for ConnectionConfig

source§

fn clone(&self) -> ConnectionConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ConnectionConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for ConnectionConfig

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for ConnectionConfig

source§

fn eq(&self, other: &ConnectionConfig) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ConnectionConfig

source§

impl StructuralPartialEq for ConnectionConfig

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.CustomCommand.html b/doc/fred/types/struct.CustomCommand.html new file mode 100644 index 00000000..160d3069 --- /dev/null +++ b/doc/fred/types/struct.CustomCommand.html @@ -0,0 +1,47 @@ +CustomCommand in fred::types - Rust

Struct fred::types::CustomCommand

source ·
pub struct CustomCommand {
+    pub cmd: Str,
+    pub cluster_hash: ClusterHash,
+    pub blocking: bool,
+}
Expand description

Configuration for custom redis commands, primarily used for interacting with third party modules or extensions.

+

Fields§

§cmd: Str

The command name, sent directly to the server.

+
§cluster_hash: ClusterHash

The cluster hashing policy to use, if any.

+

Cluster clients will use the default policy if not provided.

+
§blocking: bool

Whether the command should block the connection while waiting on a response.

+

Implementations§

source§

impl CustomCommand

source

pub fn new<C, H>(cmd: C, cluster_hash: H, blocking: bool) -> Self
where + C: Into<Str>, + H: Into<ClusterHash>,

Create a new custom command.

+

See the custom command for more information.

+
source

pub fn new_static<H>(cmd: &'static str, cluster_hash: H, blocking: bool) -> Self
where + H: Into<ClusterHash>,

Create a new custom command specified by a &'static str.

+

Trait Implementations§

source§

impl Clone for CustomCommand

source§

fn clone(&self) -> CustomCommand

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CustomCommand

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for CustomCommand

source§

fn eq(&self, other: &CustomCommand) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for CustomCommand

source§

impl StructuralPartialEq for CustomCommand

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.DatabaseMemoryStats.html b/doc/fred/types/struct.DatabaseMemoryStats.html new file mode 100644 index 00000000..1eb09cdb --- /dev/null +++ b/doc/fred/types/struct.DatabaseMemoryStats.html @@ -0,0 +1,38 @@ +DatabaseMemoryStats in fred::types - Rust

Struct fred::types::DatabaseMemoryStats

source ·
pub struct DatabaseMemoryStats {
+    pub overhead_hashtable_main: u64,
+    pub overhead_hashtable_expires: u64,
+    pub overhead_hashtable_slot_to_keys: u64,
+}
Available on crate feature i-memory only.
Expand description

The parsed result of the MEMORY STATS command for a specific database.

+

https://redis.io/commands/memory-stats

+

Fields§

§overhead_hashtable_main: u64§overhead_hashtable_expires: u64§overhead_hashtable_slot_to_keys: u64

Trait Implementations§

source§

impl Clone for DatabaseMemoryStats

source§

fn clone(&self) -> DatabaseMemoryStats

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for DatabaseMemoryStats

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for DatabaseMemoryStats

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl FromRedis for DatabaseMemoryStats

source§

impl PartialEq for DatabaseMemoryStats

source§

fn eq(&self, other: &DatabaseMemoryStats) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl TryFrom<RedisValue> for DatabaseMemoryStats

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for DatabaseMemoryStats

source§

impl StructuralPartialEq for DatabaseMemoryStats

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.FtAggregateOptions.html b/doc/fred/types/struct.FtAggregateOptions.html new file mode 100644 index 00000000..efd07267 --- /dev/null +++ b/doc/fred/types/struct.FtAggregateOptions.html @@ -0,0 +1,41 @@ +FtAggregateOptions in fred::types - Rust

Struct fred::types::FtAggregateOptions

source ·
pub struct FtAggregateOptions {
+    pub verbatim: bool,
+    pub load: Option<Load>,
+    pub timeout: Option<i64>,
+    pub pipeline: Vec<AggregateOperation>,
+    pub cursor: Option<WithCursor>,
+    pub params: Vec<SearchParameter>,
+    pub dialect: Option<i64>,
+}
Available on crate feature i-redisearch only.
Expand description

Fields§

§verbatim: bool§load: Option<Load>§timeout: Option<i64>§pipeline: Vec<AggregateOperation>§cursor: Option<WithCursor>§params: Vec<SearchParameter>§dialect: Option<i64>

Trait Implementations§

source§

impl Clone for FtAggregateOptions

source§

fn clone(&self) -> FtAggregateOptions

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for FtAggregateOptions

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for FtAggregateOptions

source§

fn default() -> FtAggregateOptions

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.FtAlterOptions.html b/doc/fred/types/struct.FtAlterOptions.html new file mode 100644 index 00000000..6fc76898 --- /dev/null +++ b/doc/fred/types/struct.FtAlterOptions.html @@ -0,0 +1,36 @@ +FtAlterOptions in fred::types - Rust

Struct fred::types::FtAlterOptions

source ·
pub struct FtAlterOptions {
+    pub skipinitialscan: bool,
+    pub attribute: Str,
+    pub options: SearchSchemaKind,
+}
Available on crate feature i-redisearch only.
Expand description

Arguments to FT.ALTER.

+

Fields§

§skipinitialscan: bool§attribute: Str§options: SearchSchemaKind

Trait Implementations§

source§

impl Clone for FtAlterOptions

source§

fn clone(&self) -> FtAlterOptions

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for FtAlterOptions

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.FtCreateOptions.html b/doc/fred/types/struct.FtCreateOptions.html new file mode 100644 index 00000000..33e382b3 --- /dev/null +++ b/doc/fred/types/struct.FtCreateOptions.html @@ -0,0 +1,49 @@ +FtCreateOptions in fred::types - Rust

Struct fred::types::FtCreateOptions

source ·
pub struct FtCreateOptions {
Show 16 fields + pub on: Option<IndexKind>, + pub prefixes: Vec<Str>, + pub filter: Option<Str>, + pub language: Option<Str>, + pub language_field: Option<Str>, + pub score: Option<f64>, + pub score_field: Option<f64>, + pub payload_field: Option<Str>, + pub maxtextfields: bool, + pub temporary: Option<u64>, + pub nooffsets: bool, + pub nohl: bool, + pub nofields: bool, + pub nofreqs: bool, + pub stopwords: Vec<Str>, + pub skipinitialscan: bool, +
}
Available on crate feature i-redisearch only.
Expand description

Arguments for FT.CREATE.

+

Fields§

§on: Option<IndexKind>§prefixes: Vec<Str>§filter: Option<Str>§language: Option<Str>§language_field: Option<Str>§score: Option<f64>§score_field: Option<f64>§payload_field: Option<Str>§maxtextfields: bool§temporary: Option<u64>§nooffsets: bool§nohl: bool§nofields: bool§nofreqs: bool§stopwords: Vec<Str>§skipinitialscan: bool

Trait Implementations§

source§

impl Clone for FtCreateOptions

source§

fn clone(&self) -> FtCreateOptions

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for FtCreateOptions

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for FtCreateOptions

source§

fn default() -> FtCreateOptions

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.FtSearchOptions.html b/doc/fred/types/struct.FtSearchOptions.html new file mode 100644 index 00000000..97a0a9e7 --- /dev/null +++ b/doc/fred/types/struct.FtSearchOptions.html @@ -0,0 +1,58 @@ +FtSearchOptions in fred::types - Rust

Struct fred::types::FtSearchOptions

source ·
pub struct FtSearchOptions {
Show 25 fields + pub nocontent: bool, + pub verbatim: bool, + pub nostopwords: bool, + pub withscores: bool, + pub withpayloads: bool, + pub withsortkeys: bool, + pub filters: Vec<SearchFilter>, + pub geofilters: Vec<SearchGeoFilter>, + pub inkeys: Vec<RedisKey>, + pub infields: Vec<Str>, + pub return: Vec<SearchField>, + pub summarize: Option<SearchSummarize>, + pub highlight: Option<SearchHighlight>, + pub slop: Option<i64>, + pub timeout: Option<i64>, + pub inorder: bool, + pub language: Option<Str>, + pub expander: Option<Str>, + pub scorer: Option<Str>, + pub explainscore: bool, + pub payload: Option<Bytes>, + pub sortby: Option<SearchSortBy>, + pub limit: Option<Limit>, + pub params: Vec<SearchParameter>, + pub dialect: Option<i64>, +
}
Available on crate feature i-redisearch only.
Expand description

Arguments to FT.SEARCH.

+

Fields§

§nocontent: bool§verbatim: bool§nostopwords: bool§withscores: bool§withpayloads: bool§withsortkeys: bool§filters: Vec<SearchFilter>§geofilters: Vec<SearchGeoFilter>§inkeys: Vec<RedisKey>§infields: Vec<Str>§return: Vec<SearchField>§summarize: Option<SearchSummarize>§highlight: Option<SearchHighlight>§slop: Option<i64>§timeout: Option<i64>§inorder: bool§language: Option<Str>§expander: Option<Str>§scorer: Option<Str>§explainscore: bool§payload: Option<Bytes>§sortby: Option<SearchSortBy>§limit: Option<Limit>§params: Vec<SearchParameter>§dialect: Option<i64>

Trait Implementations§

source§

impl Clone for FtSearchOptions

source§

fn clone(&self) -> FtSearchOptions

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for FtSearchOptions

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for FtSearchOptions

source§

fn default() -> FtSearchOptions

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.Function.html b/doc/fred/types/struct.Function.html new file mode 100644 index 00000000..706a0676 --- /dev/null +++ b/doc/fred/types/struct.Function.html @@ -0,0 +1,71 @@ +Function in fred::types - Rust

Struct fred::types::Function

source ·
pub struct Function { /* private fields */ }
Available on crate feature i-scripts only.
Expand description

An individual function within a Library.

+

See the library documentation for more information.

+

Implementations§

source§

impl Function

source

pub fn new<S: Into<Str>>(name: S, flags: Vec<FunctionFlag>) -> Self

Create a new Function.

+
source

pub fn name(&self) -> &Str

Read the name of the function.

+
source

pub fn flags(&self) -> &[FunctionFlag]

Read the flags associated with the function.

+
source

pub async fn fcall<R, C, K, V>( + &self, + client: &C, + keys: K, + args: V, +) -> RedisResult<R>

Send the fcall command via the provided client.

+
source

pub async fn fcall_ro<R, C, K, V>( + &self, + client: &C, + keys: K, + args: V, +) -> RedisResult<R>

Send the fcall_ro command via the provided client.

+

Trait Implementations§

source§

impl Clone for Function

source§

fn clone(&self) -> Function

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Function

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for Function

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Hash for Function

source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl Ord for Function

source§

fn cmp(&self, other: &Self) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized + PartialOrd,

Restrict a value to a certain interval. Read more
source§

impl PartialEq for Function

source§

fn eq(&self, other: &Function) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl PartialOrd for Function

source§

fn partial_cmp(&self, other: &Self) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
source§

impl Eq for Function

source§

impl StructuralPartialEq for Function

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CallHasher for T
where + T: Hash + ?Sized,

§

default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64
where + H: Hash + ?Sized, + B: BuildHasher,

source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.GeoPosition.html b/doc/fred/types/struct.GeoPosition.html new file mode 100644 index 00000000..1f170909 --- /dev/null +++ b/doc/fred/types/struct.GeoPosition.html @@ -0,0 +1,36 @@ +GeoPosition in fred::types - Rust

Struct fred::types::GeoPosition

source ·
pub struct GeoPosition {
+    pub longitude: f64,
+    pub latitude: f64,
+}
Available on crate feature i-geo only.
Expand description

A struct describing the longitude and latitude coordinates of a GEO command.

+

Fields§

§longitude: f64§latitude: f64

Trait Implementations§

source§

impl Clone for GeoPosition

source§

fn clone(&self) -> GeoPosition

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for GeoPosition

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<(f64, f64)> for GeoPosition

source§

fn from(d: (f64, f64)) -> Self

Converts to this type from the input type.
source§

impl FromRedis for GeoPosition

source§

impl PartialEq for GeoPosition

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl TryFrom<RedisValue> for GeoPosition

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for GeoPosition

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.GeoRadiusInfo.html b/doc/fred/types/struct.GeoRadiusInfo.html new file mode 100644 index 00000000..aa62a5cc --- /dev/null +++ b/doc/fred/types/struct.GeoRadiusInfo.html @@ -0,0 +1,44 @@ +GeoRadiusInfo in fred::types - Rust

Struct fred::types::GeoRadiusInfo

source ·
pub struct GeoRadiusInfo {
+    pub member: RedisValue,
+    pub position: Option<GeoPosition>,
+    pub distance: Option<f64>,
+    pub hash: Option<i64>,
+}
Available on crate feature i-geo only.
Expand description

A typed struct representing the full output of the GEORADIUS (or similar) command.

+

Fields§

§member: RedisValue§position: Option<GeoPosition>§distance: Option<f64>§hash: Option<i64>

Implementations§

source§

impl GeoRadiusInfo

source

pub fn from_redis_value( + value: RedisValue, + withcoord: bool, + withdist: bool, + withhash: bool, +) -> Result<Self, RedisError>

Parse the value with context from the calling command.

+

Trait Implementations§

source§

impl Clone for GeoRadiusInfo

source§

fn clone(&self) -> GeoRadiusInfo

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for GeoRadiusInfo

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for GeoRadiusInfo

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for GeoRadiusInfo

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for GeoRadiusInfo

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.GeoValue.html b/doc/fred/types/struct.GeoValue.html new file mode 100644 index 00000000..1572d986 --- /dev/null +++ b/doc/fred/types/struct.GeoValue.html @@ -0,0 +1,38 @@ +GeoValue in fred::types - Rust

Struct fred::types::GeoValue

source ·
pub struct GeoValue {
+    pub coordinates: GeoPosition,
+    pub member: RedisValue,
+}
Available on crate feature i-geo only.
Expand description

A struct describing the value inside a GEO data structure.

+

Fields§

§coordinates: GeoPosition§member: RedisValue

Trait Implementations§

source§

impl Clone for GeoValue

source§

fn clone(&self) -> GeoValue

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for GeoValue

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<GeoValue> for MultipleGeoValues

source§

fn from(d: GeoValue) -> Self

Converts to this type from the input type.
source§

impl PartialEq for GeoValue

source§

fn eq(&self, other: &GeoValue) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl<T> TryFrom<(f64, f64, T)> for GeoValue
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(v: (f64, f64, T)) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for GeoValue

source§

impl StructuralPartialEq for GeoValue

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.GroupBy.html b/doc/fred/types/struct.GroupBy.html new file mode 100644 index 00000000..bbc5cf2f --- /dev/null +++ b/doc/fred/types/struct.GroupBy.html @@ -0,0 +1,36 @@ +GroupBy in fred::types - Rust

Struct fred::types::GroupBy

source ·
pub struct GroupBy {
+    pub groupby: Str,
+    pub reduce: Reducer,
+}
Available on crate feature i-time-series only.
Expand description

A struct representing GROUPBY label REDUCE reducer in commands such as TS.MRANGE.

+

Fields§

§groupby: Str§reduce: Reducer

Trait Implementations§

source§

impl Clone for GroupBy

source§

fn clone(&self) -> GroupBy

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for GroupBy

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<S: Into<Str>> From<(S, Reducer)> for GroupBy

source§

fn from((groupby, reduce): (S, Reducer)) -> Self

Converts to this type from the input type.
source§

impl PartialEq for GroupBy

source§

fn eq(&self, other: &GroupBy) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for GroupBy

source§

impl StructuralPartialEq for GroupBy

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.HScanResult.html b/doc/fred/types/struct.HScanResult.html new file mode 100644 index 00000000..a5409350 --- /dev/null +++ b/doc/fred/types/struct.HScanResult.html @@ -0,0 +1,33 @@ +HScanResult in fred::types - Rust

Struct fred::types::HScanResult

source ·
pub struct HScanResult { /* private fields */ }
Expand description

The result of a HSCAN operation.

+

Trait Implementations§

source§

impl Scanner for HScanResult

§

type Page = RedisMap

The type of results from the scan operation.
source§

fn cursor(&self) -> Option<Cow<'_, str>>

Read the cursor returned from the last scan operation.
source§

fn has_more(&self) -> bool

Whether the scan call will continue returning results. If false this will be the last result set +returned on the stream. Read more
source§

fn results(&self) -> &Option<Self::Page>

Return a reference to the last page of results.
source§

fn take_results(&mut self) -> Option<Self::Page>

Take ownership over the results of the SCAN operation. Calls to results or take_results will return None +afterwards.
source§

fn create_client(&self) -> RedisClient

A lightweight function to create a Redis client from the SCAN result. Read more
source§

fn next(self) -> Result<(), RedisError>

Move on to the next page of results from the SCAN operation. If no more results are available this may close the +stream. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.Invalidation.html b/doc/fred/types/struct.Invalidation.html new file mode 100644 index 00000000..52ca7769 --- /dev/null +++ b/doc/fred/types/struct.Invalidation.html @@ -0,0 +1,36 @@ +Invalidation in fred::types - Rust

Struct fred::types::Invalidation

source ·
pub struct Invalidation {
+    pub keys: Vec<RedisKey>,
+    pub server: Server,
+}
Available on crate features i-client and i-tracking only.
Expand description

A client tracking invalidation message from the provided server.

+

Fields§

§keys: Vec<RedisKey>§server: Server

Trait Implementations§

source§

impl Clone for Invalidation

source§

fn clone(&self) -> Invalidation

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Invalidation

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Invalidation

source§

fn eq(&self, other: &Invalidation) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for Invalidation

source§

impl StructuralPartialEq for Invalidation

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.KeyspaceEvent.html b/doc/fred/types/struct.KeyspaceEvent.html new file mode 100644 index 00000000..61abd2bf --- /dev/null +++ b/doc/fred/types/struct.KeyspaceEvent.html @@ -0,0 +1,49 @@ +KeyspaceEvent in fred::types - Rust

Struct fred::types::KeyspaceEvent

source ·
pub struct KeyspaceEvent {
+    pub db: u8,
+    pub operation: String,
+    pub key: RedisKey,
+}
Expand description

An event on the publish-subscribe interface describing a keyspace notification.

+

https://redis.io/topics/notifications

+

Fields§

§db: u8§operation: String§key: RedisKey

Trait Implementations§

source§

impl Clone for KeyspaceEvent

source§

fn clone(&self) -> KeyspaceEvent

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for KeyspaceEvent

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Hash for KeyspaceEvent

source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl Ord for KeyspaceEvent

source§

fn cmp(&self, other: &KeyspaceEvent) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized + PartialOrd,

Restrict a value to a certain interval. Read more
source§

impl PartialEq for KeyspaceEvent

source§

fn eq(&self, other: &KeyspaceEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl PartialOrd for KeyspaceEvent

source§

fn partial_cmp(&self, other: &KeyspaceEvent) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
source§

impl Eq for KeyspaceEvent

source§

impl StructuralPartialEq for KeyspaceEvent

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CallHasher for T
where + T: Hash + ?Sized,

§

default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64
where + H: Hash + ?Sized, + B: BuildHasher,

source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.Library.html b/doc/fred/types/struct.Library.html new file mode 100644 index 00000000..efeb5da5 --- /dev/null +++ b/doc/fred/types/struct.Library.html @@ -0,0 +1,72 @@ +Library in fred::types - Rust

Struct fred::types::Library

source ·
pub struct Library { /* private fields */ }
Available on crate feature i-scripts only.
Expand description

A helper struct for interacting with libraries and functions.

+ +
let code = "#!lua name=mylib \n redis.register_function('myfunc', function(keys, args) return \
+            args[1] end)";
+let library = Library::from_code(client, code).await?;
+assert_eq!(library.name(), "mylib");
+
+if let Some(func) = library.functions().get("myfunc") {
+  if func.flags().contains(&FunctionFlag::NoWrites) {
+    let _: () = func.fcall_ro(client, "key", "arg").await?;
+  } else {
+    let _: () = func.fcall(client, "key", "arg").await?;
+  }
+}
+

Implementations§

source§

impl Library

source

pub async fn from_code<S>( + client: &RedisClient, + code: S, +) -> Result<Self, RedisError>
where + S: Into<Str>,

Create a new Library with the provided code, loading it on all the servers and inspecting the contents via the FUNCTION LIST command.

+

This interface will load the library on the server.

+
source

pub async fn from_name<S>( + client: &RedisClient, + name: S, +) -> Result<Self, RedisError>
where + S: Into<Str>,

Create a new Library with the associated name, inspecting the library contents via the FUNCTION LIST command.

+

This interface assumes the library is already loaded on the server.

+
source

pub fn name(&self) -> &Str

Read the name of the library.

+
source

pub fn functions(&self) -> &HashMap<Str, Function>

Read the functions contained within this library.

+

Trait Implementations§

source§

impl Clone for Library

source§

fn clone(&self) -> Library

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Library

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for Library

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Hash for Library

source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl Ord for Library

source§

fn cmp(&self, other: &Self) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized + PartialOrd,

Restrict a value to a certain interval. Read more
source§

impl PartialEq for Library

source§

fn eq(&self, other: &Library) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl PartialOrd for Library

source§

fn partial_cmp(&self, other: &Self) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
source§

impl Eq for Library

source§

impl StructuralPartialEq for Library

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CallHasher for T
where + T: Hash + ?Sized,

§

default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64
where + H: Hash + ?Sized, + B: BuildHasher,

source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.MemoryStats.html b/doc/fred/types/struct.MemoryStats.html new file mode 100644 index 00000000..f19a46d7 --- /dev/null +++ b/doc/fred/types/struct.MemoryStats.html @@ -0,0 +1,61 @@ +MemoryStats in fred::types - Rust

Struct fred::types::MemoryStats

source ·
pub struct MemoryStats {
Show 26 fields + pub peak_allocated: u64, + pub total_allocated: u64, + pub startup_allocated: u64, + pub replication_backlog: u64, + pub clients_slaves: u64, + pub clients_normal: u64, + pub aof_buffer: u64, + pub lua_caches: u64, + pub overhead_total: u64, + pub keys_count: u64, + pub keys_bytes_per_key: u64, + pub dataset_bytes: u64, + pub dataset_percentage: f64, + pub peak_percentage: f64, + pub fragmentation: f64, + pub fragmentation_bytes: u64, + pub rss_overhead_ratio: f64, + pub rss_overhead_bytes: u64, + pub allocator_allocated: u64, + pub allocator_active: u64, + pub allocator_resident: u64, + pub allocator_fragmentation_ratio: f64, + pub allocator_fragmentation_bytes: u64, + pub allocator_rss_ratio: f64, + pub allocator_rss_bytes: u64, + pub db: HashMap<u16, DatabaseMemoryStats>, +
}
Available on crate feature i-memory only.
Expand description

The parsed result of the MEMORY STATS command.

+

https://redis.io/commands/memory-stats

+

Fields§

§peak_allocated: u64§total_allocated: u64§startup_allocated: u64§replication_backlog: u64§clients_slaves: u64§clients_normal: u64§aof_buffer: u64§lua_caches: u64§overhead_total: u64§keys_count: u64§keys_bytes_per_key: u64§dataset_bytes: u64§dataset_percentage: f64§peak_percentage: f64§fragmentation: f64§fragmentation_bytes: u64§rss_overhead_ratio: f64§rss_overhead_bytes: u64§allocator_allocated: u64§allocator_active: u64§allocator_resident: u64§allocator_fragmentation_ratio: f64§allocator_fragmentation_bytes: u64§allocator_rss_ratio: f64§allocator_rss_bytes: u64§db: HashMap<u16, DatabaseMemoryStats>

Trait Implementations§

source§

impl Clone for MemoryStats

source§

fn clone(&self) -> MemoryStats

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for MemoryStats

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for MemoryStats

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl FromRedis for MemoryStats

source§

impl PartialEq for MemoryStats

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl TryFrom<RedisValue> for MemoryStats

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for MemoryStats

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.Message.html b/doc/fred/types/struct.Message.html new file mode 100644 index 00000000..26291eb2 --- /dev/null +++ b/doc/fred/types/struct.Message.html @@ -0,0 +1,42 @@ +Message in fred::types - Rust

Struct fred::types::Message

source ·
pub struct Message {
+    pub channel: Str,
+    pub value: RedisValue,
+    pub kind: MessageKind,
+    pub server: Server,
+}
Expand description

A publish-subscribe message.

+

Fields§

§channel: Str

The channel on which the message was sent.

+
§value: RedisValue

The message contents.

+
§kind: MessageKind

The type of message subscription.

+
§server: Server

The server that sent the message.

+

Trait Implementations§

source§

impl Clone for Message

source§

fn clone(&self) -> Message

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Message

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Message

source§

fn eq(&self, other: &Message) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for Message

source§

impl StructuralPartialEq for Message

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.MultipleGeoValues.html b/doc/fred/types/struct.MultipleGeoValues.html new file mode 100644 index 00000000..34251a7c --- /dev/null +++ b/doc/fred/types/struct.MultipleGeoValues.html @@ -0,0 +1,33 @@ +MultipleGeoValues in fred::types - Rust

Struct fred::types::MultipleGeoValues

source ·
pub struct MultipleGeoValues { /* private fields */ }
Available on crate feature i-geo only.
Expand description

A convenience struct for commands that take one or more GEO values.

+

Implementations§

source§

impl MultipleGeoValues

source

pub fn len(&self) -> usize

source

pub fn inner(self) -> Vec<GeoValue>

Trait Implementations§

source§

impl Clone for MultipleGeoValues

source§

fn clone(&self) -> MultipleGeoValues

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for MultipleGeoValues

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<GeoValue> for MultipleGeoValues

source§

fn from(d: GeoValue) -> Self

Converts to this type from the input type.
source§

impl From<Vec<GeoValue>> for MultipleGeoValues

source§

fn from(d: Vec<GeoValue>) -> Self

Converts to this type from the input type.
source§

impl From<VecDeque<GeoValue>> for MultipleGeoValues

source§

fn from(d: VecDeque<GeoValue>) -> Self

Converts to this type from the input type.
source§

impl PartialEq for MultipleGeoValues

source§

fn eq(&self, other: &MultipleGeoValues) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for MultipleGeoValues

source§

impl StructuralPartialEq for MultipleGeoValues

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.MultipleHashSlots.html b/doc/fred/types/struct.MultipleHashSlots.html new file mode 100644 index 00000000..ab924fb8 --- /dev/null +++ b/doc/fred/types/struct.MultipleHashSlots.html @@ -0,0 +1,30 @@ +MultipleHashSlots in fred::types - Rust

Struct fred::types::MultipleHashSlots

source ·
pub struct MultipleHashSlots { /* private fields */ }
Expand description

A convenience struct for functions that take one or more hash slot values.

+

Implementations§

source§

impl MultipleHashSlots

source

pub fn inner(self) -> Vec<u16>

source

pub fn len(&self) -> usize

Trait Implementations§

source§

impl<'a> From<&'a [u16]> for MultipleHashSlots

source§

fn from(d: &'a [u16]) -> Self

Converts to this type from the input type.
source§

impl From<Vec<u16>> for MultipleHashSlots

source§

fn from(d: Vec<u16>) -> Self

Converts to this type from the input type.
source§

impl From<VecDeque<u16>> for MultipleHashSlots

source§

fn from(d: VecDeque<u16>) -> Self

Converts to this type from the input type.
source§

impl From<u16> for MultipleHashSlots

source§

fn from(d: u16) -> Self

Converts to this type from the input type.
source§

impl FromIterator<u16> for MultipleHashSlots

source§

fn from_iter<I: IntoIterator<Item = u16>>(iter: I) -> Self

Creates a value from an iterator. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.MultipleIDs.html b/doc/fred/types/struct.MultipleIDs.html new file mode 100644 index 00000000..1113ebab --- /dev/null +++ b/doc/fred/types/struct.MultipleIDs.html @@ -0,0 +1,36 @@ +MultipleIDs in fred::types - Rust

Struct fred::types::MultipleIDs

source ·
pub struct MultipleIDs { /* private fields */ }
Available on crate feature i-streams only.
Expand description

One or more IDs for elements in a stream.

+

Implementations§

source§

impl MultipleIDs

source

pub fn len(&self) -> usize

source

pub fn inner(self) -> Vec<XID>

Trait Implementations§

source§

impl Clone for MultipleIDs

source§

fn clone(&self) -> MultipleIDs

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for MultipleIDs

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<T> From<T> for MultipleIDs
where + T: Into<XID>,

source§

fn from(value: T) -> Self

Converts to this type from the input type.
source§

impl<T> From<Vec<T>> for MultipleIDs
where + T: Into<XID>,

source§

fn from(value: Vec<T>) -> Self

Converts to this type from the input type.
source§

impl<T> From<VecDeque<T>> for MultipleIDs
where + T: Into<XID>,

source§

fn from(value: VecDeque<T>) -> Self

Converts to this type from the input type.
source§

impl PartialEq for MultipleIDs

source§

fn eq(&self, other: &MultipleIDs) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for MultipleIDs

source§

impl StructuralPartialEq for MultipleIDs

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.MultipleKeys.html b/doc/fred/types/struct.MultipleKeys.html new file mode 100644 index 00000000..f6022d6b --- /dev/null +++ b/doc/fred/types/struct.MultipleKeys.html @@ -0,0 +1,48 @@ +MultipleKeys in fred::types - Rust

Struct fred::types::MultipleKeys

source ·
pub struct MultipleKeys { /* private fields */ }
Expand description

Convenience struct for commands that take 1 or more keys.

+

Note: this can also be used to represent an empty array of keys by passing None to any function that takes +Into<MultipleKeys>. This is mostly useful for EVAL and EVALSHA.

+

Implementations§

source§

impl MultipleKeys

source

pub fn new() -> MultipleKeys

source

pub fn inner(self) -> Vec<RedisKey>

source

pub fn into_values(self) -> Vec<RedisValue>

source

pub fn len(&self) -> usize

Trait Implementations§

source§

impl Clone for MultipleKeys

source§

fn clone(&self) -> MultipleKeys

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for MultipleKeys

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'a, K, const N: usize> From<&'a [K; N]> for MultipleKeys
where + K: Into<RedisKey> + Clone,

source§

fn from(value: &'a [K; N]) -> Self

Converts to this type from the input type.
source§

impl From<()> for MultipleKeys

source§

fn from(_: ()) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>> From<(A0, A1)> for MultipleKeys

source§

fn from(value: (A0, A1)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>> From<(A0, A1, A2)> for MultipleKeys

source§

fn from(value: (A0, A1, A2)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>> From<(A0, A1, A2, A3)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>> From<(A0, A1, A2, A3, A4)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>, A13: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)> for MultipleKeys

source§

fn from( + value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13), +) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>, A13: Into<RedisKey>, A14: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)> for MultipleKeys

source§

fn from( + value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14), +) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>, A13: Into<RedisKey>, A14: Into<RedisKey>, A15: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)> for MultipleKeys

source§

fn from( + value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15), +) -> Self

Converts to this type from the input type.
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>, A13: Into<RedisKey>, A14: Into<RedisKey>, A15: Into<RedisKey>, A16: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16)> for MultipleKeys

source§

fn from( + value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16), +) -> Self

Converts to this type from the input type.
source§

impl From<Option<RedisKey>> for MultipleKeys

source§

fn from(key: Option<RedisKey>) -> Self

Converts to this type from the input type.
source§

impl<T> From<T> for MultipleKeys
where + T: Into<RedisKey>,

source§

fn from(d: T) -> Self

Converts to this type from the input type.
source§

impl<T> From<Vec<T>> for MultipleKeys
where + T: Into<RedisKey>,

source§

fn from(d: Vec<T>) -> Self

Converts to this type from the input type.
source§

impl<T> From<VecDeque<T>> for MultipleKeys
where + T: Into<RedisKey>,

source§

fn from(d: VecDeque<T>) -> Self

Converts to this type from the input type.
source§

impl<T> FromIterator<T> for MultipleKeys
where + T: Into<RedisKey>,

source§

fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self

Creates a value from an iterator. Read more
source§

impl PartialEq for MultipleKeys

source§

fn eq(&self, other: &MultipleKeys) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for MultipleKeys

source§

impl StructuralPartialEq for MultipleKeys

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.MultipleOrderedPairs.html b/doc/fred/types/struct.MultipleOrderedPairs.html new file mode 100644 index 00000000..fd2ad56f --- /dev/null +++ b/doc/fred/types/struct.MultipleOrderedPairs.html @@ -0,0 +1,45 @@ +MultipleOrderedPairs in fred::types - Rust

Struct fred::types::MultipleOrderedPairs

source ·
pub struct MultipleOrderedPairs { /* private fields */ }
Available on crate feature i-streams only.
Expand description

One or more ordered key-value pairs, typically used as an argument for XADD.

+

Implementations§

Trait Implementations§

source§

impl Clone for MultipleOrderedPairs

source§

fn clone(&self) -> MultipleOrderedPairs

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for MultipleOrderedPairs

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<()> for MultipleOrderedPairs

source§

fn from(_: ()) -> Self

Converts to this type from the input type.
source§

impl PartialEq for MultipleOrderedPairs

source§

fn eq(&self, other: &MultipleOrderedPairs) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl<K, V> TryFrom<(K, V)> for MultipleOrderedPairs
where + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from((key, value): (K, V)) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<K, V> TryFrom<HashMap<K, V>> for MultipleOrderedPairs
where + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(values: HashMap<K, V>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<K, V> TryFrom<Vec<(K, V)>> for MultipleOrderedPairs
where + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(values: Vec<(K, V)>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<K, V> TryFrom<VecDeque<(K, V)>> for MultipleOrderedPairs
where + K: Into<RedisKey>, + V: TryInto<RedisValue>, + V::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(values: VecDeque<(K, V)>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for MultipleOrderedPairs

source§

impl StructuralPartialEq for MultipleOrderedPairs

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.MultipleWeights.html b/doc/fred/types/struct.MultipleWeights.html new file mode 100644 index 00000000..f30a7249 --- /dev/null +++ b/doc/fred/types/struct.MultipleWeights.html @@ -0,0 +1,30 @@ +MultipleWeights in fred::types - Rust

Struct fred::types::MultipleWeights

source ·
pub struct MultipleWeights { /* private fields */ }
Available on crate feature i-sorted-sets only.
Expand description

Convenience struct for ZINTERSTORE and ZUNIONSTORE when accepting 1 or more weights arguments.

+

Implementations§

source§

impl MultipleWeights

source

pub fn new() -> MultipleWeights

source

pub fn inner(self) -> Vec<f64>

source

pub fn len(&self) -> usize

Trait Implementations§

source§

impl From<Option<f64>> for MultipleWeights

source§

fn from(d: Option<f64>) -> Self

Converts to this type from the input type.
source§

impl From<Vec<f64>> for MultipleWeights

source§

fn from(d: Vec<f64>) -> Self

Converts to this type from the input type.
source§

impl From<VecDeque<f64>> for MultipleWeights

source§

fn from(d: VecDeque<f64>) -> Self

Converts to this type from the input type.
source§

impl From<f64> for MultipleWeights

source§

fn from(d: f64) -> Self

Converts to this type from the input type.
source§

impl FromIterator<f64> for MultipleWeights

source§

fn from_iter<I: IntoIterator<Item = f64>>(iter: I) -> Self

Creates a value from an iterator. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.MultipleZaddValues.html b/doc/fred/types/struct.MultipleZaddValues.html new file mode 100644 index 00000000..e0af4831 --- /dev/null +++ b/doc/fred/types/struct.MultipleZaddValues.html @@ -0,0 +1,37 @@ +MultipleZaddValues in fred::types - Rust

Struct fred::types::MultipleZaddValues

source ·
pub struct MultipleZaddValues { /* private fields */ }
Available on crate feature i-sorted-sets only.
Expand description

Convenience struct for the ZADD command to accept 1 or more (score, value) arguments.

+

Implementations§

Trait Implementations§

source§

impl<T> FromIterator<(f64, T)> for MultipleZaddValues
where + T: Into<RedisValue>,

source§

fn from_iter<I: IntoIterator<Item = (f64, T)>>(iter: I) -> Self

Creates a value from an iterator. Read more
source§

impl<T> TryFrom<(f64, T)> for MultipleZaddValues
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from((f, d): (f64, T)) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<T> TryFrom<Vec<(f64, T)>> for MultipleZaddValues
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: Vec<(f64, T)>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<T> TryFrom<VecDeque<(f64, T)>> for MultipleZaddValues
where + T: TryInto<RedisValue>, + T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: VecDeque<(f64, T)>) -> Result<Self, Self::Error>

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.Options.html b/doc/fred/types/struct.Options.html new file mode 100644 index 00000000..4b7275aa --- /dev/null +++ b/doc/fred/types/struct.Options.html @@ -0,0 +1,78 @@ +Options in fred::types - Rust

Struct fred::types::Options

source ·
pub struct Options {
+    pub max_attempts: Option<u32>,
+    pub max_redirections: Option<u32>,
+    pub timeout: Option<Duration>,
+    pub cluster_node: Option<Server>,
+    pub cluster_hash: Option<ClusterHash>,
+    pub no_backpressure: bool,
+    pub fail_fast: bool,
+    pub caching: Option<bool>,
+}
Expand description

Options to configure or overwrite for individual commands.

+

Fields left as None will use the value from the corresponding client or global config option.

+ +
async fn example() -> Result<(), RedisError> {
+  let options = Options {
+    max_attempts: Some(10),
+    max_redirections: Some(2),
+    ..Default::default()
+  };
+
+  let client = RedisClient::default();
+  client.init().await?;
+  let _: () = client.with_options(&options).get("foo").await?;
+
+  Ok(())
+}
+

See WithOptions for more information.

+

Fields§

§max_attempts: Option<u32>

Set the max number of write attempts for a command.

+
§max_redirections: Option<u32>

Set the max number of cluster redirections to follow for a command.

+
§timeout: Option<Duration>

Set the timeout duration for a command.

+

This interface is more* cancellation-safe than a simple timeout call.

+

* But it’s not perfect. There’s no reliable mechanism to cancel a command once it has been written +to the connection.

+
§cluster_node: Option<Server>

The cluster node that should receive the command.

+

The caller will receive a RedisErrorKind::Cluster error if the provided server does not exist.

+

The client will still follow redirection errors via this interface. Callers may not notice this, but incorrect +server arguments here could result in unnecessary calls to refresh the cached cluster routing table.

+
§cluster_hash: Option<ClusterHash>

The cluster hashing policy to use, if applicable.

+

If cluster_node is also provided it will take precedence over this value.

+
§no_backpressure: bool

Whether to skip backpressure checks for a command.

+
§fail_fast: bool

Whether the command should fail quickly if the connection is not healthy or available for writes. This always +takes precedence over max_attempts if true.

+

This can be useful for caching use cases where it’s preferable to fail fast with a fallback query to another +storage layer rather than wait for a reconnection delay.

+

Default: false

+
§caching: Option<bool>
Available on crate feature i-tracking only.

Whether to send CLIENT CACHING yes|no before the command.

+

Implementations§

source§

impl Options

source

pub fn extend(&mut self, other: &Self) -> &mut Self

Set the non-null values from other onto self.

+

Trait Implementations§

source§

impl Clone for Options

source§

fn clone(&self) -> Options

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Options

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Options

source§

fn default() -> Options

Returns the “default value” for a type. Read more
source§

impl PartialEq for Options

source§

fn eq(&self, other: &Options) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for Options

source§

impl StructuralPartialEq for Options

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.PerformanceConfig.html b/doc/fred/types/struct.PerformanceConfig.html new file mode 100644 index 00000000..6b264664 --- /dev/null +++ b/doc/fred/types/struct.PerformanceConfig.html @@ -0,0 +1,57 @@ +PerformanceConfig in fred::types - Rust

Struct fred::types::PerformanceConfig

source ·
pub struct PerformanceConfig {
+    pub auto_pipeline: bool,
+    pub backpressure: BackpressureConfig,
+    pub default_command_timeout: Duration,
+    pub max_feed_count: u64,
+    pub broadcast_channel_capacity: usize,
+    pub blocking_encode_threshold: usize,
+}
Expand description

Configuration options that can affect the performance of the client.

+

Fields§

§auto_pipeline: bool

Whether the client should automatically pipeline commands across tasks when possible.

+

The Pipeline interface can be used to pipeline commands within one task, +whereas this flag can automatically pipeline commands across tasks.

+

Default: true

+
§backpressure: BackpressureConfig

Configuration options for backpressure features in the client.

+
§default_command_timeout: Duration

An optional timeout to apply to all commands.

+

If 0 this will disable any timeout being applied to commands. Callers can also set timeouts on individual +commands via the with_options interface.

+

Default: 0

+
§max_feed_count: u64

The maximum number of frames that will be fed to a socket before flushing.

+

Note: in some circumstances the client with always flush the socket (QUIT, EXEC, etc).

+

Default: 200

+
§broadcast_channel_capacity: usize

The default capacity used when creating broadcast channels in the EventInterface.

+

Default: 32

+
§blocking_encode_threshold: usize
Available on crate feature blocking-encoding only.

The minimum size, in bytes, of frames that should be encoded or decoded with a blocking task.

+

See block_in_place for more information.

+

Default: 50_000_000

+

Trait Implementations§

source§

impl Clone for PerformanceConfig

source§

fn clone(&self) -> PerformanceConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for PerformanceConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for PerformanceConfig

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for PerformanceConfig

source§

fn eq(&self, other: &PerformanceConfig) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for PerformanceConfig

source§

impl StructuralPartialEq for PerformanceConfig

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.RangeAggregation.html b/doc/fred/types/struct.RangeAggregation.html new file mode 100644 index 00000000..e37fbce8 --- /dev/null +++ b/doc/fred/types/struct.RangeAggregation.html @@ -0,0 +1,40 @@ +RangeAggregation in fred::types - Rust

Struct fred::types::RangeAggregation

source ·
pub struct RangeAggregation {
+    pub align: Option<GetTimestamp>,
+    pub aggregation: Aggregator,
+    pub bucket_duration: u64,
+    pub bucket_timestamp: Option<BucketTimestamp>,
+    pub empty: bool,
+}
Available on crate feature i-time-series only.
Expand description

A struct representing [ALIGN align] AGGREGATION aggregator bucketDuration [BUCKETTIMESTAMP bt] [EMPTY] in +commands such as TS.MRANGE.

+

Fields§

§align: Option<GetTimestamp>§aggregation: Aggregator§bucket_duration: u64§bucket_timestamp: Option<BucketTimestamp>§empty: bool

Trait Implementations§

source§

impl Clone for RangeAggregation

source§

fn clone(&self) -> RangeAggregation

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for RangeAggregation

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<(Aggregator, u64)> for RangeAggregation

source§

fn from((aggregation, duration): (Aggregator, u64)) -> Self

Converts to this type from the input type.
source§

impl PartialEq for RangeAggregation

source§

fn eq(&self, other: &RangeAggregation) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for RangeAggregation

source§

impl StructuralPartialEq for RangeAggregation

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.RedisConfig.html b/doc/fred/types/struct.RedisConfig.html new file mode 100644 index 00000000..645d8cf1 --- /dev/null +++ b/doc/fred/types/struct.RedisConfig.html @@ -0,0 +1,187 @@ +RedisConfig in fred::types - Rust

Struct fred::types::RedisConfig

source ·
pub struct RedisConfig {
+    pub fail_fast: bool,
+    pub blocking: Blocking,
+    pub username: Option<String>,
+    pub password: Option<String>,
+    pub server: ServerConfig,
+    pub version: RespVersion,
+    pub database: Option<u8>,
+    pub tls: Option<TlsConfig>,
+    pub tracing: TracingConfig,
+}
Expand description

Configuration options for a RedisClient.

+

Fields§

§fail_fast: bool

Whether the client should return an error if it cannot connect to the server the first time when being +initialized. If false the client will run the reconnect logic if it cannot connect to the server the first +time, but if true the client will return initial connection errors to the caller immediately.

+

Normally the reconnection logic only applies to connections that close unexpectedly, but this flag can apply +the same logic to the first connection as it is being created.

+

Callers should use caution setting this to false since it can make debugging configuration issues more +difficult.

+

Default: true

+
§blocking: Blocking

The default behavior of the client when a command is sent while the connection is blocked on a blocking +command.

+

Setting this to anything other than Blocking::Block incurs a small performance penalty.

+

Default: Blocking::Block

+
§username: Option<String>

An optional ACL username for the client to use when authenticating. If ACL rules are not configured this should +be None.

+

Default: None

+
§password: Option<String>

An optional password for the client to use when authenticating.

+

Default: None

+
§server: ServerConfig

Connection configuration for the server(s).

+

Default: Centralized(localhost, 6379)

+
§version: RespVersion

The protocol version to use when communicating with the server(s).

+

If RESP3 is specified the client will automatically use HELLO when authenticating. **This requires Redis

+
+

=6.0.0.** If the HELLO command fails this will prevent the client from connecting. Callers should set this +to RESP2 and use HELLO manually to fall back to RESP2 if needed.

+
+

Note: upgrading an existing codebase from RESP2 to RESP3 may require changing certain type signatures. RESP3 +has a slightly different type system than RESP2.

+

Default: RESP2

+
§database: Option<u8>

An optional database number that the client will automatically SELECT after connecting or reconnecting.

+

It is recommended that callers use this field instead of putting a select() call inside the on_reconnect +block, if possible. Commands that were in-flight when the connection closed will retry before anything inside +the on_reconnect block.

+

Default: None

+
§tls: Option<TlsConfig>
Available on crate features enable-native-tls or enable-rustls or enable-rustls-ring only.

TLS configuration options.

+

Default: None

+
§tracing: TracingConfig
Available on crate feature partial-tracing only.

Tracing configuration options.

+

Implementations§

source§

impl RedisConfig

source

pub fn uses_tls(&self) -> bool

Whether the client uses TLS.

+
source

pub fn uses_native_tls(&self) -> bool

Whether the client uses a native-tls connector.

+
source

pub fn uses_rustls(&self) -> bool

Whether the client uses a rustls connector.

+
source

pub fn from_url(url: &str) -> Result<RedisConfig, RedisError>

Parse a URL string into a RedisConfig.

+
§URL Syntax
+

Centralized

+
redis|rediss :// [[username:]password@] host [:port][/database]
+
+

Clustered

+
redis|rediss[-cluster] :// [[username:]password@] host [:port][?[node=host1:port1][&node=host2:port2][&node=hostN:portN]]
+
+

Sentinel

+
redis|rediss[-sentinel] :// [[username1:]password1@] host [:port][/database][?[node=host1:port1][&node=host2:port2][&node=hostN:portN]
+                            [&sentinelServiceName=myservice][&sentinelUsername=username2][&sentinelPassword=password2]]
+
+

Unix Socket

+
redis+unix:// [[username:]password@] /path/to/redis.sock
+
§Schemes
+

This function will use the URL scheme to determine which server type the caller is using. Valid schemes include:

+
    +
  • redis - TCP connected to a centralized server.
  • +
  • rediss - TLS connected to a centralized server.
  • +
  • redis-cluster - TCP connected to a cluster.
  • +
  • rediss-cluster - TLS connected to a cluster.
  • +
  • redis-sentinel - TCP connected to a centralized server behind a sentinel layer.
  • +
  • rediss-sentinel - TLS connected to a centralized server behind a sentinel layer.
  • +
  • redis+unix - Unix domain socket followed by a path.
  • +
+

The rediss scheme prefix requires one of the TLS feature flags.

+
§Query Parameters
+

In some cases it’s necessary to specify multiple node hostname/port tuples (with a cluster or sentinel layer for +example). The following query parameters may also be used in their respective contexts:

+
    +
  • node - Specify another node in the topology. In a cluster this would refer to any other known cluster node. +In the context of a Redis sentinel layer this refers to a known sentinel node. Multiple node parameters +may be used in a URL.
  • +
  • sentinelServiceName - Specify the name of the sentinel service. This is required when using the +redis-sentinel scheme.
  • +
  • sentinelUsername - Specify the username to use when connecting to a sentinel node. This requires the +sentinel-auth feature and allows the caller to use different credentials for sentinel nodes vs the actual +Redis server. The username part of the URL immediately following the scheme will refer to the username used +when connecting to the backing Redis server.
  • +
  • sentinelPassword - Specify the password to use when connecting to a sentinel node. This requires the +sentinel-auth feature and allows the caller to use different credentials for sentinel nodes vs the actual +Redis server. The password part of the URL immediately following the scheme will refer to the password used +when connecting to the backing Redis server.
  • +
+

See the from_url_centralized, from_url_clustered, +from_url_sentinel, and from_url_unix for more information. Or +see the RedisConfig unit tests for examples.

+
source

pub fn from_url_centralized(url: &str) -> Result<RedisConfig, RedisError>

Create a centralized RedisConfig struct from a URL.

+
redis://username:password@foo.com:6379/1
+rediss://username:password@foo.com:6379/1
+redis://foo.com:6379/1
+redis://foo.com
+// ... etc
+
+

This function is very similar to from_url, but it adds a layer of validation for configuration +parameters that are only relevant to a centralized server.

+

For example:

+
    +
  • A database can be defined in the path section.
  • +
  • The port field is optional in this context. If it is not specified then 6379 will be used.
  • +
  • Any node or sentinel query parameters will be ignored.
  • +
+
source

pub fn from_url_clustered(url: &str) -> Result<RedisConfig, RedisError>

Create a clustered RedisConfig struct from a URL.

+
redis-cluster://username:password@foo.com:30001?node=bar.com:30002&node=baz.com:30003
+rediss-cluster://username:password@foo.com:30001?node=bar.com:30002&node=baz.com:30003
+rediss://foo.com:30001?node=bar.com:30002&node=baz.com:30003
+redis://foo.com:30001
+// ... etc
+
+

This function is very similar to from_url, but it adds a layer of validation for configuration +parameters that are only relevant to a clustered deployment.

+

For example:

+
    +
  • The -cluster suffix in the scheme is optional when using this function directly.
  • +
  • Any database defined in the path section will be ignored.
  • +
  • The port field is required in this context alongside any hostname.
  • +
  • Any node query parameters will be used to find other known cluster nodes.
  • +
  • Any sentinel query parameters will be ignored.
  • +
+
source

pub fn from_url_sentinel(url: &str) -> Result<RedisConfig, RedisError>

Create a sentinel RedisConfig struct from a URL.

+
redis-sentinel://username:password@foo.com:6379/1?sentinelServiceName=fakename&node=foo.com:30001&node=bar.com:30002
+rediss-sentinel://username:password@foo.com:6379/0?sentinelServiceName=fakename&node=foo.com:30001&node=bar.com:30002
+redis://foo.com:6379?sentinelServiceName=fakename
+rediss://foo.com:6379/1?sentinelServiceName=fakename
+// ... etc
+
+

This function is very similar to from_url, but it adds a layer of validation for configuration +parameters that are only relevant to a sentinel deployment.

+

For example:

+
    +
  • The -sentinel suffix in the scheme is optional when using this function directly.
  • +
  • A database can be defined in the path section.
  • +
  • The port field is optional following the first hostname (26379 will be used if undefined), but required +within any node query parameters.
  • +
  • Any node query parameters will be used to find other known sentinel nodes.
  • +
  • The sentinelServiceName query parameter is required.
  • +
  • Depending on the cargo features used other sentinel query parameters may be used.
  • +
+

This particular function is more complex than the others when the sentinel-auth feature is used. For example, +to declare a config that uses different credentials for the sentinel nodes vs the backing Redis servers:

+
redis-sentinel://username1:password1@foo.com:26379/1?sentinelServiceName=fakename&sentinelUsername=username2&sentinelPassword=password2&node=bar.com:26379&node=baz.com:26380
+
+

The above example will use ("username1", "password1") when authenticating to the backing Redis servers, and +("username2", "password2") when initially connecting to the sentinel nodes. Additionally, all 3 addresses +(foo.com:26379, bar.com:26379, baz.com:26380) specify known sentinel nodes.

+

Trait Implementations§

source§

impl Clone for RedisConfig

source§

fn clone(&self) -> RedisConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for RedisConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for RedisConfig

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for RedisConfig

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for RedisConfig

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.RedisKey.html b/doc/fred/types/struct.RedisKey.html new file mode 100644 index 00000000..6831e5f0 --- /dev/null +++ b/doc/fred/types/struct.RedisKey.html @@ -0,0 +1,61 @@ +RedisKey in fred::types - Rust

Struct fred::types::RedisKey

source ·
pub struct RedisKey { /* private fields */ }
Expand description

A key in Redis.

+

Implementations§

source§

impl RedisKey

source

pub const fn from_static(b: &'static [u8]) -> Self

Create a new RedisKey from static bytes without copying.

+
source

pub const fn from_static_str(b: &'static str) -> Self

Create a new RedisKey from a &'static str without copying.

+
source

pub fn as_str(&self) -> Option<&str>

Read the key as a str slice if it can be parsed as a UTF8 string.

+
source

pub fn as_bytes(&self) -> &[u8]

Read the key as a byte slice.

+
source

pub fn inner(&self) -> &Bytes

Read the inner Bytes struct.

+
source

pub fn as_str_lossy(&self) -> Cow<'_, str>

Read the key as a lossy UTF8 string with String::from_utf8_lossy.

+
source

pub fn into_string(self) -> Option<String>

Convert the key to a UTF8 string, if possible.

+
source

pub fn into_bytes(self) -> Bytes

Read the inner bytes making up the key.

+
source

pub fn as_bytes_str(&self) -> Option<Str>

Parse and return the key as a Str without copying the inner contents.

+
source

pub fn cluster_hash(&self) -> u16

Hash the key to find the associated cluster hash slot.

+
source

pub fn cluster_owner<C>(&self, client: &C) -> Option<Server>
where + C: ClientLike,

Read the host:port of the cluster node that owns the key if the client is clustered and the cluster state is +known.

+
source

pub fn take(&mut self) -> Bytes

Replace this key with an empty byte array, returning the bytes from the original key.

+
source

pub fn convert<K>(self) -> Result<K, RedisError>
where + K: FromRedisKey,

Attempt to convert the key to any type that implements FromRedisKey.

+

See the RedisValue::convert documentation for more information.

+

Trait Implementations§

source§

impl Clone for RedisKey

source§

fn clone(&self) -> RedisKey

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for RedisKey

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'a> From<&'a [u8]> for RedisKey

source§

fn from(b: &'a [u8]) -> Self

Converts to this type from the input type.
source§

impl From<&RedisKey> for RedisKey

source§

fn from(k: &RedisKey) -> RedisKey

Converts to this type from the input type.
source§

impl From<&StrInner<Bytes>> for RedisKey

source§

fn from(s: &Str) -> Self

Converts to this type from the input type.
source§

impl From<&String> for RedisKey

source§

fn from(s: &String) -> Self

Converts to this type from the input type.
source§

impl From<&str> for RedisKey

source§

fn from(s: &str) -> Self

Converts to this type from the input type.
source§

impl From<Box<[u8]>> for RedisKey

source§

fn from(b: Box<[u8]>) -> Self

Converts to this type from the input type.
source§

impl From<Bytes> for RedisKey

source§

fn from(b: Bytes) -> Self

Converts to this type from the input type.
source§

impl From<RedisKey> for RedisValue

source§

fn from(d: RedisKey) -> Self

Converts to this type from the input type.
source§

impl From<StrInner<Bytes>> for RedisKey

source§

fn from(s: Str) -> Self

Converts to this type from the input type.
source§

impl From<String> for RedisKey

source§

fn from(s: String) -> Self

Converts to this type from the input type.
source§

impl From<bool> for RedisKey

source§

fn from(b: bool) -> Self

Converts to this type from the input type.
source§

impl From<f32> for RedisKey

source§

fn from(val: f32) -> Self

Converts to this type from the input type.
source§

impl From<f64> for RedisKey

source§

fn from(val: f64) -> Self

Converts to this type from the input type.
source§

impl From<i128> for RedisKey

source§

fn from(val: i128) -> Self

Converts to this type from the input type.
source§

impl From<i16> for RedisKey

source§

fn from(val: i16) -> Self

Converts to this type from the input type.
source§

impl From<i32> for RedisKey

source§

fn from(val: i32) -> Self

Converts to this type from the input type.
source§

impl From<i64> for RedisKey

source§

fn from(val: i64) -> Self

Converts to this type from the input type.
source§

impl From<i8> for RedisKey

source§

fn from(val: i8) -> Self

Converts to this type from the input type.
source§

impl From<isize> for RedisKey

source§

fn from(val: isize) -> Self

Converts to this type from the input type.
source§

impl From<u128> for RedisKey

source§

fn from(val: u128) -> Self

Converts to this type from the input type.
source§

impl From<u16> for RedisKey

source§

fn from(val: u16) -> Self

Converts to this type from the input type.
source§

impl From<u32> for RedisKey

source§

fn from(val: u32) -> Self

Converts to this type from the input type.
source§

impl From<u64> for RedisKey

source§

fn from(val: u64) -> Self

Converts to this type from the input type.
source§

impl From<u8> for RedisKey

source§

fn from(val: u8) -> Self

Converts to this type from the input type.
source§

impl From<usize> for RedisKey

source§

fn from(val: usize) -> Self

Converts to this type from the input type.
source§

impl FromRedis for RedisKey

source§

impl FromRedisKey for RedisKey

source§

impl Hash for RedisKey

source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl Ord for RedisKey

source§

fn cmp(&self, other: &RedisKey) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized + PartialOrd,

Restrict a value to a certain interval. Read more
source§

impl PartialEq for RedisKey

source§

fn eq(&self, other: &RedisKey) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl PartialOrd for RedisKey

source§

fn partial_cmp(&self, other: &RedisKey) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
source§

impl TryFrom<RedisValue> for RedisKey

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for RedisKey

source§

impl StructuralPartialEq for RedisKey

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CallHasher for T
where + T: Hash + ?Sized,

§

default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64
where + H: Hash + ?Sized, + B: BuildHasher,

source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.RedisMap.html b/doc/fred/types/struct.RedisMap.html new file mode 100644 index 00000000..d104d035 --- /dev/null +++ b/doc/fred/types/struct.RedisMap.html @@ -0,0 +1,570 @@ +RedisMap in fred::types - Rust

Struct fred::types::RedisMap

source ·
pub struct RedisMap { /* private fields */ }
Expand description

A map of (RedisKey, RedisValue) pairs.

+

Implementations§

source§

impl RedisMap

source

pub fn new() -> Self

Create a new empty map.

+
source

pub fn take(&mut self) -> Self

Replace the value an empty map, returning the original value.

+
source

pub fn len(&self) -> usize

Read the number of (key, value) pairs in the map.

+
source

pub fn inner(self) -> HashMap<RedisKey, RedisValue>

Take the inner HashMap.

+

Methods from Deref<Target = HashMap<RedisKey, RedisValue>>§

1.0.0 · source

pub fn capacity(&self) -> usize

Returns the number of elements the map can hold without reallocating.

+

This number is a lower bound; the HashMap<K, V> might be able to hold +more, but is guaranteed to be able to hold at least this many.

+
§Examples
+
use std::collections::HashMap;
+let map: HashMap<i32, i32> = HashMap::with_capacity(100);
+assert!(map.capacity() >= 100);
+
1.0.0 · source

pub fn keys(&self) -> Keys<'_, K, V>

An iterator visiting all keys in arbitrary order. +The iterator element type is &'a K.

+
§Examples
+
use std::collections::HashMap;
+
+let map = HashMap::from([
+    ("a", 1),
+    ("b", 2),
+    ("c", 3),
+]);
+
+for key in map.keys() {
+    println!("{key}");
+}
+
§Performance
+

In the current implementation, iterating over keys takes O(capacity) time +instead of O(len) because it internally visits empty buckets too.

+
1.0.0 · source

pub fn values(&self) -> Values<'_, K, V>

An iterator visiting all values in arbitrary order. +The iterator element type is &'a V.

+
§Examples
+
use std::collections::HashMap;
+
+let map = HashMap::from([
+    ("a", 1),
+    ("b", 2),
+    ("c", 3),
+]);
+
+for val in map.values() {
+    println!("{val}");
+}
+
§Performance
+

In the current implementation, iterating over values takes O(capacity) time +instead of O(len) because it internally visits empty buckets too.

+
1.10.0 · source

pub fn values_mut(&mut self) -> ValuesMut<'_, K, V>

An iterator visiting all values mutably in arbitrary order. +The iterator element type is &'a mut V.

+
§Examples
+
use std::collections::HashMap;
+
+let mut map = HashMap::from([
+    ("a", 1),
+    ("b", 2),
+    ("c", 3),
+]);
+
+for val in map.values_mut() {
+    *val = *val + 10;
+}
+
+for val in map.values() {
+    println!("{val}");
+}
+
§Performance
+

In the current implementation, iterating over values takes O(capacity) time +instead of O(len) because it internally visits empty buckets too.

+
1.0.0 · source

pub fn iter(&self) -> Iter<'_, K, V>

An iterator visiting all key-value pairs in arbitrary order. +The iterator element type is (&'a K, &'a V).

+
§Examples
+
use std::collections::HashMap;
+
+let map = HashMap::from([
+    ("a", 1),
+    ("b", 2),
+    ("c", 3),
+]);
+
+for (key, val) in map.iter() {
+    println!("key: {key} val: {val}");
+}
+
§Performance
+

In the current implementation, iterating over map takes O(capacity) time +instead of O(len) because it internally visits empty buckets too.

+
1.0.0 · source

pub fn iter_mut(&mut self) -> IterMut<'_, K, V>

An iterator visiting all key-value pairs in arbitrary order, +with mutable references to the values. +The iterator element type is (&'a K, &'a mut V).

+
§Examples
+
use std::collections::HashMap;
+
+let mut map = HashMap::from([
+    ("a", 1),
+    ("b", 2),
+    ("c", 3),
+]);
+
+// Update all values
+for (_, val) in map.iter_mut() {
+    *val *= 2;
+}
+
+for (key, val) in &map {
+    println!("key: {key} val: {val}");
+}
+
§Performance
+

In the current implementation, iterating over map takes O(capacity) time +instead of O(len) because it internally visits empty buckets too.

+
1.0.0 · source

pub fn len(&self) -> usize

Returns the number of elements in the map.

+
§Examples
+
use std::collections::HashMap;
+
+let mut a = HashMap::new();
+assert_eq!(a.len(), 0);
+a.insert(1, "a");
+assert_eq!(a.len(), 1);
+
1.0.0 · source

pub fn is_empty(&self) -> bool

Returns true if the map contains no elements.

+
§Examples
+
use std::collections::HashMap;
+
+let mut a = HashMap::new();
+assert!(a.is_empty());
+a.insert(1, "a");
+assert!(!a.is_empty());
+
1.6.0 · source

pub fn drain(&mut self) -> Drain<'_, K, V>

Clears the map, returning all key-value pairs as an iterator. Keeps the +allocated memory for reuse.

+

If the returned iterator is dropped before being fully consumed, it +drops the remaining key-value pairs. The returned iterator keeps a +mutable borrow on the map to optimize its implementation.

+
§Examples
+
use std::collections::HashMap;
+
+let mut a = HashMap::new();
+a.insert(1, "a");
+a.insert(2, "b");
+
+for (k, v) in a.drain().take(1) {
+    assert!(k == 1 || k == 2);
+    assert!(v == "a" || v == "b");
+}
+
+assert!(a.is_empty());
+
source

pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, K, V, F>
where + F: FnMut(&K, &mut V) -> bool,

🔬This is a nightly-only experimental API. (hash_extract_if)

Creates an iterator which uses a closure to determine if an element should be removed.

+

If the closure returns true, the element is removed from the map and yielded. +If the closure returns false, or panics, the element remains in the map and will not be +yielded.

+

Note that extract_if lets you mutate every value in the filter closure, regardless of +whether you choose to keep or remove it.

+

If the returned ExtractIf is not exhausted, e.g. because it is dropped without iterating +or the iteration short-circuits, then the remaining elements will be retained. +Use retain with a negated predicate if you do not need the returned iterator.

+
§Examples
+

Splitting a map into even and odd keys, reusing the original map:

+ +
#![feature(hash_extract_if)]
+use std::collections::HashMap;
+
+let mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x)).collect();
+let extracted: HashMap<i32, i32> = map.extract_if(|k, _v| k % 2 == 0).collect();
+
+let mut evens = extracted.keys().copied().collect::<Vec<_>>();
+let mut odds = map.keys().copied().collect::<Vec<_>>();
+evens.sort();
+odds.sort();
+
+assert_eq!(evens, vec![0, 2, 4, 6]);
+assert_eq!(odds, vec![1, 3, 5, 7]);
+
1.18.0 · source

pub fn retain<F>(&mut self, f: F)
where + F: FnMut(&K, &mut V) -> bool,

Retains only the elements specified by the predicate.

+

In other words, remove all pairs (k, v) for which f(&k, &mut v) returns false. +The elements are visited in unsorted (and unspecified) order.

+
§Examples
+
use std::collections::HashMap;
+
+let mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x*10)).collect();
+map.retain(|&k, _| k % 2 == 0);
+assert_eq!(map.len(), 4);
+
§Performance
+

In the current implementation, this operation takes O(capacity) time +instead of O(len) because it internally visits empty buckets too.

+
1.0.0 · source

pub fn clear(&mut self)

Clears the map, removing all key-value pairs. Keeps the allocated memory +for reuse.

+
§Examples
+
use std::collections::HashMap;
+
+let mut a = HashMap::new();
+a.insert(1, "a");
+a.clear();
+assert!(a.is_empty());
+
1.9.0 · source

pub fn hasher(&self) -> &S

Returns a reference to the map’s BuildHasher.

+
§Examples
+
use std::collections::HashMap;
+use std::hash::RandomState;
+
+let hasher = RandomState::new();
+let map: HashMap<i32, i32> = HashMap::with_hasher(hasher);
+let hasher: &RandomState = map.hasher();
+
1.0.0 · source

pub fn reserve(&mut self, additional: usize)

Reserves capacity for at least additional more elements to be inserted +in the HashMap. The collection may reserve more space to speculatively +avoid frequent reallocations. After calling reserve, +capacity will be greater than or equal to self.len() + additional. +Does nothing if capacity is already sufficient.

+
§Panics
+

Panics if the new allocation size overflows usize.

+
§Examples
+
use std::collections::HashMap;
+let mut map: HashMap<&str, i32> = HashMap::new();
+map.reserve(10);
+
1.57.0 · source

pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>

Tries to reserve capacity for at least additional more elements to be inserted +in the HashMap. The collection may reserve more space to speculatively +avoid frequent reallocations. After calling try_reserve, +capacity will be greater than or equal to self.len() + additional if +it returns Ok(()). +Does nothing if capacity is already sufficient.

+
§Errors
+

If the capacity overflows, or the allocator reports a failure, then an error +is returned.

+
§Examples
+
use std::collections::HashMap;
+
+let mut map: HashMap<&str, isize> = HashMap::new();
+map.try_reserve(10).expect("why is the test harness OOMing on a handful of bytes?");
+
1.0.0 · source

pub fn shrink_to_fit(&mut self)

Shrinks the capacity of the map as much as possible. It will drop +down as much as possible while maintaining the internal rules +and possibly leaving some space in accordance with the resize policy.

+
§Examples
+
use std::collections::HashMap;
+
+let mut map: HashMap<i32, i32> = HashMap::with_capacity(100);
+map.insert(1, 2);
+map.insert(3, 4);
+assert!(map.capacity() >= 100);
+map.shrink_to_fit();
+assert!(map.capacity() >= 2);
+
1.56.0 · source

pub fn shrink_to(&mut self, min_capacity: usize)

Shrinks the capacity of the map with a lower limit. It will drop +down no lower than the supplied limit while maintaining the internal rules +and possibly leaving some space in accordance with the resize policy.

+

If the current capacity is less than the lower limit, this is a no-op.

+
§Examples
+
use std::collections::HashMap;
+
+let mut map: HashMap<i32, i32> = HashMap::with_capacity(100);
+map.insert(1, 2);
+map.insert(3, 4);
+assert!(map.capacity() >= 100);
+map.shrink_to(10);
+assert!(map.capacity() >= 10);
+map.shrink_to(0);
+assert!(map.capacity() >= 2);
+
1.0.0 · source

pub fn entry(&mut self, key: K) -> Entry<'_, K, V>

Gets the given key’s corresponding entry in the map for in-place manipulation.

+
§Examples
+
use std::collections::HashMap;
+
+let mut letters = HashMap::new();
+
+for ch in "a short treatise on fungi".chars() {
+    letters.entry(ch).and_modify(|counter| *counter += 1).or_insert(1);
+}
+
+assert_eq!(letters[&'s'], 2);
+assert_eq!(letters[&'t'], 3);
+assert_eq!(letters[&'u'], 1);
+assert_eq!(letters.get(&'y'), None);
+
1.0.0 · source

pub fn get<Q>(&self, k: &Q) -> Option<&V>
where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized,

Returns a reference to the value corresponding to the key.

+

The key may be any borrowed form of the map’s key type, but +Hash and Eq on the borrowed form must match those for +the key type.

+
§Examples
+
use std::collections::HashMap;
+
+let mut map = HashMap::new();
+map.insert(1, "a");
+assert_eq!(map.get(&1), Some(&"a"));
+assert_eq!(map.get(&2), None);
+
1.40.0 · source

pub fn get_key_value<Q>(&self, k: &Q) -> Option<(&K, &V)>
where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized,

Returns the key-value pair corresponding to the supplied key.

+

The supplied key may be any borrowed form of the map’s key type, but +Hash and Eq on the borrowed form must match those for +the key type.

+
§Examples
+
use std::collections::HashMap;
+
+let mut map = HashMap::new();
+map.insert(1, "a");
+assert_eq!(map.get_key_value(&1), Some((&1, &"a")));
+assert_eq!(map.get_key_value(&2), None);
+
source

pub fn get_many_mut<Q, const N: usize>( + &mut self, + ks: [&Q; N], +) -> Option<[&mut V; N]>
where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized,

🔬This is a nightly-only experimental API. (map_many_mut)

Attempts to get mutable references to N values in the map at once.

+

Returns an array of length N with the results of each query. For soundness, at most one +mutable reference will be returned to any value. None will be returned if any of the +keys are duplicates or missing.

+
§Examples
+
#![feature(map_many_mut)]
+use std::collections::HashMap;
+
+let mut libraries = HashMap::new();
+libraries.insert("Bodleian Library".to_string(), 1602);
+libraries.insert("Athenæum".to_string(), 1807);
+libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
+libraries.insert("Library of Congress".to_string(), 1800);
+
+let got = libraries.get_many_mut([
+    "Athenæum",
+    "Library of Congress",
+]);
+assert_eq!(
+    got,
+    Some([
+        &mut 1807,
+        &mut 1800,
+    ]),
+);
+
+// Missing keys result in None
+let got = libraries.get_many_mut([
+    "Athenæum",
+    "New York Public Library",
+]);
+assert_eq!(got, None);
+
+// Duplicate keys result in None
+let got = libraries.get_many_mut([
+    "Athenæum",
+    "Athenæum",
+]);
+assert_eq!(got, None);
+
source

pub unsafe fn get_many_unchecked_mut<Q, const N: usize>( + &mut self, + ks: [&Q; N], +) -> Option<[&mut V; N]>
where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized,

🔬This is a nightly-only experimental API. (map_many_mut)

Attempts to get mutable references to N values in the map at once, without validating that +the values are unique.

+

Returns an array of length N with the results of each query. None will be returned if +any of the keys are missing.

+

For a safe alternative see get_many_mut.

+
§Safety
+

Calling this method with overlapping keys is undefined behavior even if the resulting +references are not used.

+
§Examples
+
#![feature(map_many_mut)]
+use std::collections::HashMap;
+
+let mut libraries = HashMap::new();
+libraries.insert("Bodleian Library".to_string(), 1602);
+libraries.insert("Athenæum".to_string(), 1807);
+libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
+libraries.insert("Library of Congress".to_string(), 1800);
+
+let got = libraries.get_many_mut([
+    "Athenæum",
+    "Library of Congress",
+]);
+assert_eq!(
+    got,
+    Some([
+        &mut 1807,
+        &mut 1800,
+    ]),
+);
+
+// Missing keys result in None
+let got = libraries.get_many_mut([
+    "Athenæum",
+    "New York Public Library",
+]);
+assert_eq!(got, None);
+
1.0.0 · source

pub fn contains_key<Q>(&self, k: &Q) -> bool
where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized,

Returns true if the map contains a value for the specified key.

+

The key may be any borrowed form of the map’s key type, but +Hash and Eq on the borrowed form must match those for +the key type.

+
§Examples
+
use std::collections::HashMap;
+
+let mut map = HashMap::new();
+map.insert(1, "a");
+assert_eq!(map.contains_key(&1), true);
+assert_eq!(map.contains_key(&2), false);
+
1.0.0 · source

pub fn get_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized,

Returns a mutable reference to the value corresponding to the key.

+

The key may be any borrowed form of the map’s key type, but +Hash and Eq on the borrowed form must match those for +the key type.

+
§Examples
+
use std::collections::HashMap;
+
+let mut map = HashMap::new();
+map.insert(1, "a");
+if let Some(x) = map.get_mut(&1) {
+    *x = "b";
+}
+assert_eq!(map[&1], "b");
+
1.0.0 · source

pub fn insert(&mut self, k: K, v: V) -> Option<V>

Inserts a key-value pair into the map.

+

If the map did not have this key present, None is returned.

+

If the map did have this key present, the value is updated, and the old +value is returned. The key is not updated, though; this matters for +types that can be == without being identical. See the module-level +documentation for more.

+
§Examples
+
use std::collections::HashMap;
+
+let mut map = HashMap::new();
+assert_eq!(map.insert(37, "a"), None);
+assert_eq!(map.is_empty(), false);
+
+map.insert(37, "b");
+assert_eq!(map.insert(37, "c"), Some("b"));
+assert_eq!(map[&37], "c");
+
source

pub fn try_insert( + &mut self, + key: K, + value: V, +) -> Result<&mut V, OccupiedError<'_, K, V>>

🔬This is a nightly-only experimental API. (map_try_insert)

Tries to insert a key-value pair into the map, and returns +a mutable reference to the value in the entry.

+

If the map already had this key present, nothing is updated, and +an error containing the occupied entry and the value is returned.

+
§Examples
+

Basic usage:

+ +
#![feature(map_try_insert)]
+
+use std::collections::HashMap;
+
+let mut map = HashMap::new();
+assert_eq!(map.try_insert(37, "a").unwrap(), &"a");
+
+let err = map.try_insert(37, "b").unwrap_err();
+assert_eq!(err.entry.key(), &37);
+assert_eq!(err.entry.get(), &"a");
+assert_eq!(err.value, "b");
+
1.0.0 · source

pub fn remove<Q>(&mut self, k: &Q) -> Option<V>
where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized,

Removes a key from the map, returning the value at the key if the key +was previously in the map.

+

The key may be any borrowed form of the map’s key type, but +Hash and Eq on the borrowed form must match those for +the key type.

+
§Examples
+
use std::collections::HashMap;
+
+let mut map = HashMap::new();
+map.insert(1, "a");
+assert_eq!(map.remove(&1), Some("a"));
+assert_eq!(map.remove(&1), None);
+
1.27.0 · source

pub fn remove_entry<Q>(&mut self, k: &Q) -> Option<(K, V)>
where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized,

Removes a key from the map, returning the stored key and value if the +key was previously in the map.

+

The key may be any borrowed form of the map’s key type, but +Hash and Eq on the borrowed form must match those for +the key type.

+
§Examples
+
use std::collections::HashMap;
+
+let mut map = HashMap::new();
+map.insert(1, "a");
+assert_eq!(map.remove_entry(&1), Some((1, "a")));
+assert_eq!(map.remove(&1), None);
+
source

pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<'_, K, V, S>

🔬This is a nightly-only experimental API. (hash_raw_entry)

Creates a raw entry builder for the HashMap.

+

Raw entries provide the lowest level of control for searching and +manipulating a map. They must be manually initialized with a hash and +then manually searched. After this, insertions into a vacant entry +still require an owned key to be provided.

+

Raw entries are useful for such exotic situations as:

+
    +
  • Hash memoization
  • +
  • Deferring the creation of an owned key until it is known to be required
  • +
  • Using a search key that doesn’t work with the Borrow trait
  • +
  • Using custom comparison logic without newtype wrappers
  • +
+

Because raw entries provide much more low-level control, it’s much easier +to put the HashMap into an inconsistent state which, while memory-safe, +will cause the map to produce seemingly random results. Higher-level and +more foolproof APIs like entry should be preferred when possible.

+

In particular, the hash used to initialize the raw entry must still be +consistent with the hash of the key that is ultimately stored in the entry. +This is because implementations of HashMap may need to recompute hashes +when resizing, at which point only the keys are available.

+

Raw entries give mutable access to the keys. This must not be used +to modify how the key would compare or hash, as the map will not re-evaluate +where the key should go, meaning the keys may become “lost” if their +location does not reflect their state. For instance, if you change a key +so that the map now contains keys which compare equal, search may start +acting erratically, with two keys randomly masking each other. Implementations +are free to assume this doesn’t happen (within the limits of memory-safety).

+
source

pub fn raw_entry(&self) -> RawEntryBuilder<'_, K, V, S>

🔬This is a nightly-only experimental API. (hash_raw_entry)

Creates a raw immutable entry builder for the HashMap.

+

Raw entries provide the lowest level of control for searching and +manipulating a map. They must be manually initialized with a hash and +then manually searched.

+

This is useful for

+
    +
  • Hash memoization
  • +
  • Using a search key that doesn’t work with the Borrow trait
  • +
  • Using custom comparison logic without newtype wrappers
  • +
+

Unless you are in such a situation, higher-level and more foolproof APIs like +get should be preferred.

+

Immutable raw entries have very limited use; you might instead want raw_entry_mut.

+

Trait Implementations§

source§

impl Clone for RedisMap

source§

fn clone(&self) -> RedisMap

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for RedisMap

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Deref for RedisMap

§

type Target = HashMap<RedisKey, RedisValue>

The resulting type after dereferencing.
source§

fn deref(&self) -> &Self::Target

Dereferences the value.
source§

impl DerefMut for RedisMap

source§

fn deref_mut(&mut self) -> &mut Self::Target

Mutably dereferences the value.
source§

impl<'a> From<&'a RedisMap> for RedisMap

source§

fn from(vals: &'a RedisMap) -> Self

Converts to this type from the input type.
source§

impl From<()> for RedisMap

source§

fn from(_: ()) -> Self

Converts to this type from the input type.
source§

impl From<RedisMap> for RedisValue

source§

fn from(m: RedisMap) -> Self

Converts to this type from the input type.
source§

impl<K, V> FromIterator<(K, V)> for RedisMap
where + K: Into<RedisKey>, + V: Into<RedisValue>,

source§

fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self

Creates a value from an iterator. Read more
source§

impl PartialEq for RedisMap

source§

fn eq(&self, other: &RedisMap) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl<'a, K, V, const N: usize> TryFrom<&'a [(K, V); N]> for RedisMap

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: &'a [(K, V); N]) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<K, V, const N: usize> TryFrom<[(K, V); N]> for RedisMap

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: [(K, V); N]) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<K, V> TryFrom<(K, V)> for RedisMap

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from((key, value): (K, V)) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<K, V> TryFrom<BTreeMap<K, V>> for RedisMap

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: BTreeMap<K, V>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<K, V> TryFrom<HashMap<K, V>> for RedisMap

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: HashMap<K, V>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<K, V> TryFrom<Vec<(K, V)>> for RedisMap

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(values: Vec<(K, V)>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<K, V> TryFrom<VecDeque<(K, V)>> for RedisMap

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(values: VecDeque<(K, V)>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for RedisMap

source§

impl StructuralPartialEq for RedisMap

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.ReplicaConfig.html b/doc/fred/types/struct.ReplicaConfig.html new file mode 100644 index 00000000..bb5ca840 --- /dev/null +++ b/doc/fred/types/struct.ReplicaConfig.html @@ -0,0 +1,49 @@ +ReplicaConfig in fred::types - Rust

Struct fred::types::ReplicaConfig

source ·
pub struct ReplicaConfig {
+    pub lazy_connections: bool,
+    pub filter: Option<Rc<dyn ReplicaFilter>>,
+    pub ignore_reconnection_errors: bool,
+    pub connection_error_count: u32,
+    pub primary_fallback: bool,
+}
Available on crate feature replicas only.
Expand description

Configuration options for replica node connections.

+

Fields§

§lazy_connections: bool

Whether the client should lazily connect to replica nodes.

+

Default: true

+
§filter: Option<Rc<dyn ReplicaFilter>>

An optional interface for filtering available replica nodes.

+

Default: None

+
§ignore_reconnection_errors: bool

Whether the client should ignore errors from replicas that occur when the max reconnection count is reached.

+

Default: true

+
§connection_error_count: u32

The number of times a command can fail with a replica connection error before being sent to a primary node.

+

Default: 0 (unlimited)

+
§primary_fallback: bool

Whether the client should use the associated primary node if no replica exists that can serve a command.

+

Default: true

+

Trait Implementations§

source§

impl Clone for ReplicaConfig

source§

fn clone(&self) -> ReplicaConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ReplicaConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for ReplicaConfig

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for ReplicaConfig

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for ReplicaConfig

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.SScanResult.html b/doc/fred/types/struct.SScanResult.html new file mode 100644 index 00000000..63604567 --- /dev/null +++ b/doc/fred/types/struct.SScanResult.html @@ -0,0 +1,33 @@ +SScanResult in fred::types - Rust

Struct fred::types::SScanResult

source ·
pub struct SScanResult { /* private fields */ }
Expand description

The result of a SSCAN operation.

+

Trait Implementations§

source§

impl Scanner for SScanResult

§

type Page = Vec<RedisValue>

The type of results from the scan operation.
source§

fn cursor(&self) -> Option<Cow<'_, str>>

Read the cursor returned from the last scan operation.
source§

fn results(&self) -> &Option<Self::Page>

Return a reference to the last page of results.
source§

fn take_results(&mut self) -> Option<Self::Page>

Take ownership over the results of the SCAN operation. Calls to results or take_results will return None +afterwards.
source§

fn has_more(&self) -> bool

Whether the scan call will continue returning results. If false this will be the last result set +returned on the stream. Read more
source§

fn create_client(&self) -> RedisClient

A lightweight function to create a Redis client from the SCAN result. Read more
source§

fn next(self) -> Result<(), RedisError>

Move on to the next page of results from the SCAN operation. If no more results are available this may close the +stream. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.ScanResult.html b/doc/fred/types/struct.ScanResult.html new file mode 100644 index 00000000..e736d4f5 --- /dev/null +++ b/doc/fred/types/struct.ScanResult.html @@ -0,0 +1,33 @@ +ScanResult in fred::types - Rust

Struct fred::types::ScanResult

source ·
pub struct ScanResult { /* private fields */ }
Expand description

The result of a SCAN operation.

+

Trait Implementations§

source§

impl Scanner for ScanResult

§

type Page = Vec<RedisKey>

The type of results from the scan operation.
source§

fn cursor(&self) -> Option<Cow<'_, str>>

Read the cursor returned from the last scan operation.
source§

fn has_more(&self) -> bool

Whether the scan call will continue returning results. If false this will be the last result set +returned on the stream. Read more
source§

fn results(&self) -> &Option<Self::Page>

Return a reference to the last page of results.
source§

fn take_results(&mut self) -> Option<Self::Page>

Take ownership over the results of the SCAN operation. Calls to results or take_results will return None +afterwards.
source§

fn create_client(&self) -> RedisClient

A lightweight function to create a Redis client from the SCAN result. Read more
source§

fn next(self) -> Result<(), RedisError>

Move on to the next page of results from the SCAN operation. If no more results are available this may close the +stream. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.Script.html b/doc/fred/types/struct.Script.html new file mode 100644 index 00000000..76828e99 --- /dev/null +++ b/doc/fred/types/struct.Script.html @@ -0,0 +1,83 @@ +Script in fred::types - Rust

Struct fred::types::Script

source ·
pub struct Script { /* private fields */ }
Available on crate feature i-scripts only.
Expand description

An interface for caching and running lua scripts.

+ +
async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  let script = Script::from_lua("return ARGV[1]");
+  assert_eq!(script.sha1(), "098e0f0d1448c0a81dafe820f66d460eb09263da");
+
+  let _ = script.load(client).await?;
+  let result: String = script.evalsha(client, "key", "arg").await?;
+  assert_eq!(result, "arg");
+  Ok(())
+}
+

Implementations§

source§

impl Script

source

pub fn from_lua<S: Into<Str>>(lua: S) -> Self

Available on crate feature sha-1 only.

Create a new Script from a lua script.

+
source

pub fn from_hash<S: Into<Str>>(hash: S) -> Self

Create a new Script from a lua hash.

+
source

pub fn lua(&self) -> Option<&Str>

Read the lua script contents.

+
source

pub fn sha1(&self) -> &Str

Read the SHA-1 hash for the script.

+
source

pub async fn load(&self, client: &RedisClient) -> RedisResult<()>

Available on crate feature sha-1 only.

Call SCRIPT LOAD on all the associated servers. This must be +called once before calling evalsha.

+
source

pub async fn evalsha<R, C, K, V>( + &self, + client: &C, + keys: K, + args: V, +) -> RedisResult<R>

Send EVALSHA to the server with the provided arguments.

+
source

pub async fn evalsha_with_reload<R, K, V>( + &self, + client: &RedisClient, + keys: K, + args: V, +) -> RedisResult<R>

Available on crate feature sha-1 only.

Send EVALSHA to the server with the provided arguments. Automatically SCRIPT LOAD in case +of NOSCRIPT error and try EVALSHA again.

+

Trait Implementations§

source§

impl Clone for Script

source§

fn clone(&self) -> Script

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Script

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for Script

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Hash for Script

source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl Ord for Script

source§

fn cmp(&self, other: &Self) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized + PartialOrd,

Restrict a value to a certain interval. Read more
source§

impl PartialEq for Script

source§

fn eq(&self, other: &Script) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl PartialOrd for Script

source§

fn partial_cmp(&self, other: &Self) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
source§

impl Eq for Script

source§

impl StructuralPartialEq for Script

Auto Trait Implementations§

§

impl !Freeze for Script

§

impl RefUnwindSafe for Script

§

impl Send for Script

§

impl Sync for Script

§

impl Unpin for Script

§

impl UnwindSafe for Script

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CallHasher for T
where + T: Hash + ?Sized,

§

default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64
where + H: Hash + ?Sized, + B: BuildHasher,

source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.SearchField.html b/doc/fred/types/struct.SearchField.html new file mode 100644 index 00000000..674ad040 --- /dev/null +++ b/doc/fred/types/struct.SearchField.html @@ -0,0 +1,37 @@ +SearchField in fred::types - Rust

Struct fred::types::SearchField

source ·
pub struct SearchField {
+    pub identifier: Str,
+    pub property: Option<Str>,
+}
Available on crate feature i-redisearch only.
Expand description

A search field with an optional property.

+

Typically equivalent to identifier [AS property] in FT.AGGREGATE, FT.SEARCH, etc.

+

Fields§

§identifier: Str§property: Option<Str>

Trait Implementations§

source§

impl Clone for SearchField

source§

fn clone(&self) -> SearchField

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SearchField

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for SearchField

source§

fn eq(&self, other: &SearchField) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for SearchField

source§

impl StructuralPartialEq for SearchField

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.SearchFilter.html b/doc/fred/types/struct.SearchFilter.html new file mode 100644 index 00000000..fcabefb8 --- /dev/null +++ b/doc/fred/types/struct.SearchFilter.html @@ -0,0 +1,37 @@ +SearchFilter in fred::types - Rust

Struct fred::types::SearchFilter

source ·
pub struct SearchFilter {
+    pub attribute: Str,
+    pub min: ZRange,
+    pub max: ZRange,
+}
Available on crate feature i-redisearch only.
Expand description

Arguments for FILTER in FT.SEARCH.

+

Callers should use the *Score* variants on any provided ZRange values.

+

Fields§

§attribute: Str§min: ZRange§max: ZRange

Trait Implementations§

source§

impl Clone for SearchFilter

source§

fn clone(&self) -> SearchFilter

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SearchFilter

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.SearchGeoFilter.html b/doc/fred/types/struct.SearchGeoFilter.html new file mode 100644 index 00000000..2fbf11c1 --- /dev/null +++ b/doc/fred/types/struct.SearchGeoFilter.html @@ -0,0 +1,37 @@ +SearchGeoFilter in fred::types - Rust

Struct fred::types::SearchGeoFilter

source ·
pub struct SearchGeoFilter {
+    pub attribute: Str,
+    pub position: GeoPosition,
+    pub radius: RedisValue,
+    pub units: GeoUnit,
+}
Available on crate feature i-redisearch only.
Expand description

Arguments for GEOFILTER in FT.SEARCH.

+

Fields§

§attribute: Str§position: GeoPosition§radius: RedisValue§units: GeoUnit

Trait Implementations§

source§

impl Clone for SearchGeoFilter

source§

fn clone(&self) -> SearchGeoFilter

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SearchGeoFilter

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.SearchHighlight.html b/doc/fred/types/struct.SearchHighlight.html new file mode 100644 index 00000000..c628815a --- /dev/null +++ b/doc/fred/types/struct.SearchHighlight.html @@ -0,0 +1,37 @@ +SearchHighlight in fred::types - Rust

Struct fred::types::SearchHighlight

source ·
pub struct SearchHighlight {
+    pub fields: Vec<Str>,
+    pub tags: Option<(Str, Str)>,
+}
Available on crate feature i-redisearch only.
Expand description

Fields§

§fields: Vec<Str>§tags: Option<(Str, Str)>

Trait Implementations§

source§

impl Clone for SearchHighlight

source§

fn clone(&self) -> SearchHighlight

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SearchHighlight

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for SearchHighlight

source§

fn default() -> SearchHighlight

Returns the “default value” for a type. Read more
source§

impl PartialEq for SearchHighlight

source§

fn eq(&self, other: &SearchHighlight) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for SearchHighlight

source§

impl StructuralPartialEq for SearchHighlight

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.SearchParameter.html b/doc/fred/types/struct.SearchParameter.html new file mode 100644 index 00000000..6a74fcbc --- /dev/null +++ b/doc/fred/types/struct.SearchParameter.html @@ -0,0 +1,36 @@ +SearchParameter in fred::types - Rust

Struct fred::types::SearchParameter

source ·
pub struct SearchParameter {
+    pub name: Str,
+    pub value: Str,
+}
Available on crate feature i-redisearch only.
Expand description

Arguments for PARAMS in FT.AGGREGATE.

+

Fields§

§name: Str§value: Str

Trait Implementations§

source§

impl Clone for SearchParameter

source§

fn clone(&self) -> SearchParameter

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SearchParameter

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for SearchParameter

source§

fn eq(&self, other: &SearchParameter) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for SearchParameter

source§

impl StructuralPartialEq for SearchParameter

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.SearchReducer.html b/doc/fred/types/struct.SearchReducer.html new file mode 100644 index 00000000..a64e8f38 --- /dev/null +++ b/doc/fred/types/struct.SearchReducer.html @@ -0,0 +1,38 @@ +SearchReducer in fred::types - Rust

Struct fred::types::SearchReducer

source ·
pub struct SearchReducer {
+    pub func: ReducerFunc,
+    pub args: Vec<Str>,
+    pub name: Option<Str>,
+}
Available on crate feature i-redisearch only.
Expand description

REDUCE arguments in FT.AGGREGATE.

+

Equivalent to function nargs arg [arg ...] [AS name]

+

Fields§

§func: ReducerFunc§args: Vec<Str>§name: Option<Str>

Trait Implementations§

source§

impl Clone for SearchReducer

source§

fn clone(&self) -> SearchReducer

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SearchReducer

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for SearchReducer

source§

fn eq(&self, other: &SearchReducer) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for SearchReducer

source§

impl StructuralPartialEq for SearchReducer

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.SearchSchema.html b/doc/fred/types/struct.SearchSchema.html new file mode 100644 index 00000000..e379011a --- /dev/null +++ b/doc/fred/types/struct.SearchSchema.html @@ -0,0 +1,36 @@ +SearchSchema in fred::types - Rust

Struct fred::types::SearchSchema

source ·
pub struct SearchSchema {
+    pub field_name: Str,
+    pub alias: Option<Str>,
+    pub kind: SearchSchemaKind,
+}
Available on crate feature i-redisearch only.
Expand description

Arguments for SCHEMA in FT.CREATE.

+

Fields§

§field_name: Str§alias: Option<Str>§kind: SearchSchemaKind

Trait Implementations§

source§

impl Clone for SearchSchema

source§

fn clone(&self) -> SearchSchema

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SearchSchema

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.SearchSortBy.html b/doc/fred/types/struct.SearchSortBy.html new file mode 100644 index 00000000..d5268afc --- /dev/null +++ b/doc/fred/types/struct.SearchSortBy.html @@ -0,0 +1,37 @@ +SearchSortBy in fred::types - Rust

Struct fred::types::SearchSortBy

source ·
pub struct SearchSortBy {
+    pub attribute: Str,
+    pub order: Option<SortOrder>,
+    pub withcount: bool,
+}
Available on crate feature i-redisearch only.
Expand description

Arguments for SORTBY in FT.SEARCH.

+

Fields§

§attribute: Str§order: Option<SortOrder>§withcount: bool

Trait Implementations§

source§

impl Clone for SearchSortBy

source§

fn clone(&self) -> SearchSortBy

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SearchSortBy

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for SearchSortBy

source§

fn eq(&self, other: &SearchSortBy) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for SearchSortBy

source§

impl StructuralPartialEq for SearchSortBy

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.SearchSummarize.html b/doc/fred/types/struct.SearchSummarize.html new file mode 100644 index 00000000..254a4856 --- /dev/null +++ b/doc/fred/types/struct.SearchSummarize.html @@ -0,0 +1,39 @@ +SearchSummarize in fred::types - Rust

Struct fred::types::SearchSummarize

source ·
pub struct SearchSummarize {
+    pub fields: Vec<Str>,
+    pub frags: Option<u64>,
+    pub len: Option<u64>,
+    pub separator: Option<Str>,
+}
Available on crate feature i-redisearch only.
Expand description

Fields§

§fields: Vec<Str>§frags: Option<u64>§len: Option<u64>§separator: Option<Str>

Trait Implementations§

source§

impl Clone for SearchSummarize

source§

fn clone(&self) -> SearchSummarize

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SearchSummarize

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for SearchSummarize

source§

fn default() -> SearchSummarize

Returns the “default value” for a type. Read more
source§

impl PartialEq for SearchSummarize

source§

fn eq(&self, other: &SearchSummarize) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for SearchSummarize

source§

impl StructuralPartialEq for SearchSummarize

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.SentinelConfig.html b/doc/fred/types/struct.SentinelConfig.html new file mode 100644 index 00000000..52b9b1d2 --- /dev/null +++ b/doc/fred/types/struct.SentinelConfig.html @@ -0,0 +1,53 @@ +SentinelConfig in fred::types - Rust

Struct fred::types::SentinelConfig

source ·
pub struct SentinelConfig {
+    pub host: String,
+    pub port: u16,
+    pub username: Option<String>,
+    pub password: Option<String>,
+    pub tls: Option<TlsConfig>,
+    pub tracing: TracingConfig,
+}
Available on crate feature sentinel-client only.
Expand description

Configuration options for sentinel clients.

+

Fields§

§host: String

The hostname for the sentinel node.

+

Default: 127.0.0.1

+
§port: u16

The port on which the sentinel node is listening.

+

Default: 26379

+
§username: Option<String>

An optional ACL username for the client to use when authenticating. If ACL rules are not configured this should +be None.

+

Default: None

+
§password: Option<String>

An optional password for the client to use when authenticating.

+

Default: None

+
§tls: Option<TlsConfig>
Available on crate features enable-native-tls or enable-rustls or enable-rustls-ring only.

TLS configuration fields. If None the connection will not use TLS.

+

See the tls examples on Github for more information.

+

Default: None

+
§tracing: TracingConfig
Available on crate feature partial-tracing only.

Whether to enable tracing for this client.

+

Default: false

+

Trait Implementations§

source§

impl Clone for SentinelConfig

source§

fn clone(&self) -> SentinelConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SentinelConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for SentinelConfig

source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.Server.html b/doc/fred/types/struct.Server.html new file mode 100644 index 00000000..1ed184fb --- /dev/null +++ b/doc/fred/types/struct.Server.html @@ -0,0 +1,58 @@ +Server in fred::types - Rust

Struct fred::types::Server

source ·
pub struct Server {
+    pub host: Str,
+    pub port: u16,
+    pub tls_server_name: Option<Str>,
+}
Expand description

State necessary to identify or connect to a server.

+

Fields§

§host: Str

The hostname or IP address for the server.

+
§port: u16

The port for the server.

+
§tls_server_name: Option<Str>
Available on crate features enable-rustls or enable-native-tls or enable-rustls-ring only.

The server name used during the TLS handshake.

+

Implementations§

source§

impl Server

source

pub fn new_with_tls<S: Into<Str>>( + host: S, + port: u16, + tls_server_name: Option<String>, +) -> Self

Available on crate features enable-rustls or enable-native-tls or enable-rustls-ring only.

Create a new Server from parts with a TLS server name.

+
source

pub fn new<S: Into<Str>>(host: S, port: u16) -> Self

Create a new Server from parts.

+

Trait Implementations§

source§

impl Clone for Server

source§

fn clone(&self) -> Server

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Server

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for Server

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<&Server> for Server

source§

fn from(value: &Server) -> Self

Converts to this type from the input type.
source§

impl From<(&str, u16)> for Server

source§

fn from((host, port): (&str, u16)) -> Self

Converts to this type from the input type.
source§

impl From<(String, u16)> for Server

source§

fn from((host, port): (String, u16)) -> Self

Converts to this type from the input type.
source§

impl Hash for Server

source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl Ord for Server

source§

fn cmp(&self, other: &Self) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized + PartialOrd,

Restrict a value to a certain interval. Read more
source§

impl PartialEq for Server

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl PartialOrd for Server

source§

fn partial_cmp(&self, other: &Self) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
source§

impl TryFrom<&str> for Server

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: &str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<String> for Server

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: String) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for Server

Auto Trait Implementations§

§

impl !Freeze for Server

§

impl RefUnwindSafe for Server

§

impl Send for Server

§

impl Sync for Server

§

impl Unpin for Server

§

impl UnwindSafe for Server

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CallHasher for T
where + T: Hash + ?Sized,

§

default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64
where + H: Hash + ?Sized, + B: BuildHasher,

source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.SlotRange.html b/doc/fred/types/struct.SlotRange.html new file mode 100644 index 00000000..a60ee2ec --- /dev/null +++ b/doc/fred/types/struct.SlotRange.html @@ -0,0 +1,44 @@ +SlotRange in fred::types - Rust

Struct fred::types::SlotRange

source ·
pub struct SlotRange {
+    pub start: u16,
+    pub end: u16,
+    pub primary: Server,
+    pub id: Str,
+    pub replicas: Vec<Server>,
+}
Expand description

A slot range and associated cluster node information from the CLUSTER SLOTS command.

+

Fields§

§start: u16

The start of the hash slot range.

+
§end: u16

The end of the hash slot range.

+
§primary: Server

The primary server owner.

+
§id: Str

The internal ID assigned by the server.

+
§replicas: Vec<Server>
Available on crate feature replicas only.

Replica node owners.

+

Trait Implementations§

source§

impl Clone for SlotRange

source§

fn clone(&self) -> SlotRange

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SlotRange

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for SlotRange

source§

fn eq(&self, other: &SlotRange) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for SlotRange

source§

impl StructuralPartialEq for SlotRange

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.SlowlogEntry.html b/doc/fred/types/struct.SlowlogEntry.html new file mode 100644 index 00000000..deced8bc --- /dev/null +++ b/doc/fred/types/struct.SlowlogEntry.html @@ -0,0 +1,41 @@ +SlowlogEntry in fred::types - Rust

Struct fred::types::SlowlogEntry

source ·
pub struct SlowlogEntry {
+    pub id: i64,
+    pub timestamp: i64,
+    pub duration: Duration,
+    pub args: Vec<RedisValue>,
+    pub ip: Option<Str>,
+    pub name: Option<Str>,
+}
Expand description

The output of an entry in the slow queries log.

+

https://redis.io/commands/slowlog#output-format

+

Fields§

§id: i64§timestamp: i64§duration: Duration§args: Vec<RedisValue>§ip: Option<Str>§name: Option<Str>

Trait Implementations§

source§

impl Clone for SlowlogEntry

source§

fn clone(&self) -> SlowlogEntry

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SlowlogEntry

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl FromRedis for SlowlogEntry

Available on crate feature i-slowlog only.
source§

impl PartialEq for SlowlogEntry

source§

fn eq(&self, other: &SlowlogEntry) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl TryFrom<RedisValue> for SlowlogEntry

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: RedisValue) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for SlowlogEntry

source§

impl StructuralPartialEq for SlowlogEntry

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.Stats.html b/doc/fred/types/struct.Stats.html new file mode 100644 index 00000000..2d2173b0 --- /dev/null +++ b/doc/fred/types/struct.Stats.html @@ -0,0 +1,38 @@ +Stats in fred::types - Rust

Struct fred::types::Stats

source ·
pub struct Stats {
+    pub min: i64,
+    pub max: i64,
+    pub avg: f64,
+    pub stddev: f64,
+    pub samples: u64,
+    pub sum: i64,
+}
Available on crate feature metrics only.
Expand description

Stats describing a distribution of samples.

+

Time units are in milliseconds, data size units are in bytes.

+

Fields§

§min: i64§max: i64§avg: f64§stddev: f64§samples: u64§sum: i64

Auto Trait Implementations§

§

impl Freeze for Stats

§

impl RefUnwindSafe for Stats

§

impl Send for Stats

§

impl Sync for Stats

§

impl Unpin for Stats

§

impl UnwindSafe for Stats

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.TcpConfig.html b/doc/fred/types/struct.TcpConfig.html new file mode 100644 index 00000000..003c9a14 --- /dev/null +++ b/doc/fred/types/struct.TcpConfig.html @@ -0,0 +1,42 @@ +TcpConfig in fred::types - Rust

Struct fred::types::TcpConfig

source ·
pub struct TcpConfig {
+    pub nodelay: Option<bool>,
+    pub linger: Option<Duration>,
+    pub ttl: Option<u32>,
+    pub keepalive: Option<TcpKeepalive>,
+}
Expand description

TCP configuration options.

+

Fields§

§nodelay: Option<bool>

Set the TCP_NODELAY value.

+
§linger: Option<Duration>

Set the SO_LINGER value.

+
§ttl: Option<u32>

Set the IP_TTL value.

+
§keepalive: Option<TcpKeepalive>

Trait Implementations§

source§

impl Clone for TcpConfig

source§

fn clone(&self) -> TcpConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for TcpConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for TcpConfig

source§

fn default() -> TcpConfig

Returns the “default value” for a type. Read more
source§

impl PartialEq for TcpConfig

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for TcpConfig

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.TlsConfig.html b/doc/fred/types/struct.TlsConfig.html new file mode 100644 index 00000000..3737fff7 --- /dev/null +++ b/doc/fred/types/struct.TlsConfig.html @@ -0,0 +1,49 @@ +TlsConfig in fred::types - Rust

Struct fred::types::TlsConfig

source ·
pub struct TlsConfig {
+    pub connector: TlsConnector,
+    pub hostnames: TlsHostMapping,
+}
Available on crate features enable-rustls or enable-native-tls or enable-rustls-ring only.
Expand description

TLS configuration for a client.

+

Note: the hostnames field is only necessary to use with certain clustered deployments.

+ +
let config = TlsConfig {
+  // or use `TlsConnector::default_rustls()`
+  connector: TlsConnector::default_native_tls().unwrap(),
+  hostnames: TlsHostMapping::None
+};
+
+// or use the shorthand
+let config: TlsConfig = TlsConnector::default_native_tls()?.into();
+let config: TlsConfig = TlsConnector::default_rustls()?.into();
+

Fields§

§connector: TlsConnector

The TLS connector from either native-tls or rustls.

+
§hostnames: TlsHostMapping

The hostname modification or mapping policy to use when discovering and connecting to cluster nodes.

+

Trait Implementations§

source§

impl Clone for TlsConfig

source§

fn clone(&self) -> TlsConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for TlsConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<C: Into<TlsConnector>> From<C> for TlsConfig

source§

fn from(connector: C) -> Self

Converts to this type from the input type.
source§

impl PartialEq for TlsConfig

source§

fn eq(&self, other: &TlsConfig) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for TlsConfig

source§

impl StructuralPartialEq for TlsConfig

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.TracingConfig.html b/doc/fred/types/struct.TracingConfig.html new file mode 100644 index 00000000..42ac2d3f --- /dev/null +++ b/doc/fred/types/struct.TracingConfig.html @@ -0,0 +1,42 @@ +TracingConfig in fred::types - Rust

Struct fred::types::TracingConfig

source ·
pub struct TracingConfig {
+    pub enabled: bool,
+    pub default_tracing_level: Level,
+    pub full_tracing_level: Level,
+}
Available on crate feature partial-tracing only.
Expand description

Configuration options for tracing.

+

Fields§

§enabled: bool

Whether to enable tracing for this client.

+

Default: false

+
§default_tracing_level: Level

Set the tracing::Level of spans under partial-tracing feature.

+

Default: INFO

+
§full_tracing_level: Level
Available on crate feature full-tracing only.

Set the tracing::Level of spans under full-tracing feature.

+

Default: DEBUG

+

Implementations§

source§

impl TracingConfig

source

pub fn new(enabled: bool) -> Self

Trait Implementations§

source§

impl Clone for TracingConfig

source§

fn clone(&self) -> TracingConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for TracingConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for TracingConfig

source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.UnresponsiveConfig.html b/doc/fred/types/struct.UnresponsiveConfig.html new file mode 100644 index 00000000..a7e69bcf --- /dev/null +++ b/doc/fred/types/struct.UnresponsiveConfig.html @@ -0,0 +1,49 @@ +UnresponsiveConfig in fred::types - Rust

Struct fred::types::UnresponsiveConfig

source ·
pub struct UnresponsiveConfig {
+    pub max_timeout: Option<Duration>,
+    pub interval: Duration,
+}
Expand description

Configuration options used to detect potentially unresponsive connections.

+

Fields§

§max_timeout: Option<Duration>

If provided, the amount of time a frame can wait without a response before the associated connection is +considered unresponsive.

+

If a connection is considered unresponsive it will be forcefully closed and the client will reconnect based on +the ReconnectPolicy. This heuristic can be useful in environments where +connections may close or change in subtle or unexpected ways.

+

Unlike the timeout and default_command_timeout +interfaces, any in-flight commands waiting on a response when the connection is closed this way will be +retried based on the associated ReconnectPolicy and +Options.

+

Default: None

+
§interval: Duration

The frequency at which the client checks for unresponsive connections.

+

This value should usually be less than half of max_timeout and always more than 1 ms.

+

Default: 2 sec

+

Trait Implementations§

source§

impl Clone for UnresponsiveConfig

source§

fn clone(&self) -> UnresponsiveConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for UnresponsiveConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for UnresponsiveConfig

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for UnresponsiveConfig

source§

fn eq(&self, other: &UnresponsiveConfig) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for UnresponsiveConfig

source§

impl StructuralPartialEq for UnresponsiveConfig

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.Version.html b/doc/fred/types/struct.Version.html new file mode 100644 index 00000000..cbc52ffd --- /dev/null +++ b/doc/fred/types/struct.Version.html @@ -0,0 +1,188 @@ +Version in fred::types - Rust

Struct fred::types::Version

source ·
pub struct Version {
+    pub major: u64,
+    pub minor: u64,
+    pub patch: u64,
+    pub pre: Prerelease,
+    pub build: BuildMetadata,
+}
Expand description

SemVer version as defined by https://semver.org.

+

§Syntax

+
    +
  • +

    The major, minor, and patch numbers may be any integer 0 through u64::MAX. +When representing a SemVer version as a string, each number is written as +a base 10 integer. For example, 1.0.119.

    +
  • +
  • +

    Leading zeros are forbidden in those positions. For example 1.01.00 is +invalid as a SemVer version.

    +
  • +
  • +

    The pre-release identifier, if present, must conform to the syntax +documented for Prerelease.

    +
  • +
  • +

    The build metadata, if present, must conform to the syntax documented for +BuildMetadata.

    +
  • +
  • +

    Whitespace is not allowed anywhere in the version.

    +
  • +
+

§Total ordering

+

Given any two SemVer versions, one is less than, greater than, or equal to +the other. Versions may be compared against one another using Rust’s usual +comparison operators.

+
    +
  • +

    The major, minor, and patch number are compared numerically from left to +right, lexicographically ordered as a 3-tuple of integers. So for example +version 1.5.0 is less than version 1.19.0, despite the fact that +“1.19.0” < “1.5.0” as ASCIIbetically compared strings and 1.19 < 1.5 +as real numbers.

    +
  • +
  • +

    When major, minor, and patch are equal, a pre-release version is +considered less than the ordinary release: version 1.0.0-alpha.1 is +less than version 1.0.0.

    +
  • +
  • +

    Two pre-releases of the same major, minor, patch are compared by +lexicographic ordering of dot-separated components of the pre-release +string.

    +
      +
    • +

      Identifiers consisting of only digits are compared +numerically: 1.0.0-pre.8 is less than 1.0.0-pre.12.

      +
    • +
    • +

      Identifiers that contain a letter or hyphen are compared in ASCII sort +order: 1.0.0-pre12 is less than 1.0.0-pre8.

      +
    • +
    • +

      Any numeric identifier is always less than any non-numeric +identifier: 1.0.0-pre.1 is less than 1.0.0-pre.x.

      +
    • +
    +
  • +
+

Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0

+

Fields§

§major: u64§minor: u64§patch: u64§pre: Prerelease§build: BuildMetadata

Implementations§

source§

impl Version

source

pub const fn new(major: u64, minor: u64, patch: u64) -> Version

Create Version with an empty pre-release and build metadata.

+

Equivalent to:

+ +
Version {
+    major,
+    minor,
+    patch,
+    pre: Prerelease::EMPTY,
+    build: BuildMetadata::EMPTY,
+}
+
source

pub fn parse(text: &str) -> Result<Version, Error>

Create Version by parsing from string representation.

+
§Errors
+

Possible reasons for the parse to fail include:

+
    +
  • +

    1.0 — too few numeric components. A SemVer version must have +exactly three. If you are looking at something that has fewer than +three numbers in it, it’s possible it is a VersionReq instead (with +an implicit default ^ comparison operator).

    +
  • +
  • +

    1.0.01 — a numeric component has a leading zero.

    +
  • +
  • +

    1.0.unknown — unexpected character in one of the components.

    +
  • +
  • +

    1.0.0- or 1.0.0+ — the pre-release or build metadata are +indicated present but empty.

    +
  • +
  • +

    1.0.0-alpha_123 — pre-release or build metadata have something +outside the allowed characters, which are 0-9, A-Z, a-z, -, +and . (dot).

    +
  • +
  • +

    23456789999999999999.0.0 — overflow of a u64.

    +
  • +
+
source

pub fn cmp_precedence(&self, other: &Version) -> Ordering

Compare the major, minor, patch, and pre-release value of two versions, +disregarding build metadata. Versions that differ only in build metadata +are considered equal. This comparison is what the SemVer spec refers to +as “precedence”.

+
§Example
+
use semver::Version;
+
+let mut versions = [
+    "1.20.0+c144a98".parse::<Version>().unwrap(),
+    "1.20.0".parse().unwrap(),
+    "1.0.0".parse().unwrap(),
+    "1.0.0-alpha".parse().unwrap(),
+    "1.20.0+bc17664".parse().unwrap(),
+];
+
+// This is a stable sort, so it preserves the relative order of equal
+// elements. The three 1.20.0 versions differ only in build metadata so
+// they are not reordered relative to one another.
+versions.sort_by(Version::cmp_precedence);
+assert_eq!(versions, [
+    "1.0.0-alpha".parse().unwrap(),
+    "1.0.0".parse().unwrap(),
+    "1.20.0+c144a98".parse().unwrap(),
+    "1.20.0".parse().unwrap(),
+    "1.20.0+bc17664".parse().unwrap(),
+]);
+
+// Totally order the versions, including comparing the build metadata.
+versions.sort();
+assert_eq!(versions, [
+    "1.0.0-alpha".parse().unwrap(),
+    "1.0.0".parse().unwrap(),
+    "1.20.0".parse().unwrap(),
+    "1.20.0+bc17664".parse().unwrap(),
+    "1.20.0+c144a98".parse().unwrap(),
+]);
+

Trait Implementations§

source§

impl Clone for Version

source§

fn clone(&self) -> Version

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Version

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
source§

impl Display for Version

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
source§

impl FromStr for Version

§

type Err = Error

The associated error which can be returned from parsing.
source§

fn from_str(text: &str) -> Result<Version, <Version as FromStr>::Err>

Parses a string s to return a value of this type. Read more
source§

impl Hash for Version

source§

fn hash<__H>(&self, state: &mut __H)
where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl Ord for Version

source§

fn cmp(&self, other: &Version) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized + PartialOrd,

Restrict a value to a certain interval. Read more
source§

impl PartialEq for Version

source§

fn eq(&self, other: &Version) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl PartialOrd for Version

source§

fn partial_cmp(&self, other: &Version) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
source§

impl Eq for Version

source§

impl StructuralPartialEq for Version

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CallHasher for T
where + T: Hash + ?Sized,

§

default fn get_hash<H, B>(value: &H, build_hasher: &B) -> u64
where + H: Hash + ?Sized, + B: BuildHasher,

source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.WithCursor.html b/doc/fred/types/struct.WithCursor.html new file mode 100644 index 00000000..d3c5c135 --- /dev/null +++ b/doc/fred/types/struct.WithCursor.html @@ -0,0 +1,36 @@ +WithCursor in fred::types - Rust

Struct fred::types::WithCursor

source ·
pub struct WithCursor {
+    pub count: Option<u64>,
+    pub max_idle: Option<u64>,
+}
Available on crate feature i-redisearch only.
Expand description

Arguments for WITHCURSOR in FT.AGGREGATE.

+

Fields§

§count: Option<u64>§max_idle: Option<u64>

Trait Implementations§

source§

impl Clone for WithCursor

source§

fn clone(&self) -> WithCursor

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for WithCursor

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for WithCursor

source§

fn eq(&self, other: &WithCursor) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for WithCursor

source§

impl StructuralPartialEq for WithCursor

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.XCap.html b/doc/fred/types/struct.XCap.html new file mode 100644 index 00000000..f52cc886 --- /dev/null +++ b/doc/fred/types/struct.XCap.html @@ -0,0 +1,49 @@ +XCap in fred::types - Rust

Struct fred::types::XCap

source ·
pub struct XCap { /* private fields */ }
Available on crate feature i-streams only.
Expand description

Stream cap arguments for XADD, XTRIM, etc.

+

Equivalent to [MAXLEN|MINID [=|~] threshold [LIMIT count]].

+

Trait Implementations§

source§

impl Clone for XCap

source§

fn clone(&self) -> XCap

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for XCap

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<Option<()>> for XCap

source§

fn from(_: Option<()>) -> Self

Converts to this type from the input type.
source§

impl PartialEq for XCap

source§

fn eq(&self, other: &XCap) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl<K, S> TryFrom<(K, S)> for XCap

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from((kind, threshold): (K, S)) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<K, T, S> TryFrom<(K, T, S)> for XCap

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from((kind, trim, threshold): (K, T, S)) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl<K, T, S> TryFrom<(K, T, S, Option<i64>)> for XCap

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from( + (kind, trim, threshold, limit): (K, T, S, Option<i64>), +) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for XCap

source§

impl StructuralPartialEq for XCap

Auto Trait Implementations§

§

impl !Freeze for XCap

§

impl RefUnwindSafe for XCap

§

impl Send for XCap

§

impl Sync for XCap

§

impl Unpin for XCap

§

impl UnwindSafe for XCap

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.XPendingArgs.html b/doc/fred/types/struct.XPendingArgs.html new file mode 100644 index 00000000..0e219658 --- /dev/null +++ b/doc/fred/types/struct.XPendingArgs.html @@ -0,0 +1,51 @@ +XPendingArgs in fred::types - Rust

Struct fred::types::XPendingArgs

source ·
pub struct XPendingArgs {
+    pub idle: Option<u64>,
+    pub start: Option<XID>,
+    pub end: Option<XID>,
+    pub count: Option<u64>,
+    pub consumer: Option<Str>,
+}
Available on crate feature i-streams only.
Expand description

A struct representing the trailing optional arguments to XPENDING.

+

See the From implementations for various shorthand representations of these arguments. Callers should use () +to represent no arguments.

+

Fields§

§idle: Option<u64>§start: Option<XID>§end: Option<XID>§count: Option<u64>§consumer: Option<Str>

Trait Implementations§

source§

impl Clone for XPendingArgs

source§

fn clone(&self) -> XPendingArgs

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for XPendingArgs

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<()> for XPendingArgs

source§

fn from(_: ()) -> Self

Converts to this type from the input type.
source§

impl<S, E> From<(S, E, u64)> for XPendingArgs
where + S: Into<XID>, + E: Into<XID>,

source§

fn from((start, end, count): (S, E, u64)) -> Self

Converts to this type from the input type.
source§

impl<S, E, C> From<(S, E, u64, C)> for XPendingArgs
where + S: Into<XID>, + E: Into<XID>, + C: Into<Str>,

source§

fn from((start, end, count, consumer): (S, E, u64, C)) -> Self

Converts to this type from the input type.
source§

impl<S, E> From<(u64, S, E, u64)> for XPendingArgs
where + S: Into<XID>, + E: Into<XID>,

source§

fn from((idle, start, end, count): (u64, S, E, u64)) -> Self

Converts to this type from the input type.
source§

impl<S, E, C> From<(u64, S, E, u64, C)> for XPendingArgs
where + S: Into<XID>, + E: Into<XID>, + C: Into<Str>,

source§

fn from((idle, start, end, count, consumer): (u64, S, E, u64, C)) -> Self

Converts to this type from the input type.
source§

impl PartialEq for XPendingArgs

source§

fn eq(&self, other: &XPendingArgs) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
source§

impl Eq for XPendingArgs

source§

impl StructuralPartialEq for XPendingArgs

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.ZRange.html b/doc/fred/types/struct.ZRange.html new file mode 100644 index 00000000..5694a31f --- /dev/null +++ b/doc/fred/types/struct.ZRange.html @@ -0,0 +1,35 @@ +ZRange in fred::types - Rust

Struct fred::types::ZRange

source ·
pub struct ZRange {
+    pub kind: ZRangeKind,
+    pub range: ZRangeBound,
+}
Available on crate feature i-sorted-sets only.
Expand description

A wrapper struct for a range bound in a sorted set command.

+

Fields§

§kind: ZRangeKind§range: ZRangeBound

Trait Implementations§

source§

impl Clone for ZRange

source§

fn clone(&self) -> ZRange

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ZRange

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'a> From<&'a String> for ZRange

source§

fn from(s: &'a String) -> Self

Converts to this type from the input type.
source§

impl<'a> From<&'a ZRange> for ZRange

source§

fn from(range: &'a ZRange) -> Self

Converts to this type from the input type.
source§

impl<'a> From<&'a str> for ZRange

source§

fn from(s: &'a str) -> Self

Converts to this type from the input type.
source§

impl From<String> for ZRange

source§

fn from(s: String) -> Self

Converts to this type from the input type.
source§

impl From<i64> for ZRange

source§

fn from(i: i64) -> Self

Converts to this type from the input type.
source§

impl TryFrom<f64> for ZRange

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(f: f64) -> Result<Self, Self::Error>

Performs the conversion.

Auto Trait Implementations§

§

impl Freeze for ZRange

§

impl RefUnwindSafe for ZRange

§

impl Send for ZRange

§

impl Sync for ZRange

§

impl Unpin for ZRange

§

impl UnwindSafe for ZRange

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where + T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/struct.ZScanResult.html b/doc/fred/types/struct.ZScanResult.html new file mode 100644 index 00000000..7575ff6a --- /dev/null +++ b/doc/fred/types/struct.ZScanResult.html @@ -0,0 +1,33 @@ +ZScanResult in fred::types - Rust

Struct fred::types::ZScanResult

source ·
pub struct ZScanResult { /* private fields */ }
Expand description

The result of a ZSCAN operation.

+

Trait Implementations§

source§

impl Scanner for ZScanResult

§

type Page = Vec<(RedisValue, f64)>

The type of results from the scan operation.
source§

fn cursor(&self) -> Option<Cow<'_, str>>

Read the cursor returned from the last scan operation.
source§

fn has_more(&self) -> bool

Whether the scan call will continue returning results. If false this will be the last result set +returned on the stream. Read more
source§

fn results(&self) -> &Option<Self::Page>

Return a reference to the last page of results.
source§

fn take_results(&mut self) -> Option<Self::Page>

Take ownership over the results of the SCAN operation. Calls to results or take_results will return None +afterwards.
source§

fn create_client(&self) -> RedisClient

A lightweight function to create a Redis client from the SCAN result. Read more
source§

fn next(self) -> Result<(), RedisError>

Move on to the next page of results from the SCAN operation. If no more results are available this may close the +stream. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an +Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +WithDispatch wrapper. Read more
\ No newline at end of file diff --git a/doc/fred/types/timeseries/enum.Aggregator.html b/doc/fred/types/timeseries/enum.Aggregator.html new file mode 100644 index 00000000..c9411fa2 --- /dev/null +++ b/doc/fred/types/timeseries/enum.Aggregator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.Aggregator.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/timeseries/enum.BucketTimestamp.html b/doc/fred/types/timeseries/enum.BucketTimestamp.html new file mode 100644 index 00000000..ad5f28ba --- /dev/null +++ b/doc/fred/types/timeseries/enum.BucketTimestamp.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.BucketTimestamp.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/timeseries/enum.DuplicatePolicy.html b/doc/fred/types/timeseries/enum.DuplicatePolicy.html new file mode 100644 index 00000000..52741c6b --- /dev/null +++ b/doc/fred/types/timeseries/enum.DuplicatePolicy.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.DuplicatePolicy.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/timeseries/enum.Encoding.html b/doc/fred/types/timeseries/enum.Encoding.html new file mode 100644 index 00000000..95c78e76 --- /dev/null +++ b/doc/fred/types/timeseries/enum.Encoding.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.Encoding.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/timeseries/enum.GetLabels.html b/doc/fred/types/timeseries/enum.GetLabels.html new file mode 100644 index 00000000..528643ea --- /dev/null +++ b/doc/fred/types/timeseries/enum.GetLabels.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.GetLabels.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/timeseries/enum.GetTimestamp.html b/doc/fred/types/timeseries/enum.GetTimestamp.html new file mode 100644 index 00000000..555ff808 --- /dev/null +++ b/doc/fred/types/timeseries/enum.GetTimestamp.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.GetTimestamp.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/timeseries/enum.Reducer.html b/doc/fred/types/timeseries/enum.Reducer.html new file mode 100644 index 00000000..c52817cc --- /dev/null +++ b/doc/fred/types/timeseries/enum.Reducer.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.Reducer.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/timeseries/enum.Timestamp.html b/doc/fred/types/timeseries/enum.Timestamp.html new file mode 100644 index 00000000..6adf830a --- /dev/null +++ b/doc/fred/types/timeseries/enum.Timestamp.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/enum.Timestamp.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/timeseries/struct.GroupBy.html b/doc/fred/types/timeseries/struct.GroupBy.html new file mode 100644 index 00000000..9165e71a --- /dev/null +++ b/doc/fred/types/timeseries/struct.GroupBy.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.GroupBy.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/timeseries/struct.RangeAggregation.html b/doc/fred/types/timeseries/struct.RangeAggregation.html new file mode 100644 index 00000000..6723721e --- /dev/null +++ b/doc/fred/types/timeseries/struct.RangeAggregation.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/struct.RangeAggregation.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/timeseries/type.Resp2TimeSeriesValues.html b/doc/fred/types/timeseries/type.Resp2TimeSeriesValues.html new file mode 100644 index 00000000..e1cf465f --- /dev/null +++ b/doc/fred/types/timeseries/type.Resp2TimeSeriesValues.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/type.Resp2TimeSeriesValues.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/timeseries/type.Resp3TimeSeriesValues.html b/doc/fred/types/timeseries/type.Resp3TimeSeriesValues.html new file mode 100644 index 00000000..04150733 --- /dev/null +++ b/doc/fred/types/timeseries/type.Resp3TimeSeriesValues.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../fred/types/type.Resp3TimeSeriesValues.html...

+ + + \ No newline at end of file diff --git a/doc/fred/types/trait.FromRedis.html b/doc/fred/types/trait.FromRedis.html new file mode 100644 index 00000000..e2b42c38 --- /dev/null +++ b/doc/fred/types/trait.FromRedis.html @@ -0,0 +1,54 @@ +FromRedis in fred::types - Rust

Trait fred::types::FromRedis

source ·
pub trait FromRedis: Sized {
+    // Required method
+    fn from_value(value: RedisValue) -> Result<Self, RedisError>;
+}
Expand description

A trait used to convert various forms of RedisValue into different types.

+

§Examples

+
let foo: usize = RedisValue::String("123".into()).convert()?;
+let foo: i64 = RedisValue::String("123".into()).convert()?;
+let foo: String = RedisValue::String("123".into()).convert()?;
+let foo: Vec<u8> = RedisValue::Bytes(vec![102, 111, 111].into()).convert()?;
+let foo: Vec<u8> = RedisValue::String("foo".into()).convert()?;
+let foo: Vec<String> = RedisValue::Array(vec!["a".into(), "b".into()]).convert()?;
+let foo: HashMap<String, u16> =
+  RedisValue::Array(vec!["a".into(), 1.into(), "b".into(), 2.into()]).convert()?;
+let foo: (String, i64) = RedisValue::Array(vec!["a".into(), 1.into()]).convert()?;
+let foo: Vec<(String, i64)> =
+  RedisValue::Array(vec!["a".into(), 1.into(), "b".into(), 2.into()]).convert()?;
+// ...
+

§Bulk Values

+

This interface can also convert single-element vectors to scalar values in certain scenarios. This is often +useful with commands that conditionally return bulk values, or where the number of elements in the response +depends on the number of arguments (MGET, etc).

+

For example:

+ +
let _: String = RedisValue::Array(vec![]).convert()?; // error
+let _: String = RedisValue::Array(vec!["a".into()]).convert()?; // "a"
+let _: String = RedisValue::Array(vec!["a".into(), "b".into()]).convert()?; // error
+let _: Option<String> = RedisValue::Array(vec![]).convert()?; // None
+let _: Option<String> = RedisValue::Array(vec!["a".into()]).convert()?; // Some("a")
+let _: Option<String> = RedisValue::Array(vec!["a".into(), "b".into()]).convert()?; // error
+

§The default-nil-types Feature Flag

+

By default a nil value cannot be converted directly into any of the scalar types (u8, String, Bytes, +etc). In practice this often requires callers to use an Option or Vec container with commands that can return +nil.

+

The default-nil-types feature flag can enable some further type conversion branches that treat nil values as +default values for the relevant type. For RedisValue::Null these include:

+
    +
  • impl FromRedis for String or Str returns an empty string.
  • +
  • impl FromRedis for Bytes or Vec<T> returns an empty array.
  • +
  • impl FromRedis for any integer or float type returns 0
  • +
  • impl FromRedis for bool returns false
  • +
  • impl FromRedis for map or set types return an empty map or set.
  • +
+

Required Methods§

Object Safety§

This trait is not object safe.

Implementations on Foreign Types§

source§

impl FromRedis for Value

Available on crate feature serde-json only.
source§

impl FromRedis for bool

source§

impl FromRedis for f32

source§

impl FromRedis for f64

source§

impl FromRedis for i8

source§

impl FromRedis for i16

source§

impl FromRedis for i32

source§

impl FromRedis for i64

source§

impl FromRedis for i128

source§

impl FromRedis for isize

source§

impl FromRedis for u8

source§

impl FromRedis for u16

source§

impl FromRedis for u32

source§

impl FromRedis for u64

source§

impl FromRedis for u128

source§

impl FromRedis for ()

source§

impl FromRedis for usize

source§

impl FromRedis for String

source§

impl FromRedis for Bytes

source§

impl FromRedis for Str

source§

impl<K, V> FromRedis for BTreeMap<K, V>
where + K: FromRedisKey + Ord, + V: FromRedis,

source§

impl<K, V, S> FromRedis for HashMap<K, V, S>
where + K: FromRedisKey + Eq + Hash, + V: FromRedis, + S: BuildHasher + Default,

source§

impl<T> FromRedis for Option<T>
where + T: FromRedis,

source§

impl<T> FromRedis for Vec<T>
where + T: FromRedis,

source§

impl<T, const N: usize> FromRedis for [T; N]
where + T: FromRedis,

source§

impl<V> FromRedis for BTreeSet<V>
where + V: FromRedis + Ord,

source§

impl<V, S> FromRedis for HashSet<V, S>
where + V: FromRedis + Hash + Eq, + S: BuildHasher + Default,

Implementors§

source§

impl FromRedis for RedisValue

source§

impl FromRedis for ClusterInfo

Available on crate feature i-cluster only.
source§

impl FromRedis for DatabaseMemoryStats

Available on crate feature i-memory only.
source§

impl FromRedis for GeoPosition

Available on crate feature i-geo only.
source§

impl FromRedis for MemoryStats

Available on crate feature i-memory only.
source§

impl FromRedis for RedisKey

source§

impl FromRedis for SlowlogEntry

Available on crate feature i-slowlog only.
\ No newline at end of file diff --git a/doc/fred/types/trait.FromRedisKey.html b/doc/fred/types/trait.FromRedisKey.html new file mode 100644 index 00000000..813465d8 --- /dev/null +++ b/doc/fred/types/trait.FromRedisKey.html @@ -0,0 +1,6 @@ +FromRedisKey in fred::types - Rust

Trait fred::types::FromRedisKey

source ·
pub trait FromRedisKey: Sized {
+    // Required method
+    fn from_key(value: RedisKey) -> Result<Self, RedisError>;
+}
Expand description

A trait used to convert RedisKey values to various types.

+

See the convert documentation for more information.

+

Required Methods§

Object Safety§

This trait is not object safe.

Implementations on Foreign Types§

source§

impl FromRedisKey for f32

source§

impl FromRedisKey for f64

source§

impl FromRedisKey for i8

source§

impl FromRedisKey for i16

source§

impl FromRedisKey for i32

source§

impl FromRedisKey for i64

source§

impl FromRedisKey for i128

source§

impl FromRedisKey for isize

source§

impl FromRedisKey for u8

source§

impl FromRedisKey for u16

source§

impl FromRedisKey for u32

source§

impl FromRedisKey for u64

source§

impl FromRedisKey for u128

source§

impl FromRedisKey for ()

source§

impl FromRedisKey for usize

source§

impl FromRedisKey for String

source§

impl FromRedisKey for Vec<u8>

source§

impl FromRedisKey for Bytes

source§

impl FromRedisKey for Str

Implementors§

\ No newline at end of file diff --git a/doc/fred/types/trait.HostMapping.html b/doc/fred/types/trait.HostMapping.html new file mode 100644 index 00000000..32a49bc6 --- /dev/null +++ b/doc/fred/types/trait.HostMapping.html @@ -0,0 +1,11 @@ +HostMapping in fred::types - Rust

Trait fred::types::HostMapping

source ·
pub trait HostMapping:
+    Send
+    + Sync
+    + Debug {
+    // Required method
+    fn map(&self, ip: &IpAddr, default_host: &str) -> Option<String>;
+}
Available on crate features enable-rustls or enable-native-tls or enable-rustls-ring only.
Expand description

A trait used for mapping IP addresses to hostnames when processing the CLUSTER SLOTS response.

+

Required Methods§

source

fn map(&self, ip: &IpAddr, default_host: &str) -> Option<String>

Map the provided IP address to a hostname that should be used during the TLS handshake.

+

The default_host argument represents the hostname of the node that returned the CLUSTER SLOTS response.

+

If None is returned the client will use the IP address as the server name during the TLS handshake.

+

Implementors§

\ No newline at end of file diff --git a/doc/fred/types/trait.ReplicaFilter.html b/doc/fred/types/trait.ReplicaFilter.html new file mode 100644 index 00000000..f2b34fc5 --- /dev/null +++ b/doc/fred/types/trait.ReplicaFilter.html @@ -0,0 +1,25 @@ +ReplicaFilter in fred::types - Rust

Trait fred::types::ReplicaFilter

source ·
pub trait ReplicaFilter:
+    Send
+    + Sync
+    + 'static {
+    // Provided method
+    fn filter<'life0, 'life1, 'life2, 'async_trait>(
+        &'life0 self,
+        primary: &'life1 Server,
+        replica: &'life2 Server,
+    ) -> Pin<Box<dyn Future<Output = bool> + Send + 'async_trait>>
+       where Self: 'async_trait,
+             'life0: 'async_trait,
+             'life1: 'async_trait,
+             'life2: 'async_trait { ... }
+}
Available on crate feature replicas only.
Expand description

An interface used to filter the list of available replica nodes.

+

Provided Methods§

source

fn filter<'life0, 'life1, 'life2, 'async_trait>( + &'life0 self, + primary: &'life1 Server, + replica: &'life2 Server, +) -> Pin<Box<dyn Future<Output = bool> + Send + 'async_trait>>
where + Self: 'async_trait, + 'life0: 'async_trait, + 'life1: 'async_trait, + 'life2: 'async_trait,

Returns whether the replica node mapping can be used when routing commands to replicas.

+

Implementors§

\ No newline at end of file diff --git a/doc/fred/types/trait.Resolve.html b/doc/fred/types/trait.Resolve.html new file mode 100644 index 00000000..141352ff --- /dev/null +++ b/doc/fred/types/trait.Resolve.html @@ -0,0 +1,19 @@ +Resolve in fred::types - Rust

Trait fred::types::Resolve

source ·
pub trait Resolve: 'static {
+    // Required method
+    fn resolve<'life0, 'async_trait>(
+        &'life0 self,
+        host: Str,
+        port: u16,
+    ) -> Pin<Box<dyn Future<Output = RedisResult<Vec<SocketAddr>>> + 'async_trait>>
+       where Self: 'async_trait,
+             'life0: 'async_trait;
+}
Available on crate feature dns only.
Expand description

A trait that can be used to override DNS resolution logic.

+

Note: currently this requires async-trait.

+

Required Methods§

source

fn resolve<'life0, 'async_trait>( + &'life0 self, + host: Str, + port: u16, +) -> Pin<Box<dyn Future<Output = RedisResult<Vec<SocketAddr>>> + 'async_trait>>
where + Self: 'async_trait, + 'life0: 'async_trait,

Resolve a hostname.

+

Implementors§

\ No newline at end of file diff --git a/doc/fred/types/trait.Scanner.html b/doc/fred/types/trait.Scanner.html new file mode 100644 index 00000000..c731bd29 --- /dev/null +++ b/doc/fred/types/trait.Scanner.html @@ -0,0 +1,32 @@ +Scanner in fred::types - Rust

Trait fred::types::Scanner

source ·
pub trait Scanner {
+    type Page;
+
+    // Required methods
+    fn cursor(&self) -> Option<Cow<'_, str>>;
+    fn has_more(&self) -> bool;
+    fn results(&self) -> &Option<Self::Page>;
+    fn take_results(&mut self) -> Option<Self::Page>;
+    fn create_client(&self) -> RedisClient;
+    fn next(self) -> Result<(), RedisError>;
+}
Expand description

An interface for interacting with the results of a scan operation.

+

Required Associated Types§

source

type Page

The type of results from the scan operation.

+

Required Methods§

source

fn cursor(&self) -> Option<Cow<'_, str>>

Read the cursor returned from the last scan operation.

+
source

fn has_more(&self) -> bool

Whether the scan call will continue returning results. If false this will be the last result set +returned on the stream.

+

Calling next when this returns false will return Ok(()), so this does not need to be checked on each +result.

+
source

fn results(&self) -> &Option<Self::Page>

Return a reference to the last page of results.

+
source

fn take_results(&mut self) -> Option<Self::Page>

Take ownership over the results of the SCAN operation. Calls to results or take_results will return None +afterwards.

+
source

fn create_client(&self) -> RedisClient

A lightweight function to create a Redis client from the SCAN result.

+

To continue scanning the caller should call next on this struct. Calling scan again on the client will +initiate a new SCAN call starting with a cursor of 0.

+
source

fn next(self) -> Result<(), RedisError>

Move on to the next page of results from the SCAN operation. If no more results are available this may close the +stream.

+

This must be called to continue scanning the keyspace. Results are not automatically scanned in the +background since this could cause the buffer backing the stream to grow too large very quickly. This +interface provides a mechanism for throttling the throughput of the SCAN call. If this struct is dropped +without calling this function the stream will close without an error.

+

If this function returns an error the scan call cannot continue as the client has been closed, or some other +fatal error has occurred. If this happens the error will appear in the stream from the original SCAN call.

+

Implementors§

\ No newline at end of file diff --git a/doc/fred/types/type.Any.html b/doc/fred/types/type.Any.html new file mode 100644 index 00000000..3f17d7a4 --- /dev/null +++ b/doc/fred/types/type.Any.html @@ -0,0 +1,2 @@ +Any in fred::types - Rust

Type Alias fred::types::Any

source ·
pub type Any = bool;
Expand description

The ANY flag used on certain GEO commands.

+
\ No newline at end of file diff --git a/doc/fred/types/type.ConnectHandle.html b/doc/fred/types/type.ConnectHandle.html new file mode 100644 index 00000000..143f46e7 --- /dev/null +++ b/doc/fred/types/type.ConnectHandle.html @@ -0,0 +1,2 @@ +ConnectHandle in fred::types - Rust

Type Alias fred::types::ConnectHandle

source ·
pub type ConnectHandle = JoinHandle<Result<(), RedisError>>;
Expand description

The result from any of the connect functions showing the error that closed the connection, if any.

+

Aliased Type§

struct ConnectHandle { /* private fields */ }
\ No newline at end of file diff --git a/doc/fred/types/type.Limit.html b/doc/fred/types/type.Limit.html new file mode 100644 index 00000000..aba6fa77 --- /dev/null +++ b/doc/fred/types/type.Limit.html @@ -0,0 +1,2 @@ +Limit in fred::types - Rust

Type Alias fred::types::Limit

source ·
pub type Limit = (i64, i64);
Expand description

A tuple of (offset, count) values for commands that allow paging through results.

+
\ No newline at end of file diff --git a/doc/fred/types/type.LimitCount.html b/doc/fred/types/type.LimitCount.html new file mode 100644 index 00000000..28994606 --- /dev/null +++ b/doc/fred/types/type.LimitCount.html @@ -0,0 +1,7 @@ +LimitCount in fred::types - Rust

Type Alias fred::types::LimitCount

source ·
pub type LimitCount = Option<i64>;
Expand description

An argument type equivalent to “[LIMIT count]”.

+

Aliased Type§

enum LimitCount {
+    None,
+    Some(i64),
+}

Variants§

§1.0.0

None

No value.

+
§1.0.0

Some(i64)

Some value of type T.

+
\ No newline at end of file diff --git a/doc/fred/types/type.MultipleStrings.html b/doc/fred/types/type.MultipleStrings.html new file mode 100644 index 00000000..eaffddf4 --- /dev/null +++ b/doc/fred/types/type.MultipleStrings.html @@ -0,0 +1,2 @@ +MultipleStrings in fred::types - Rust

Type Alias fred::types::MultipleStrings

source ·
pub type MultipleStrings = MultipleKeys;
Expand description

Convenience interface for commands that take 1 or more strings.

+

Aliased Type§

struct MultipleStrings { /* private fields */ }
\ No newline at end of file diff --git a/doc/fred/types/type.MultipleValues.html b/doc/fred/types/type.MultipleValues.html new file mode 100644 index 00000000..8541a136 --- /dev/null +++ b/doc/fred/types/type.MultipleValues.html @@ -0,0 +1,22 @@ +MultipleValues in fred::types - Rust

Type Alias fred::types::MultipleValues

source ·
pub type MultipleValues = RedisValue;
Expand description

Convenience interface for commands that take 1 or more values.

+

Aliased Type§

enum MultipleValues {
+    Boolean(bool),
+    Integer(i64),
+    Double(f64),
+    String(StrInner<Bytes>),
+    Bytes(Bytes),
+    Null,
+    Queued,
+    Map(RedisMap),
+    Array(Vec<RedisValue>),
+}

Variants§

§

Boolean(bool)

A boolean value.

+
§

Integer(i64)

An integer value.

+
§

Double(f64)

A double floating point number.

+
§

String(StrInner<Bytes>)

A string value.

+
§

Bytes(Bytes)

A byte array value.

+
§

Null

A nil value.

+
§

Queued

A special value used to indicate a MULTI block command was received by the server.

+
§

Map(RedisMap)

A map of key/value pairs, primarily used in RESP3 mode.

+
§

Array(Vec<RedisValue>)

An ordered list of values.

+

In RESP2 mode the server usually sends map structures as an array of key/value pairs.

+
\ No newline at end of file diff --git a/doc/fred/types/type.Resp2TimeSeriesValues.html b/doc/fred/types/type.Resp2TimeSeriesValues.html new file mode 100644 index 00000000..5ad28583 --- /dev/null +++ b/doc/fred/types/type.Resp2TimeSeriesValues.html @@ -0,0 +1,51 @@ +Resp2TimeSeriesValues in fred::types - Rust

Type Alias fred::types::Resp2TimeSeriesValues

source ·
pub type Resp2TimeSeriesValues<K, Lk, Lv> = Vec<(K, Vec<(Lk, Lv)>, Vec<(i64, f64)>)>;
Available on crate feature i-time-series only.
Expand description

Shorthand for the result of commands such as MGET, MRANGE, etc.

+
    +
  • K - The key type, usually a RedisKey, Str, or String.
  • +
  • Lk - The label key type, usually a Str or String.
  • +
  • Lv - The label value type, often some kind of string type.
  • +
+

The fastest/cheapest option is usually TimeseriesValues<RedisKey, Str, Str>.

+ +
async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  assert_eq!(client.protocol_version(), RespVersion::RESP2);
+
+  client
+    .ts_add("foo", "*", 1.1, None, None, None, None, ("a", "b"))
+    .await?;
+  sleep(Duration::from_millis(5)).await;
+  client
+    .ts_add("foo", "*", 2.2, None, None, None, None, ("a", "b"))
+    .await?;
+  sleep(Duration::from_millis(5)).await;
+  client
+    .ts_add("bar", "*", 3.3, None, None, None, None, ("a", "b"))
+    .await?;
+  sleep(Duration::from_millis(5)).await;
+  client
+    .ts_add("bar", "*", 4.4, None, None, None, None, ("a", "b"))
+    .await?;
+
+  let ranges: Resp2TimeSeriesValues<RedisKey, Str, Str> = client
+    .ts_mrange(
+      "-",
+      "+",
+      true,
+      [],
+      None,
+      Some(GetLabels::WithLabels),
+      None,
+      None,
+      ["a=b"],
+      None,
+    )
+    .await?;
+
+  for (key, labels, values) in ranges.into_iter() {
+    println!("{} [{:?}] {:?}", key.as_str_lossy(), labels, values);
+  }
+  // bar [[("a", "b")]] [(1705355605510, 3.3), (1705355605517, 4.4)]
+  // foo [[("a", "b")]] [(1705355605498, 1.1), (1705355605504, 2.2)]
+  Ok(())
+}
+

See Resp3TimeSeriesValues for the RESP3 equivalent.

+

Aliased Type§

struct Resp2TimeSeriesValues<K, Lk, Lv> { /* private fields */ }
\ No newline at end of file diff --git a/doc/fred/types/type.Resp3TimeSeriesValues.html b/doc/fred/types/type.Resp3TimeSeriesValues.html new file mode 100644 index 00000000..6c48443a --- /dev/null +++ b/doc/fred/types/type.Resp3TimeSeriesValues.html @@ -0,0 +1,34 @@ +Resp3TimeSeriesValues in fred::types - Rust

Type Alias fred::types::Resp3TimeSeriesValues

source ·
pub type Resp3TimeSeriesValues<K, Lk, Lv> = HashMap<K, (Vec<(Lk, Lv)>, Vec<(i64, f64)>)>;
Available on crate feature i-time-series only.
Expand description

The RESP3 equivalent of Resp2TimeSeriesValues.

+

The timeseries interface uses slightly different type signatures in RESP3 mode.

+ +
async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  assert_eq!(client.protocol_version(), RespVersion::RESP3);
+
+  client
+    .ts_add("foo", "*", 1.1, None, None, None, None, ("a", "b"))
+    .await?;
+  sleep(Duration::from_millis(5)).await;
+  client
+    .ts_add("foo", "*", 2.2, None, None, None, None, ("a", "b"))
+    .await?;
+  sleep(Duration::from_millis(5)).await;
+  client
+    .ts_add("bar", "*", 3.3, None, None, None, None, ("a", "b"))
+    .await?;
+  sleep(Duration::from_millis(5)).await;
+  client
+    .ts_add("bar", "*", 4.4, None, None, None, None, ("a", "b"))
+    .await?;
+
+  let ranges: Resp3TimeSeriesValues<RedisKey, Str, Str> = client
+    .ts_mget(false, Some(GetLabels::WithLabels), ["a=b"])
+    .await?;
+
+  for (key, (labels, values)) in ranges.into_iter() {
+    println!("{} [{:?}] {:?}", key.as_str_lossy(), labels, values);
+  }
+  // bar [[("a", "b")]] [(1705355605517, 4.4)]
+  // foo [[("a", "b")]] [(1705355605504, 2.2)]
+  Ok(())
+}
+

Aliased Type§

struct Resp3TimeSeriesValues<K, Lk, Lv> { /* private fields */ }
\ No newline at end of file diff --git a/doc/fred/types/type.XReadResponse.html b/doc/fred/types/type.XReadResponse.html new file mode 100644 index 00000000..64b864be --- /dev/null +++ b/doc/fred/types/type.XReadResponse.html @@ -0,0 +1,12 @@ +XReadResponse in fred::types - Rust

Type Alias fred::types::XReadResponse

source ·
pub type XReadResponse<K1, I, K2, V> = HashMap<K1, Vec<XReadValue<I, K2, V>>>;
Available on crate feature i-streams only.
Expand description

A generic helper type describing the top level response from XREAD or XREADGROUP.

+

See the xread documentation for more information.

+

The inner type declarations refer to the following:

+
    +
  • K1 - The type of the outer Redis key for the stream. Usually a String or RedisKey.
  • +
  • I - The type of the ID for a stream record (“abc-123”). This is usually a String.
  • +
  • K2 - The type of key in the map associated with each stream record.
  • +
  • V - The type of value in the map associated with each stream record.
  • +
+

To support heterogeneous values in the map describing each stream element it is recommended to declare the last +type as RedisValue and convert as needed.

+

Aliased Type§

struct XReadResponse<K1, I, K2, V> { /* private fields */ }
\ No newline at end of file diff --git a/doc/fred/types/type.XReadValue.html b/doc/fred/types/type.XReadValue.html new file mode 100644 index 00000000..0dc24381 --- /dev/null +++ b/doc/fred/types/type.XReadValue.html @@ -0,0 +1,3 @@ +XReadValue in fred::types - Rust

Type Alias fred::types::XReadValue

source ·
pub type XReadValue<I, K, V> = (I, HashMap<K, V>);
Available on crate feature i-streams only.
Expand description

A generic helper type describing the ID and associated map for each record in a stream.

+

See the XReadResponse type for more information.

+
\ No newline at end of file diff --git a/doc/fred/util/constant.NONE.html b/doc/fred/util/constant.NONE.html new file mode 100644 index 00000000..c95b3f2a --- /dev/null +++ b/doc/fred/util/constant.NONE.html @@ -0,0 +1,4 @@ +NONE in fred::util - Rust

Constant fred::util::NONE

source ·
pub const NONE: Option<String>;
Expand description

A convenience constant for None values used as generic arguments.

+

Functions that take Option<T> as an argument often require the caller to use a turbofish when the +variant is None. In many cases this constant can be used instead.

+
\ No newline at end of file diff --git a/doc/fred/util/fn.f64_to_redis_string.html b/doc/fred/util/fn.f64_to_redis_string.html new file mode 100644 index 00000000..273b53b1 --- /dev/null +++ b/doc/fred/util/fn.f64_to_redis_string.html @@ -0,0 +1,2 @@ +f64_to_redis_string in fred::util - Rust

Function fred::util::f64_to_redis_string

source ·
pub fn f64_to_redis_string(d: f64) -> Result<RedisValue, RedisError>
Expand description

Convert an f64 to a redis string, supporting “+inf” and “-inf”.

+
\ No newline at end of file diff --git a/doc/fred/util/fn.group_by_hash_slot.html b/doc/fred/util/fn.group_by_hash_slot.html new file mode 100644 index 00000000..bcc423cd --- /dev/null +++ b/doc/fred/util/fn.group_by_hash_slot.html @@ -0,0 +1,19 @@ +group_by_hash_slot in fred::util - Rust

Function fred::util::group_by_hash_slot

source ·
pub fn group_by_hash_slot<T>(
+    args: impl IntoIterator<Item = T>,
+) -> Result<BTreeMap<u16, VecDeque<RedisKey>>, RedisError>
where + T: TryInto<RedisKey>, + T::Error: Into<RedisError>,
Expand description

Group the provided arguments by their cluster hash slot.

+

This can be useful with commands that require all keys map to the same hash slot, such as SSUBSCRIBE, +MGET, etc.

+ +
async fn example(client: impl KeysInterface) -> Result<(), RedisError> {
+  let keys = vec!["foo", "bar", "baz", "a{1}", "b{1}", "c{1}"];
+  let groups = fred::util::group_by_hash_slot(keys)?;
+
+  for (slot, keys) in groups.into_iter() {
+    // `MGET` requires that all arguments map to the same hash slot
+    println!("{:?}", client.mget::<Vec<String>, _>(keys).await?);
+  }
+  Ok(())
+}
+
\ No newline at end of file diff --git a/doc/fred/util/fn.redis_keyslot.html b/doc/fred/util/fn.redis_keyslot.html new file mode 100644 index 00000000..083bdf68 --- /dev/null +++ b/doc/fred/util/fn.redis_keyslot.html @@ -0,0 +1,7 @@ +redis_keyslot in fred::util - Rust

Function fred::util::redis_keyslot

pub fn redis_keyslot(key: &[u8]) -> u16
Expand description

Map a key to the corresponding cluster key slot.

+ +
$ redis-cli cluster keyslot "8xjx7vWrfPq54mKfFD3Y1CcjjofpnAcQ"
+(integer) 5458
+ +
assert_eq!(redis_keyslot(b"8xjx7vWrfPq54mKfFD3Y1CcjjofpnAcQ"), 5458);
+
\ No newline at end of file diff --git a/doc/fred/util/fn.redis_string_to_f64.html b/doc/fred/util/fn.redis_string_to_f64.html new file mode 100644 index 00000000..3c687dd1 --- /dev/null +++ b/doc/fred/util/fn.redis_string_to_f64.html @@ -0,0 +1,2 @@ +redis_string_to_f64 in fred::util - Rust

Function fred::util::redis_string_to_f64

source ·
pub fn redis_string_to_f64(s: &str) -> Result<f64, RedisError>
Expand description

Convert a redis string to an f64, supporting “+inf” and “-inf”.

+
\ No newline at end of file diff --git a/doc/fred/util/fn.sha1_hash.html b/doc/fred/util/fn.sha1_hash.html new file mode 100644 index 00000000..21b968d0 --- /dev/null +++ b/doc/fred/util/fn.sha1_hash.html @@ -0,0 +1,3 @@ +sha1_hash in fred::util - Rust

Function fred::util::sha1_hash

source ·
pub fn sha1_hash(input: &str) -> String
Available on crate feature sha-1 only.
Expand description

Calculate the SHA1 hash output as a hex string. This is provided for clients that use the Lua interface to +manage their own script caches.

+
\ No newline at end of file diff --git a/doc/fred/util/fn.static_bytes.html b/doc/fred/util/fn.static_bytes.html new file mode 100644 index 00000000..e5106016 --- /dev/null +++ b/doc/fred/util/fn.static_bytes.html @@ -0,0 +1,2 @@ +static_bytes in fred::util - Rust

Function fred::util::static_bytes

source ·
pub fn static_bytes(b: &'static [u8]) -> Bytes
Expand description

Create a Bytes from static bytes without copying.

+
\ No newline at end of file diff --git a/doc/fred/util/fn.static_str.html b/doc/fred/util/fn.static_str.html new file mode 100644 index 00000000..fcede14f --- /dev/null +++ b/doc/fred/util/fn.static_str.html @@ -0,0 +1,2 @@ +static_str in fred::util - Rust

Function fred::util::static_str

source ·
pub const fn static_str(s: &'static str) -> Str
Expand description

Create a Str from a static str slice without copying.

+
\ No newline at end of file diff --git a/doc/fred/util/index.html b/doc/fred/util/index.html new file mode 100644 index 00000000..fdcdc331 --- /dev/null +++ b/doc/fred/util/index.html @@ -0,0 +1,3 @@ +fred::util - Rust

Module fred::util

source ·
Expand description

Various client utility functions.

+

Constants§

  • A convenience constant for None values used as generic arguments.

Functions§

  • Convert an f64 to a redis string, supporting “+inf” and “-inf”.
  • Group the provided arguments by their cluster hash slot.
  • Map a key to the corresponding cluster key slot.
  • Convert a redis string to an f64, supporting “+inf” and “-inf”.
  • Calculate the SHA1 hash output as a hex string. This is provided for clients that use the Lua interface to +manage their own script caches.
  • Create a Bytes from static bytes without copying.
  • Create a Str from a static str slice without copying.
\ No newline at end of file diff --git a/doc/fred/util/sidebar-items.js b/doc/fred/util/sidebar-items.js new file mode 100644 index 00000000..85eec246 --- /dev/null +++ b/doc/fred/util/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"constant":["NONE"],"fn":["f64_to_redis_string","group_by_hash_slot","redis_keyslot","redis_string_to_f64","sha1_hash","static_bytes","static_str"]}; \ No newline at end of file diff --git a/doc/fred/utils/fn.f64_to_redis_string.html b/doc/fred/utils/fn.f64_to_redis_string.html new file mode 100644 index 00000000..73d269d1 --- /dev/null +++ b/doc/fred/utils/fn.f64_to_redis_string.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../fred/util/fn.f64_to_redis_string.html...

+ + + \ No newline at end of file diff --git a/doc/fred/utils/fn.redis_string_to_f64.html b/doc/fred/utils/fn.redis_string_to_f64.html new file mode 100644 index 00000000..47c2efb0 --- /dev/null +++ b/doc/fred/utils/fn.redis_string_to_f64.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../fred/util/fn.redis_string_to_f64.html...

+ + + \ No newline at end of file diff --git a/doc/fred/utils/fn.static_bytes.html b/doc/fred/utils/fn.static_bytes.html new file mode 100644 index 00000000..1f73010d --- /dev/null +++ b/doc/fred/utils/fn.static_bytes.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../fred/util/fn.static_bytes.html...

+ + + \ No newline at end of file diff --git a/doc/fred/utils/fn.static_str.html b/doc/fred/utils/fn.static_str.html new file mode 100644 index 00000000..a7091317 --- /dev/null +++ b/doc/fred/utils/fn.static_str.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../fred/util/fn.static_str.html...

+ + + \ No newline at end of file diff --git a/doc/help.html b/doc/help.html new file mode 100644 index 00000000..0257b120 --- /dev/null +++ b/doc/help.html @@ -0,0 +1 @@ +Help

Rustdoc help

Back
\ No newline at end of file diff --git a/doc/search-index.js b/doc/search-index.js new file mode 100644 index 00000000..898d9020 --- /dev/null +++ b/doc/search-index.js @@ -0,0 +1,4 @@ +var searchIndex = new Map(JSON.parse('[["fred",{"t":"DDCQCCQCDCDDDDDCCFFFFFFFFNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNPPPPPPPPPPPFGPPPPPPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNKPKPPPPPKKKKPPKKKKKPKKKKPKKPPPKPKKIGKKPKPPKKKKKKPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNHNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOFONNONNNOONNNNNNNNNHONNNNNNEEEEEEEEEEEEEEEEEEEEEEEPPPGGGPPPPPPPIPPPPPPPPPPPFGPPPPPPGPPPPGFPPPPPPPPGGGGGGPPGPGGFGFGGGPPPPIPPFPPPPPPPPPPPPPPPPFSFPPPPPPPPPPPGPPPGPPPPPPGGPPPPPPPPPPGPKKFFFFFGPPFFPGFGGPFPFPPPPPKPPPPPGPPGPPPPFPPPFPPGPPPPPPPFIPIPPGGPPPPPPPPPPPPPPPPPFFPGPPPPPPPPPPPPFFFFFIIFFPPPPPPPPPPPPPPPPPPPPPPPPPPPPPFGPPPRFPPPPPPPPPPPPPFPPGGFFFGGGGPPPFKPKIGIGPPPFPFGKPFGFFFFFFFGFFPPFGFPGPPGGPPPPPFFPPPPGGPPFPPPPPPPPPPPPGPPPPPPPPPFPPGFGGPGFPPFPPPPPPPPFFPPFGGGFIIPPPGFGGFPGNOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNOOOONNOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOONONNNONOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOONOOOOONOOOOOOOOONNNNNNNNNOOOOONNOOOMNNNNMNNNNOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNNONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOONOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOONOONNOOONNOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMNNNNNNNNNNNNNNNNMNNNNNNNOONONNNNNNNNNNNNNNNNOMNNNNNNNNNNNNNNNONOOOONOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNNNNNNNNOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNNNNNNNNNNNNNNNNNNNOOOOONNOOOOOOOONNNNNNNNNNOOONOONOOMOOOOOOOOOOOOOOOOONNOOONNNNNNNNNNNNNNNNNNNNNMNNNNNOOOOOOOOOOOOOOOOOONNNNNNNNNOOOOOOOOOOOOOOONOOOONNOOOOONOOMMNNNNOOOOOOOOOOOONNNNNNNNOOONOOOOOOOOONNNNNMNNNNOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOONNNOOOONONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOSHHHHHHH","n":["bytes","bytes_utils","clients","cmd","error","interfaces","json_quote","monitor","native_tls","prelude","rustls","rustls_native_certs","serde_json","socket2","tracing","types","util","Pipeline","RedisClient","RedisPool","Replicas","SentinelClient","SubscriberClient","Transaction","WithOptions","active_connections","all","borrow","","","","","","","","borrow_mut","","","","","","","","client","clients","clone","","","","","","","clone_into","","","","","","","","clone_new","","clone_to_uninit","","","","","","","","cluster_node","connect","connect_pool","default","deref","","","","","","","","","deref_mut","","","","","","","","drop","","","","","","","","enable_heartbeat","eq","exec","fmt","","","","","","","","","force_reconnection","from","","","","","","","","from_clients","hash_slot","hscan","id","init","","","","","","","","","into","","","","","","","","last","","len","manage_subscriptions","new","","","","next","next_connected","nodes","options","pipeline","","prefer_connected","psubscribe","punsubscribe","quit","replicas","","reset","resubscribe_all","scan","scan_cluster","set_resolver","size","split_cluster","sscan","ssubscribe","subscribe","sunsubscribe","sync","to_client","to_owned","","","","","","","","to_string","tracked_channels","tracked_patterns","tracked_shard_channels","try_all","try_from","","","","","","","","try_into","","","","","","","","type_id","","","","","","","","unsubscribe","unsubscribe_all","update_perf_config","vzip","","","","","","","","wait_for_connect","watch_before","watched_len","with_cluster_node","zscan","Auth","Backpressure","Canceled","Cluster","Config","IO","InvalidArgument","InvalidCommand","NotFound","Parse","Protocol","RedisError","RedisErrorKind","Replica","Sentinel","Timeout","Tls","Unknown","Url","borrow","","borrow_mut","","change_kind","clone","","clone_into","","clone_to_uninit","","deref","","deref_mut","","details","drop","","eq","","fmt","","","from","","","init","","into","","is_canceled","is_cluster","is_not_found","is_replica","kind","new","new_canceled","source","to_owned","","to_str","to_string","try_from","","try_into","","type_id","","vzip","","AclInterface","Array","AuthInterface","BigNumber","BlobError","BlobString","Boolean","ChunkedString","ClientInterface","ClientLike","ClusterInterface","ConfigInterface","Double","Err","EventInterface","FunctionInterface","GeoInterface","HashesInterface","HeartbeatInterface","Hello","HyperloglogInterface","KeysInterface","ListInterface","LuaInterface","Map","MemoryInterface","MetricsInterface","Null","Number","Ok","PubsubInterface","Push","RediSearchInterface","RedisJsonInterface","RedisResult","Resp3Frame","SentinelInterface","ServerInterface","Set","SetsInterface","SimpleError","SimpleString","SlowlogInterface","SortedSetsInterface","StreamsInterface","TimeSeriesInterface","TrackingInterface","TransactionInterface","VerbatimString","acl_cat","","acl_deluser","","acl_genpass","","acl_getuser","","acl_list","","acl_load","","acl_log_count","","acl_log_reset","","acl_save","","acl_setuser","","acl_users","","acl_whoami","","active_connections","","append","","auth","bgrewriteaof","","bgsave","","blmove","","blmpop","","blpop","","brpop","","brpoplpush","","bzmpop","","bzpopmax","","bzpopmin","","cached_cluster_state","","ckquorum","","client_caching","","client_config","","client_getname","","client_getredir","","client_id","","client_info","","client_kill","","client_list","","client_pause","","client_reconnect_policy","","client_reply","","client_setname","","client_tracking","","client_trackinginfo","","client_unblock","","client_unpause","","cluster_add_slots","","cluster_bumpepoch","","cluster_change_rx","cluster_count_failure_reports","","cluster_count_keys_in_slot","","cluster_del_slots","","cluster_failover","","cluster_flushslots","","cluster_forget","","cluster_get_keys_in_slot","","cluster_info","","cluster_keyslot","","cluster_meet","","cluster_myid","","cluster_nodes","","cluster_replicas","","cluster_replicate","","cluster_reset","","cluster_saveconfig","","cluster_set_config_epoch","","cluster_setslot","","cluster_slots","","command_queue_len","","config_get","","","","config_resetstat","","config_rewrite","","config_set","","","","connect","","connection_config","","connection_ids","","copy","","custom","","custom_raw","","dbsize","","decr","","decr_by","","del","","dump","","enable_heartbeat","error_rx","eval","","evalsha","","exists","","expire","","expire_at","","failover","","","","fcall","","fcall_ro","","flushall","","flushall_cluster","","flushconfig","","force_reconnection","","ft_aggregate","","ft_aliasadd","","ft_aliasdel","","ft_aliasupdate","","ft_alter","","ft_config_get","","ft_config_set","","ft_create","","ft_cursor_del","","ft_cursor_read","","ft_dictadd","","ft_dictdel","","ft_dictdump","","ft_dropindex","","ft_explain","","ft_info","","ft_list","","ft_search","","ft_spellcheck","","ft_sugadd","","ft_sugdel","","ft_sugget","","ft_suglen","","ft_syndump","","ft_synupdate","","ft_tagvals","","function_delete","","function_delete_cluster","","function_dump","","function_flush","","function_flush_cluster","","function_kill","","function_list","","function_load","","function_load_cluster","","function_restore","","function_restore_cluster","","function_stats","","geoadd","","geodist","","geohash","","geopos","","georadius","","georadiusbymember","","geosearch","","geosearchstore","","get","","get_master_addr_by_name","","getdel","","getrange","","getset","","has_reconnect_policy","","hdel","","hello","hexists","","hget","","hgetall","","hincrby","","hincrbyfloat","","hkeys","","hlen","","hmget","","hmset","","hrandfield","","hset","","hsetnx","","hstrlen","","hvals","","id","","incr","","incr_by","","incr_by_float","","info","","info_cache","","init","","invalidation_rx","","is_clustered","","is_connected","","is_pipelined","","json_arrappend","","json_arrindex","","json_arrinsert","","json_arrlen","","json_arrpop","","json_arrtrim","","json_clear","","json_debug_memory","","json_del","","json_get","","json_merge","","json_mget","","json_mset","","json_numincrby","","json_objkeys","","json_objlen","","json_resp","","json_set","","json_strappend","","json_strlen","","json_toggle","","json_type","","keyspace_event_rx","lastsave","","lcs","","lindex","","linsert","","llen","","lmove","","lmpop","","lpop","","lpos","","lpush","","lpushx","","lrange","","lrem","","lset","","ltrim","","master","","masters","","memory_doctor","","memory_malloc_stats","","memory_purge","","memory_stats","","memory_usage","","message_rx","mget","","monitor","","mset","","msetnx","","multi","","myid","","num_primary_cluster_nodes","","on_any","on_cluster_change","on_error","on_invalidation","","on_keyspace_event","on_message","on_reconnect","on_unresponsive","pending_scripts","","perf_config","","persist","","pexpire","","pexpire_at","","pfadd","","pfcount","","pfmerge","","ping","","protocol_version","","psubscribe","","pttl","","publish","","pubsub_channels","","pubsub_numpat","","pubsub_numsub","","pubsub_shardchannels","","pubsub_shardnumsub","","punsubscribe","","quit","","randomkey","","read_latency_metrics","","read_network_latency_metrics","","read_redelivery_count","","read_req_size_metrics","","read_res_size_metrics","","reconnect_rx","remove","","rename","","renamenx","","replicas","","reset","","restore","","rpop","","rpoplpush","","rpush","","rpushx","","sadd","","scard","","script_debug","","script_exists","","script_flush","","script_flush_cluster","","script_kill","","script_kill_cluster","","script_load","","script_load_cluster","","sdiff","","sdiffstore","","select","","sentinel_nodes","","sentinel_primary","","sentinels","","server_version","","set","","","","set_resolver","","setrange","","shutdown","","simulate_failure","","sinter","","sinterstore","","sismember","","slowlog_get","","slowlog_length","","slowlog_reset","","smembers","","smismember","","smove","","sort","","sort_ro","","spawn_event_listener","spop","","spublish","","srandmember","","srem","","ssubscribe","","start_tracking","","state","","stop_tracking","","strlen","","subscribe","","sunion","","sunionstore","","sunsubscribe","","sync_cluster","","take_latency_metrics","","take_network_latency_metrics","","take_redelivery_count","","take_req_size_metrics","","take_res_size_metrics","","ts_add","","ts_alter","","ts_create","","ts_createrule","","ts_decrby","","ts_del","","ts_deleterule","","ts_get","","ts_incrby","","ts_info","","ts_madd","","ts_mget","","ts_mrange","","ts_mrevrange","","ts_queryindex","","ts_range","","ts_revrange","","ttl","","unblock_self","","unlink","","unresponsive_rx","unsubscribe","","unwatch","","update_perf_config","","uses_sentinels","","wait","","wait_for_connect","","watch","","with_options","","xack","","xadd","","xautoclaim","","xautoclaim_values","","xclaim","","xclaim_values","","xdel","","xgroup_create","","xgroup_createconsumer","","xgroup_delconsumer","","xgroup_destroy","","xgroup_setid","","xinfo_consumers","","xinfo_groups","","xinfo_stream","","xlen","","xpending","","xrange","","xrange_values","","xread","","xread_map","","xreadgroup","","xreadgroup_map","","xrevrange","","xrevrange_values","","xtrim","","zadd","","zcard","","zcount","","zdiff","","zdiffstore","","zincrby","","zinter","","zinterstore","","zlexcount","","zmpop","","zmscore","","zpopmax","","zpopmin","","zrandmember","","zrange","","zrangebylex","","zrangebyscore","","zrangestore","","zrank","","zrem","","zremrangebylex","","zremrangebyrank","","zremrangebyscore","","zrevrange","","zrevrangebylex","","zrevrangebyscore","","zrevrank","","zscore","","zunion","","zunionstore","","attributes","","","","","","","","","","","","","auth","data","","","","","","","","","","","","","format","setname","version","Command","args","borrow","borrow_mut","client","clone","clone_into","clone_to_uninit","command","db","deref","deref_mut","drop","eq","fmt","","from","init","into","run","timestamp","to_owned","to_string","try_from","try_into","type_id","vzip","Blocking","Builder","ConnectionConfig","Expiration","FromRedis","Options","PerformanceConfig","ReconnectPolicy","RedisClient","RedisConfig","RedisError","RedisErrorKind","RedisKey","RedisPool","RedisValue","RedisValueKind","Server","ServerConfig","SetOptions","TcpConfig","TlsConfig","TlsConnector","TracingConfig","Add","Addr","After","AggregateOperation","AggregateOptions","Aggregator","All","","","AllowCrossSlotKeys","AllowOOM","AllowStale","AlmostExact","Any","Append","Apply","Array","","","","Asc","Auto","Avg","","","BackpressureConfig","BackpressurePolicy","Before","BigNumber","BlobError","BlobString","Block","","Blocking","Boolean","","","","BucketTimestamp","Builder","Busy","ByLex","ByScore","Bytes","","","Centralized","ChunkedString","ClientKillFilter","ClientKillType","ClientPauseKind","ClientReplyFlag","ClientState","ClientUnblockFlag","Clients","Cluster","ClusterDiscoveryPolicy","ClusterDown","ClusterFailoverFlag","ClusterHash","ClusterInfo","ClusterResetFlag","ClusterRouting","ClusterSetSlotState","ClusterState","ClusterStateChange","Clustered","CommandStats","Compressed","ConfigEndpoint","ConnectHandle","Connected","Connecting","ConnectionConfig","Constant","Count","","","CountDistinct","CountDistinctIsh","Cpu","CrashAfterElection","CrashAfterPromotion","Custom","","","","","","","CustomCommand","DEFAULT_JITTER_MS","DatabaseMemoryStats","Default","DefaultHost","Desc","Disconnected","Disconnecting","Double","","","","","Drain","DuplicatePolicy","EX","EXAT","Earliest","Encoding","End","Error","","Exact","Exclude","Exclusive","Expiration","ExpireOptions","Exponential","Fail","Feet","Filter","First","","FirstKey","FirstValue","","Flush","FnPolicy","Force","FromRedis","FromRedisKey","FtAggregateOptions","FtAlterOptions","FtCreateOptions","FtSearchOptions","Function","FunctionFlag","GT","Geo","GeoPosition","GeoRadiusInfo","GeoShape","GeoUnit","GeoValue","GetLabels","GetTimestamp","GreaterThan","GroupBy","","HScanResult","Hard","Hash","","Hello","Help","HostMapping","ID","Importing","Include","Inclusive","Index","IndexKind","InfiniteLex","InfiniteScore","InfoKind","Integer","","","Interrupt","Invalidation","JSON","KEEPTTL","Keyspace","KeyspaceEvent","Kilometers","LAddr","LMoveDirection","LT","Last","","Latest","Left","LessThan","Lex","Library","Limit","","LimitCount","Linear","List","ListLocation","Load","Loading","Manual","Map","","","","Master","MasterDown","Max","","","","","","","MaxLen","Memory","MemoryStats","Message","","MessageKind","Meters","Mid","Migrating","Miles","Min","","","","","","MinID","Misconf","MultipleGeoValues","MultipleHashSlots","MultipleIDs","MultipleKeys","MultipleOrderedPairs","MultipleStrings","MultipleValues","MultipleWeights","MultipleZaddValues","NX","","Native","NegInfiniteScore","NegInfinityLex","NewInGroup","No","NoCluster","NoReplicas","NoSave","NoWrites","Node","None","","Normal","Now","Null","","","","Number","","Numeric","Off","","Offset","Ok","On","","Options","Ordering","PMessage","PX","PXAT","Page","PerformanceConfig","Persistence","Pubsub","Push","Quantile","Queued","","","RESP2","RESP3","Random","RandomSample","Range","","RangeAggregation","ReadOnly","Rebalance","ReconnectError","ReconnectPolicy","RedisConfig","RedisKey","RedisMap","RedisValue","RedisValueKind","Reducer","ReducerFunc","Remove","Replace","Replica","ReplicaConfig","ReplicaFilter","Replication","Resolve","Resp2TimeSeriesValues","Resp3Frame","Resp3TimeSeriesValues","RespVersion","Right","Rustls","SMessage","SScanResult","Save","ScanResult","ScanType","Scanner","Score","Script","ScriptDebugFlag","SearchField","SearchFilter","SearchGeoFilter","SearchHighlight","SearchParameter","SearchReducer","SearchSchema","SearchSchemaKind","SearchSortBy","SearchSummarize","SelectedLabels","Sentinel","SentinelConfig","SentinelFailureKind","Server","","ServerConfig","Set","","SetOptions","ShutdownFlags","SimpleError","SimpleString","Skip","SkipMe","Sleep","SlotRange","SlowlogEntry","Soft","Some","","SortBy","SortOrder","SpellcheckTerms","Stable","Start","Stats","","StdDev","StdP","","StdS","","Stream","String","","","","","StringOrNumber","Sum","","","","","Sync","TWA","Tag","Takeover","TcpConfig","Text","Timeout","Timestamp","TlsConfig","TlsConnector","TlsHostMapping","ToList","Toggle","TracingConfig","Type","Uncompressed","UnresponsiveConfig","UseCache","User","VarP","","VarS","","Vector","VerbatimString","Version","WithCursor","WithLabels","Write","XCap","XCapKind","XCapTrim","XID","XPendingArgs","XReadResponse","XReadValue","XX","","Yes","ZCmp","ZRange","ZRangeBound","ZRangeKind","ZScanResult","ZSet","ZSort","add_attributes","aggregation","alias","align","allocator_active","allocator_allocated","allocator_fragmentation_bytes","allocator_fragmentation_ratio","allocator_resident","allocator_rss_bytes","allocator_rss_ratio","aof_buffer","args","","array_len","as_bool","","as_bytes","","","as_bytes_str","","as_f64","as_functions","as_geo_position","as_i64","as_str","","","as_str_lossy","","as_string","as_u64","as_usize","attempts","attribute","","","","attributes","attributes_mut","auto_client_setname","auto_pipeline","avg","backpressure","blocking","","blocking_encode_threshold","borrow","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","borrow_mut","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","broadcast_channel_capacity","bucket_duration","bucket_timestamp","build","","build_pool","build_sentinel_client","build_subscriber_client","caching","can_hash","channel","clients_normal","clients_slaves","clone","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","clone_into","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","clone_to_uninit","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","cluster_cache_update_delay","cluster_current_epoch","cluster_hash","","","cluster_known_nodes","cluster_my_epoch","cluster_node","cluster_owner","cluster_size","cluster_slots_assigned","cluster_slots_fail","cluster_slots_ok","cluster_slots_pfail","cluster_state","cluster_stats_messages_received","cluster_stats_messages_sent","cmd","cmp","","","","","","","","cmp_precedence","connection_error_count","connection_task_queue","connection_timeout","connector","consumer","convert","","coordinates","count","","create_client","","","","","cursor","","","","","","database","dataset_bytes","dataset_percentage","db","","default","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","default_centralized","","default_clustered","","default_command_timeout","default_native_tls","default_rustls","default_sleep","default_tracing_level","deref","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","deref_mut","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","dialect","","disable_auto_backpressure","disable_cluster_health_check","distance","drop","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","duration","empty","enabled","encode_len","end","","eq","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","evalsha","evalsha_with_reload","expander","explainscore","extend","fail_fast","","fcall","fcall_ro","field_name","fields","","filter","","","","filters","find_key","flags","flatten_array_values","fmt","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","fragmentation","fragmentation_bytes","frags","from","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","from_buffer","from_cluster_slots","from_code","from_config","from_hash","from_iter","","","","","","","from_key","","","from_lua","from_name","from_redis_value","from_static","","from_static_str","","","from_str","","from_url","from_url_centralized","from_url_clustered","from_url_sentinel","from_value","","","","","","","","full_tracing_level","func","functions","geofilters","get_config","get_connection_config","get_hash","","","","","","","","","","get_performance_config","get_policy","get_sentinel_config","get_server","groupby","has_more","","","","","hash","","","","","","","","","","","","hash_key","highlight","host","","hostnames","hosts","id","","identifier","idle","ignore_reconnection_errors","infields","init","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","inkeys","inner","","","","","","","","","inorder","internal_command_timeout","interval","into","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","into_array","into_bytes","","into_bytes_str","into_geo_radius_result","into_integer","into_json","into_map","into_owned_bytes","into_set","into_string","","into_values","into_xautoclaim_values","into_xread_response","into_xread_value","into_zset_result","ip","is_aggregate_type","is_array","is_boolean","is_bytes","is_centralized","is_clustered","is_double","is_end_stream_frame","is_integer","is_map","is_maybe_map","is_normal_pubsub_message","is_null","is_ok","is_pattern_pubsub_message","is_queued","is_sentinel","is_shard_pubsub_message","is_string","is_unix_socket","keepalive","key","keys","keys_bytes_per_key","keys_count","kind","","","","","language","","language_field","latitude","lazy_connections","len","","","","","","","","","","","limit","linger","load","","longitude","lua","lua_caches","major","map","max","","max_attempts","max_command_attempts","max_command_buffer_len","max_feed_count","max_idle","max_in_flight_commands","max_redirections","","max_timeout","maxtextfields","member","","min","","minor","name","","","","","new","","","","","","","","","","new_centralized","new_clustered","new_constant","new_empty","new_end_stream","new_exponential","new_linear","new_ok","new_sentinel","new_static","new_with_tls","next","","","","","next_delay","no_backpressure","nocontent","nodelay","nofields","nofreqs","nohl","nooffsets","nostopwords","on","operation","options","order","overhead_hashtable_expires","overhead_hashtable_main","overhead_hashtable_slot_to_keys","overhead_total","params","","parse","partial_cmp","","","","","","","","password","","patch","payload","payload_field","peak_allocated","peak_percentage","pipeline","policy","port","","position","","pre","prefixes","pretty","primary","primary_fallback","property","radius","random_node","random_slot","range","reconnect_errors","reconnect_on_auth_error","reduce","replica","replicas","","replication_backlog","resolve","results","","","","","return","router_task_queue","rss_overhead_bytes","rss_overhead_ratio","samples","score","score_field","scorer","separator","server","","","set_cluster_discovery_policy","set_config","set_connection_config","set_jitter","set_performance_config","set_policy","set_sentinel_config","sha1","skipinitialscan","","slop","slots","sortby","start","","startup_allocated","stddev","stopwords","sum","summarize","tags","take","","","","take_attributes","take_results","","","","","tcp","temporary","timeout","","","timestamp","tls","","tls_server_name","to_byte","to_owned","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","to_owned_frame","to_str","to_string","","","","","","","","total_allocated","tracing","","try_from","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","try_into","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","ttl","type_id","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","unique_hash_slots","unique_primary_nodes","units","unresponsive","username","","uses_native_tls","uses_rustls","uses_tls","value","","verbatim","","verbatim_string_format","version","vzip","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","with_config","with_connection_config","with_performance_config","with_sentinel_config","withcount","withpayloads","withscores","withsortkeys","expression","","fields","max","name","num","offset","properties","reducers","disable_backpressure_scaling","min_sleep_duration","attempts","","","delay","","jitter","","","max_attempts","","","max_delay","","min_delay","mult","attributes","","","","","","","","","","","","","auth","data","","","","","","","","","","","","","format","setname","version","arguments","casesensitive","name","noindex","","","","","","nostem","phonetic","separator","sortable","","","","unf","","","","weight","withsuffixtrie","","hosts","","policy","server","service_name","dictionary","","terms","","NONE","f64_to_redis_string","group_by_hash_slot","redis_keyslot","redis_string_to_f64","sha1_hash","static_bytes","static_str"],"q":[[0,"fred"],[17,"fred::clients"],[227,"fred::error"],[296,"fred::interfaces"],[1156,"fred::interfaces::Resp3Frame"],[1186,"fred::monitor"],[1213,"fred::prelude"],[1236,"fred::types"],[4656,"fred::types::AggregateOperation"],[4665,"fred::types::BackpressurePolicy"],[4667,"fred::types::ReconnectPolicy"],[4682,"fred::types::Resp3Frame"],[4712,"fred::types::SearchSchemaKind"],[4735,"fred::types::ServerConfig"],[4740,"fred::types::SpellcheckTerms"],[4744,"fred::util"],[4752,"fred::clients::pool"],[4753,"fred::protocol::types"],[4754,"alloc::vec"],[4755,"core::result"],[4756,"core::future::future"],[4757,"fred::clients::pipeline"],[4758,"fred::glommio::interfaces"],[4759,"fred::modules::response"],[4760,"fred::clients::replica"],[4761,"fred::clients::redis"],[4762,"fred::clients::options"],[4763,"core::clone"],[4764,"fred::clients::sentinel"],[4765,"fred::clients::pubsub"],[4766,"fred::clients::transaction"],[4767,"core::option"],[4768,"core::time"],[4769,"core::fmt"],[4770,"fred::types::scan"],[4771,"futures_core::stream"],[4772,"fred::types::args"],[4773,"core::convert"],[4774,"bytes_utils::string"],[4775,"fred::types::config"],[4776,"std::collections::hash::map"],[4777,"fred::types::multiple"],[4778,"alloc::rc"],[4779,"alloc::string"],[4780,"alloc::collections::btree::set"],[4781,"core::any"],[4782,"serde_json::error"],[4783,"alloc::borrow"],[4784,"core::error"],[4785,"fred::commands::interfaces::acl"],[4786,"fred::commands::interfaces::keys"],[4787,"fred::commands::interfaces::server"],[4788,"fred::commands::interfaces::lists"],[4789,"fred::types::lists"],[4790,"fred::commands::interfaces::sorted_sets"],[4791,"fred::types::sorted_sets"],[4792,"fred::commands::interfaces::cluster"],[4793,"fred::commands::interfaces::sentinel"],[4794,"fred::commands::interfaces::client"],[4795,"fred::types::client"],[4796,"fred::types::misc"],[4797,"fred::types::cluster"],[4798,"fred::commands::interfaces::metrics"],[4799,"fred::commands::interfaces::config"],[4800,"fred::commands::interfaces::lua"],[4801,"fred::commands::interfaces::redisearch"],[4802,"fred::types::redisearch"],[4803,"bytes::bytes"],[4804,"fred::commands::interfaces::geo"],[4805,"fred::types::geo"],[4806,"fred::commands::interfaces::hashes"],[4807,"redis_protocol::resp3::types"],[4808,"fred::commands::interfaces::tracking"],[4809,"fred::commands::interfaces::redis_json"],[4810,"serde_json::value"],[4811,"fred::commands::interfaces::memory"],[4812,"core::net::ip_addr"],[4813,"fred::commands::interfaces::transactions"],[4814,"core::ops::function"],[4815,"fred::commands::interfaces::hyperloglog"],[4816,"fred::commands::interfaces::pubsub"],[4817,"fred::modules::metrics"],[4818,"fred::commands::interfaces::sets"],[4819,"semver"],[4820,"fred::commands::interfaces::slowlog"],[4821,"fred::commands::interfaces::timeseries"],[4822,"fred::types::timeseries"],[4823,"core::iter::traits::collect"],[4824,"fred::commands::interfaces::streams"],[4825,"fred::types::streams"],[4826,"core::hash"],[4827,"core::cmp"],[4828,"redis_protocol::error"],[4829,"fred::types::scripts"],[4830,"fred::types::builder"],[4831,"fred::protocol::hashers"],[4832,"fred::protocol::tls"],[4833,"fred::router::replicas"],[4834,"core::marker"],[4835,"alloc::boxed"],[4836,"core::pin"],[4837,"alloc::collections::vec_deque"],[4838,"native_tls"],[4839,"tokio_rustls"],[4840,"tokio_native_tls"],[4841,"rustls::client::client_conn"],[4842,"std::collections::hash::set"],[4843,"semver::parse"],[4844,"alloc::collections::btree::map"],[4845,"fred::utils"],[4846,"redis_protocol::utils"]],"i":"`````````````````````````bAb0B`2AlBdBfAjBh657432101757432106574321042657432100774655743210657432106574321070065744321076574321070406577432106574321067027432771541722774024477442221265743210422266574321065743210657432102276574321070044Gb0000000000``000000l1010010101010100101001001010100000000011001010101`Kn`00000````0Cf`````1````1``110`1``````1`11``````1Gl00000000000000000000000Ad0Hb0HfHh000Hj000000000Ib00000If0Ij0Il08800000000000000880000000000002222Jl33333333333333333333333333333333333333Kf0Kh04400000044;;;;33::;;;;88::::::::L`3Lb000<<<<<<66::Lf000>>>>77>>Lh000000000000000000000000000000000000000000000000000111111111111111111111111Mj000000000000000??99??????Ad0Nh0Hf111111111111111111111111111122Hb0000033==33O`0444444Od0000000000000000000000000000000000000000000=Hh033Hj0000000000000000000000000Ij000Ol000000000Jl77227777A@b033If022288222244<<999999A@h00000====A@j0;;00000000000000>>;;Kf000000000577<<<<7777<<88888888A@n000Lb0000000000000001111;;;;;;99Ad0??::00??00::222222AAj00000333333<<<<`3355333355??11??Hb066444466885555555555AB`00000000000000000000000000000000011Il022<88224444Hh0553355ACd000000000000000000000000000000000000000000000000000Ib00000000000000000000000000000000000000000000000000000000000B@bB@dB@fB@hB@jB@lB@nBA`BAbBAdBAfBAhBAjBAl=<;:987654321500`AEf00000000000000000`0000000```````````````````````JnInOj```JbNnAIfAJn00AKf`MhAIlKnGnAFnHdNdACjAI`ABhAKj``Oj999AGfABd`;:98``AGdAEd0<;:AHb>``````Nn0`3````````10ABbAH``AAn0`Ed=<;==4AAh0AEnAF`:AI`AJjABfACb```;4Nd99KnGnAFlAFnHdAGh`AAd08`AKlAGfJhAKfMdAKd``EdAG`N`AIlABdABhAEn0AI`Mh`K`````````A@fAJj``0````ADj`9`KbAJhEnKnAAh`InKdMdAKdAKb`00`GnAFnHdAGf``KnEn``11JdInAGh``KbBAnAIfAIl``KdAKl`NnAI`ABhAKj10=GnAFlAFnHdEn`ADn8ABd87AA`9AJjK``1Jh````=``InABb`AH`2?>?>5Kn``ABlJb```````MlA@f;`````>`4ABnM`1AHl0000000AHnAIbHd:0:Df1011111;0101111EdAInAJ`AJfLlKn0EbE`A@l1DnKl3AAbNj74ADnJjF`DbFfG`AE`ADlAEnAF`AFbAFdhAFfOnAFhIhAFjAFlDfNlAFnHdAElJ`InJbJdJfObAG`AGbK`KbKdAGdEdAGfAGhAGjAGlAGnAH`EbE`DnAHbAHdEfEjNfN`AHfMnAHhHlOjAAfOhNnKlJnMlA@fAAdAAnAHjAHlAHnAA`AAhNdMhJhFnAI`AIbAIdAIfAIhAIjAIlLjAInAJ`AJbAJdAJfMbAJhLnAJjM`LlMdEnAJlAJnAEjAK`IdADjAEdAKbAKdAEbAKfAClACfAKhAChACjADfABbABdABfABhABlACbABnAKjAC`AKlAAbNjKnA@lADnJjF`DbFfG`AE`ADlAEnAF`AFbAFdhAFfOnAFhIhAFjAFlDfNlAFnHdAElJ`InJbJdJfObAG`AGbK`KbKdAGdEdAGfAGhAGjAGlAGnAH`EbE`DnAHbAHdEfEjNfN`AHfMnAHhHlOjAAfOhNnKlJnMlA@fAAdAAnAHjAHlAHnAA`AAhNdMhJhFnAI`AIbAIdAIfAIhAIjAIlLjAInAJ`AJbAJdAJfMbAJhLnAJjM`LlMdEnAJlAJnAEjAK`IdADjAEdAKbAKdAEbAKfAClACfAKhAChACjADfABbABdABfABhABlACbABnAKjAC`AKlE`44AElAAb111EjHdOnAHl04NjKnAEnAF`AFbAFdhAFf9AFhIhAFjAFlDfNlAFnHdAElJ`InJbJdJfObAG`AGbK`KbKdAGdEdAGfAGhAGjAGlAGnAH`EbE`DnAHbAHdEfEjNfN`AHfMnAHhHlOjAAfOhNnKlJnMlA@fAAdAAnAHjAHlAHnAA`AAhNdMhJhFnAI`AIbAIdAIfAIhAIjAIlLjAInAJ`AJbAJdAJfMbAJhLnAJjM`LlMdEnAJlAJnAEjAK`IdADjAEdAKbAKdAEbAKfAClACfAKhAChACjADfABbABdABfABhABlACbABnAKjAC`AKlAAbNjKnAEnAF`AFbAFdhAFfOnAFhIhAFjAFlDfNlAFnHdAElJ`InJbJdJfObAG`AGbK`KbKdAGdEdAGfAGhAGjAGlAGnAH`EbE`DnAHbAHdEfEjNfN`AHfMnAHhHlOjAAfOhNnKlJnMlA@fAAdAAnAHjAHlAHnAA`AAhNdMhJhFnAI`AIbAIdAIfAIhAIjAIlLjAInAJ`AJbAJdAJfMbAJhLnAJjM`LlMdEnAJlAJnAEjAK`IdADjAEdAKbAKdAEbAKfAClACfAKhAChACjADfABbABdABfABhABlACbABnAKjAC`AKlAAbNjKnAEnAF`AFbAFdhAFfOnAFhIhAFjAFlDfNlAFnHdAElJ`InJbJdJfObAG`AGbK`KbKdAGdEdAGfAGhAGjAGlAGnAH`EbE`DnAHbAHdEfEjNfN`AHfMnAHhHlOjAAfOhNnKlJnMlA@fAAdAAnAHjAHlAHnAA`AAhNdMhJhFnAI`AIbAIdAIfAIhAIjAIlLjAInAJ`AJbAJdAJfMbAJhLnAJjM`LlMdEnAJlAJnAEjAK`IdADjAEdAKbAKdAEbAKfAClACfAKhAChACjADfABbABdABfABhABlACbABnAKjAC`AKlEbAGbDfEjKl3312333333330AAbh4OhAJlAJnAEjAK`6AFj<AAbNj3AEnAF`AFbAFdhAFfOn9AFjAFlDfNlAFnHdJ`InJbJdJfObAG`AGbK`KbKdAGdEdAGfAGhAGjAGlAGnAH`EbE`DnAHbEjNfN`AHfMnAHhHlOjAAfOhNnKlJnMlA@fAAdAAnAHjAHlAHnAA`AAhNdMhJhFnAI`AIbAIdAIfAIhAIjAIlAJbAJdAJfAJhEnAJlAJnAEjAK`IdADjAEdAKdAKfAClACfAKhAChACjADfABbABdABfABhABlACbABnAKjAC`AKlAJl0Mb0EjDn1AEj0M`AJbAJdALh0AFjLn9AEn7HdAAb0NjKn4AF`AFbAFdh0AFfOnAFhIh>AFlDfNlAFn0?AElJ`InJbJdJfObAG`AGbK`KbKdAGdEdAGfAGhAGjAGlAGnAH`EbE`DnAHbAHdEfEjNfN`AHfMnAHhHlOjAAfOhNnKlJnMlA@fAAdAAn0AHjAHlAHnAA`AAhNdMhJhFnAI`AIbAIdAIfAIhAIjAIlLjAInAJ`AJbAJdAJfMbAJhLnAJjM`LlMdEnAJl0AJnAEj0AK`0IdADjAEdAKbAKdAEbAKfAClACfAKhAChACjADfABbABdABfABhABlACbABnAKjAC`AKlAHl0AJbAAbNjKn000A@lADnJj0000F`DbFfG`AE`0000ADlAEn0000AF`AFb0AFd0000h000AFfOnAFhIhAFjAFl000000000000000Df000000000000000000000000Nl00AFnHd000000000000000000000000000000000000AElJ`InJbJdJf0ObAG`AGbK`KbKdAGdEdAGfAGhAGjAGlAGnAH`EbE`DnAHbAHdEfEjNf0N`AHfMn000AHhHlOjAAfOhNnKlJnMlA@fAAdAAnAHjAHlAHnAA`AAhNdMhJhFn0000000000000000000000AI`AIbAIdAIfAIhAIjAIlLjAInAJ`AJbAJdAJfMbAJhLnAJjM`LlMdEnAJlAJnAEjAK`IdADjAEdAKb0000AKdAEb00000AKfACl0ACf000AKhACh0ACj0000ADf00000ABbABdABf0ABhABl00ACb0ABn0AKjAC`0AKlKnIhAK`AElAJlJjAE`ADlNlHdFnABlAD`Df49;AHh15AFl26AAbAJnDn000Af6:AGbNfAHjAHlAHnAHdAIbAK`MbAEl0=KnhDfHdOhAJlAJnAEj:888IhAC`ALbF`DbFfG`AAb?AEn?>=<;:9AK`AHh:MbhEfAFbAHbAFhAHnAIdADfAFj9=NjKnA@lADnJjF`DbFfG`AE`ADlAEnAF`AFbAFdhAFfOnAFhIhAFjAFlDfNlAFnHdAElJ`InJbJdJfObAG`AGbK`KbKdAGdEdAGfAGhAGjAGlAGnAH`EbE`DnAHbAHdEfEjNfN`AHfMnAHhHlOjAAfOhNnKlJnMlA@fAAdAAnAHjAHlAHnAA`AAhNdMhJhFnAI`AIbAIdAIfAIhAIjAIlLjAInAJ`AJbAJdAJfMbAJhLnAJjM`LlMdEnAJlAJnAEjAK`IdADjAEdAKbAKdAEbAKfAClACfAKhAChACjADfABbABdABfABhABlACbABnAKjAC`AKlMbJjAE`ADlDfNlMnFnAClACf9EbAGnAAbNjKnA@lADn?F`DbFfG`AE`ADlAEnAF`AFbAFdhAFfOnAFhIhAFjAFlDfNlAFnHdAElJ`InJbJdJfObAG`AGbK`KbKdAGdEdAGfAGhAGjAGlAGnAH`EbE`DnAHbAHdEfEjNfN`AHfMnAHhHlOjAAfOhNnKlJnMlA@fAAdAAnAHjAHlAHnAA`AAhNdMhJhFnAI`AIbAIdAIfAIhAIjAIlLjAInAJ`AJbAJdAJfMbAJhLnAJjM`LlMdEnAJlAJnAEjAK`IdADjAEdAKbAKdAEbAKfAClACfAKhAChACjADfABbABdABfABhABlACbABnAKjAC`AKlHdDf1111111101Fn2222AHn3333AHb04Kn555055051051AGlOhObAHl049OnM`AEbMbLn0NfAFj;JjAE`ADlIhNlMnFnAClACfAJb=AGlAJlLj>1AHlAAbAOdA@lAInEjEb0E`AIhAGj34AGnLnAHfAHh:9=>?AJdKnDfNlHd3ALbF`DbFfG`EbLnEjLjMbAHnDnEfhNjAAb1KnAEnAF`AFbAFd7AFfOnAFhIhAFjAFlDfNlAFnHdAElJ`InJbJdJfObAG`AGbK`KbKdAGdEdAGfAGhAGjAGlAGnAH`EbE`DnAHbAHdEfEjNfN`AHfMnAHhHlOjAAfOhNnKlJnMlA@fAAdAAnAHjAHlAHnAA`AAhNdMhJhFnAI`AIbAIdAIfAIhAIjAIlLjAInAJ`AJbAJdAJfMbAJhLnAJjM`LlMdEnAJlAJnAEjAK`IdADjAEdAKbAKdAEbAKfAClACfAKhAChACjADfABbABdABfABhABlACbABnAKjAC`AKlKnAJnAAb2hAFnAAnAJlAEjAK`AHlDnEf9Nj<>{{{d{Lh}}ceCdg}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Dj}}}{{Dh{Dj}}}{{Dh{El}}}Af}0{{{d{Lh}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Dj}}}{{Dh{Dj}}}Af}0{{{d{Lf}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Dj}}}Af}0{{{d{Lf}}c}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}{{Dh{Dj}}}}0{{{d{Lf}}}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}0{{{d{Lf}}Cd}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}0{{{d{Lf}}Cd}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}022{{{d{Lf}}{Bl{c}}Cd}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Dj}}}Af}0{{{d{Lf}}Cdc}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Dj}}}Af}000{{{d{Lf}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Mf}}}{{H`{Mh}}}Af}0{{{d{Lf}}ce}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}{{Dh{Mf}}}{{H`{Mh}}}}066{{{d{Mj}}c{Bl{Ml}}Cde}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Mn}}}Af}0{{{d{Mj}}ceg{Bl{N`}}}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{H`{Hd}}}{{H`{Hd}}}Af}0{{{d{Mj}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Gn}}}Af}000{{{d{Mj}}ceHnN`CdCdCd{Bl{{Ld{DlNb}}}}{Bl{Nd}}{Bl{Df}}{Bl{Df}}}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Nf}}}Af}0{{{d{Mj}}ceHnN`CdCdCd{Bl{{Ld{DlNb}}}}{Bl{Nd}}{Bl{Df}}{Bl{Df}}}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Hd}}}Af}0{{{d{Mj}}c{Bl{Hd}}{Bl{Nf}}{Bl{{Ld{HnN`}}}}{Bl{{Ld{HnHnN`}}}}{Bl{Nd}}{Bl{{Ld{DlNb}}}}CdCdCd}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Mj}}ce{Bl{Hd}}{Bl{Nf}}{Bl{{Ld{HnN`}}}}{Bl{{Ld{HnHnN`}}}}{Bl{Nd}}{Bl{{Ld{DlNb}}}}Cd}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Df}}}Af}0{{{d{Hb}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Ij}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Dj}}}Af}011{{{d{Hb}}cC`C`}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Hb}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Hd}}}Af}0{{{d{Ad}}}Cd}0{{{d{Nh}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Fn}}}Af}0{{{d{Hf}}Nj{Bl{{Ld{DjDj}}}}{Bl{Dj}}}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}{{{d{Nh}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Df}}}Af}000{{{d{Nh}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Nh}}ceI`}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Df}}}Af}0{{{d{Nh}}ceHn}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Df}}}Af}0222255{{{d{Nh}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Nl}}}Af}0{{{d{Nh}}c{Bl{{Ld{I`Cd}}}}}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}011{{{d{Nh}}ceg}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Df}}}{{H`{Hd}}}Af}06655{{{d{Ad}}}{{d{Gd}}}}0>>{{{d{Hb}}cI`}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Hb}}cHn}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Ad}}{Bl{Nn}}}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}0{{{d{Ij}}}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}0{{{d{Ad}}}{{`{{A`{}{{f{{Cf{Bn}}}}}}}}}}0{{{d{O`}}}{{`{Ob}}}}0{{{d{Ad}}}Cd}00000{{{d{Od}}ce{j{g}}}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{Of}}}Af}0{{{d{Od}}ceg{Bl{I`}}{Bl{I`}}}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{Of}}}Af}0{{{d{Od}}ceI`{j{g}}}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{Of}}}Af}0{{{d{Od}}c{Bl{e}}}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}Af}0{{{d{Od}}c{Bl{e}}{Bl{I`}}}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}Af}0{{{d{Od}}ceI`I`}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}Af}02222{{{d{Od}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}Af}0{{{d{Od}}c{Bl{e}}{Bl{g}}{Bl{i}}k}{{`{{A`{}{{f{{Cf{m}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{Dj}}}{{Dh{Dj}}}{{Dh{El}}}Af}0{{{d{Od}}ceg}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{Of}}}Af}0{{{d{Od}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Fn}}}{{Dh{Dj}}}Af}0{{{d{Od}}{j{{Ld{ceg}}}}}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{Of}}}Af}022777777{{{d{Od}}ceg{Bl{Ml}}}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{Of}}}Af}0{{{d{Od}}c{Bl{e}}g}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{Of}}}Af}0996699{{{d{Jl}}}{{`{Oh}}}}{{{d{Hh}}}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}0{{{d{Hb}}ceCdCd{Bl{I`}}Cd}{{`{{A`{}{{f{{n{gl}}}}}}}}}{{Dh{Df}}}{{Dh{Df}}}Af}0{{{d{Hj}}cI`}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Hj}}cOjeg}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{H`{Hd}}}{{H`{Hd}}}Af}0{{{d{Hj}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Hj}}ceHlHl}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Df}}}Af}0{{{d{Hj}}cHl{Bl{I`}}}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Fn}}}Af}0{{{d{Hj}}c{Bl{C`}}}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Hj}}ce{Bl{I`}}{Bl{I`}}{Bl{I`}}}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Hd}}}Af}0{{{d{Hj}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Gn}}}Af}000{{{d{Hj}}cI`I`}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Hj}}cI`e}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Hd}}}Af}00011{{{d{Ij}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Dj}}}Af}0{{{d{Ij}}}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}0{{{d{Ol}}}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}000{{{d{Ol}}}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}011{{{d{Ol}}c{Bl{Cn}}}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Jl}}}{{`{On}}}}{{{d{Hb}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Fn}}}Af}0{{{d{Ij}}cA@`ClCn}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Dj}}}Af}0{{{d{Hb}}c}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}{{H`{Nl}}}}0{{{d{Hb}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{H`{Nl}}}Af}0{{{d{A@b}}}Bh}099{{{d{If}}}C`}0{{{d{Jl}}ceg}{{`{{Cf{Bj}}}}}{{A@d{l}{{f{{Cf{Bj}}}}}}}{{A@d{h}{{f{{Cf{Bj}}}}}}}{{A@d{{j{Jn}}}{{f{{Cf{Bj}}}}}}}}{{{d{Jl}}c}{{`{{Cf{Bj}}}}}{{A@d{{j{Jn}}}{{f{{Cf{Bj}}}}}}}}{{{d{Jl}}c}{{`{{Cf{Bj}}}}}{{A@d{l}{{f{{Cf{Bj}}}}}}}}{{{d{O`}}c}{{`{{Cf{Bj}}}}}{{A@d{Ob}{{f{{Cf{Bj}}}}}}}}0{{{d{Jl}}c}{{`{{Cf{Bj}}}}}{{A@d{Oh}{{f{{Cf{Bj}}}}}}}}{{{d{Jl}}c}{{`{{Cf{Bj}}}}}{{A@d{On}{{f{{Cf{Bj}}}}}}}}{{{d{Jl}}c}{{`{{Cf{Bj}}}}}{{A@d{h}{{f{{Cf{Bj}}}}}}}}0{{{d{Ij}}}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}0{{{d{Ad}}}E`}0{{{d{Hb}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Hb}}cI`{Bl{A@f}}}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}000{{{d{A@h}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Gn}}}Af}0{{{d{A@h}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Fn}}}Af}0{{{d{A@h}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Fn}}}Af}0{{{d{Ad}}}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}0{{{d{Ad}}}Nj}0{{{d{A@j}}c}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}{{Dh{El}}}}077{{{d{A@j}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Dj}}}{{H`{Hd}}}Af}0{{{d{A@j}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Dj}}}Af}0{{{d{A@j}}}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}0{{{d{A@j}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{El}}}Af}0220044{{{d{Ad}}}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}0{{{d{Hb}}}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}0{{{d{Kf}}}A@l}000{{{d{Kf}}}C`}01111{{{d{Jl}}}{{`{h}}}}{{{d{Ij}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Dj}}}Af}0{{{d{Hb}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Df}}}Af}0001111{{{d{Hb}}cI`HdCdCd{Bl{I`}}{Bl{I`}}}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Hj}}c{Bl{C`}}}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Hj}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Df}}}Af}0{{{d{Hj}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Gn}}}Af}000{{{d{A@n}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Gn}}}Af}0{{{d{A@n}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Lb}}AA`}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}0{{{d{Lb}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{El}}}Af}0{{{d{Lb}}Cd}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}000{{{d{Lb}}}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}000{{{d{Lb}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Dj}}}Af}000{{{d{A@n}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Fn}}}Af}0{{{d{A@n}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Fn}}}Af}0{{{d{Hh}}Kj}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}0{{{d{Hh}}}{{Bl{{j{h}}}}}}0{{{d{Hh}}}{{Bl{h}}}}0{{{d{Ij}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Dj}}}Af}0{{{d{Ad}}}{{Bl{AAb}}}}0{{{d{Hb}}ce{Bl{AAd}}{Bl{Ml}}Cd}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Hd}}}Af}0{{{d{Ij}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Dj}}}{{H`{Nl}}}Af}0{{{d{Ad}}{Fd{Fb}}}{{`{A`}}}}0{{{d{Hb}}cCne}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Hd}}}Af}0{{{d{Ad}}{Bl{AAf}}}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}0{{{d{Ij}}AAh}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}0<<;;{{{d{A@n}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Hd}}}Af}0{{{d{AAj}}{Bl{I`}}}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}0{{{d{AAj}}}{{`{{A`{}{{f{{Cf{c}}}}}}}}}Af}0{{{d{AAj}}}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}0{{{d{A@n}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{A@n}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Gn}}}Af}0{{{d{A@n}}ceg}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Df}}}{{H`{Hd}}}Af}0{{{d{Hj}}c{Bl{Dj}}{Bl{AAl}}e{Bl{Nd}}Cd{Bl{Df}}}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{El}}}Af}0{{{d{Hj}}c{Bl{Dj}}{Bl{AAl}}e{Bl{Nd}}Cd}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{El}}}Af}0{{{`{c}}e}{{`{{Cf{Bj}}}}}Bb{{A@d{c}{{f{{Cf{Bj}}}}}}}}{{{d{A@n}}c{Bl{C`}}}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{A@j}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Dj}}}{{H`{Hd}}}Af}01166{{{d{A@j}}c}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}{{Dh{El}}}}0{{{d{O`}}cCdCdCdCd}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}{{Dh{El}}}}0{{{d{Ad}}}AAn}0{{{d{O`}}}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}0{{{d{Hb}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}044{{{d{A@n}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Fn}}}Af}0{{{d{A@n}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Fn}}}Af}066{{{d{If}}}{{`{{A`{}{{f{{n{Bjl}}}}}}}}}}0{{{d{Kf}}}A@l}000{{{d{Kf}}}C`}01111{{{d{AB`}}ceHn{Bl{Dl}}{Bl{ABb}}{Bl{Dl}}{Bl{ABd}}g}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{H`{ABf}}}{{H`{Nl}}}Af}0{{{d{AB`}}c{Bl{Dl}}{Bl{Dl}}{Bl{ABd}}e}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Nl}}}Af}0{{{d{AB`}}c{Bl{Dl}}{Bl{ABb}}{Bl{Dl}}{Bl{ABd}}e}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Nl}}}Af}0{{{d{AB`}}ce{Ld{ABhDl}}{Bl{Dl}}}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Df}}}Af}0{{{d{AB`}}cHn{Bl{ABf}}{Bl{Dl}}Cd{Bl{Dl}}e}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Nl}}}Af}0{{{d{AB`}}cI`I`}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{AB`}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Df}}}Af}0{{{d{AB`}}cCd}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}03300{{{d{AB`}}e}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{ABj{}{{D`{{Ld{cABfHn}}}}}}}Af}0{{{d{AB`}}Cd{Bl{c}}g}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{ABl}}}{{Dh{Dj}}}{{ABj{}{{D`{e}}}}}Af}0{{{d{AB`}}ceCdg{Bl{{Ld{I`I`}}}}{Bl{ABl}}{Bl{Dl}}{Bl{ABn}}k{Bl{AC`}}}{{`{{A`{}{{f{{Cf{m}}}}}}}}}{{H`{ACb}}}{{H`{ACb}}}{{ABj{}{{D`{I`}}}}}{{Dh{Dj}}}{{ABj{}{{D`{i}}}}}Af}000{{{d{AB`}}e}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Dj}}}{{ABj{}{{D`{c}}}}}Af}0{{{d{AB`}}cegCdi{Bl{{Ld{I`I`}}}}{Bl{Dl}}{Bl{ABn}}}{{`{{A`{}{{f{{Cf{k}}}}}}}}}{{Dh{Df}}}{{H`{ACb}}}{{H`{ACb}}}{{ABj{}{{D`{I`}}}}}Af}000{{{d{Hb}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Il}}{Bl{Jh}}}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}0{{{d{Hb}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Fn}}}Af}0{{{d{Jl}}}{{`{h}}}}{{{d{A@j}}c}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}{{Dh{El}}}}0{{{d{Hb}}}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}0{{{d{Ad}}E`}Bj}0{{{d{Ad}}}Cd}0{{{d{Hh}}I`I`}{{`{{A`{}{{f{{n{cl}}}}}}}}}Af}0{{{d{Ad}}}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}}0{{{d{Hb}}c}{{`{{A`{}{{f{{Cf{Bj}}}}}}}}}{{Dh{Fn}}}}0{{{d{Ad}}{d{Ej}}}{{B`{Ad}}}}0{{{d{ACd}}ceg}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{ACf}}}Af}0{{{d{ACd}}cCdegi}{{`{{A`{}{{f{{Cf{k}}}}}}}}}{{Dh{Df}}}{{H`{ACh}}}{{Dh{ACj}}}{{H`{ACl}}}Af}0{{{d{ACd}}cegDli{Bl{Dl}}Cd}{{`{{A`{}{{f{{Cf{k}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{Dj}}}{{Dh{ACj}}}Af}0{{{d{ACd}}cegDli{Bl{Dl}}Cd}{{`{{A`{}{{f{{Cf{{Ld{Fh{j{{ACn{kmo}}}}}}}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{Dj}}}{{Dh{ACj}}}Af{AD`ADbADd}Af}0{{{d{ACd}}cegDli{Bl{Dl}}{Bl{Dl}}{Bl{Dl}}CdCd}{{`{{A`{}{{f{{Cf{k}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{Dj}}}{{Dh{ACf}}}Af}0{{{d{ACd}}cegDli{Bl{Dl}}{Bl{Dl}}{Bl{Dl}}CdCd}{{`{{A`{}{{f{{Cf{{j{{ACn{kmo}}}}}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{Dj}}}{{Dh{ACf}}}Af{AD`ADbADd}Af}0{{{d{ACd}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{El}}}Af}0{{{d{ACd}}cegCd}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{ACj}}}Af}0{{{d{ACd}}ceg}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{Dj}}}Af}000{{{d{ACd}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}Af}0{{{d{ACd}}ceg}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{ACj}}}Af}011{{{d{ACd}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{ACd}}cCd{Bl{Dl}}}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}011{{{d{ACd}}ceg}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Dj}}}{{Dh{ADf}}}Af}0{{{d{ACd}}ceg{Bl{Dl}}}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{H`{Hd}}}{{H`{Hd}}}Af}0{{{d{ACd}}ceg{Bl{Dl}}}{{`{{A`{}{{f{{Cf{{j{{ACn{ikm}}}}}}}}}}}}}{{Dh{Df}}}{{H`{Hd}}}{{H`{Hd}}}Af{AD`ADbADd}Af}0{{{d{ACd}}{Bl{Dl}}{Bl{Dl}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Fn}}}{{Dh{ACf}}}Af}0{{{d{ACd}}{Bl{Dl}}{Bl{Dl}}ce}{{`{{A`{}{{f{{Cf{{ADh{gikm}}}}}}}}}}}{{Dh{Fn}}}{{Dh{ACf}}}{AD`ADbADd}Af{AD`ADbADd}Af}0{{{d{ACd}}ce{Bl{Dl}}{Bl{Dl}}Cdgi}{{`{{A`{}{{f{{Cf{k}}}}}}}}}{{Dh{Dj}}}{{Dh{Dj}}}{{Dh{Fn}}}{{Dh{ACf}}}Af}0{{{d{ACd}}ce{Bl{Dl}}{Bl{Dl}}Cdgi}{{`{{A`{}{{f{{Cf{{ADh{kmoAa}}}}}}}}}}}{{Dh{Dj}}}{{Dh{Dj}}}{{Dh{Fn}}}{{Dh{ACf}}}{AD`ADbADd}Af{AD`ADbADd}Af}05544{{{d{ACd}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{ACh}}}Af}0{{{d{Ib}}c{Bl{Ml}}{Bl{ADj}}CdCde}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{ADl}}}Af}0{{{d{Ib}}c}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Ib}}cHnHn}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Ib}}cCd}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Fn}}}Af}0{{{d{Ib}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{Dh{Fn}}}Af}0{{{d{Ib}}cHne}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Hd}}}Af}0{{{d{Ib}}ce{Bl{ADn}}Cd}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Fn}}}{{Dh{AE`}}}Af}0{{{d{Ib}}ceg{Bl{ADn}}}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{Dh{Fn}}}{{Dh{AE`}}}Af}0{{{d{Ib}}ceg}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{H`{AEb}}}{{H`{AEb}}}Af}0{{{d{Ib}}cId{Bl{I`}}}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Fn}}}Af}0{{{d{Ib}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Gn}}}Af}0{{{d{Ib}}c{Bl{C`}}}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}000{{{d{Ib}}c{Bl{{Ld{I`Cd}}}}}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0{{{d{Ib}}ceg{Bl{AEd}}Cd{Bl{AAl}}Cd}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{H`{AEb}}}{{H`{AEb}}}Af}0{{{d{Ib}}ceg{Bl{AAl}}}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{H`{AEb}}}{{H`{AEb}}}Af}0{{{d{Ib}}cegCd{Bl{AAl}}}{{`{{A`{}{{f{{Cf{i}}}}}}}}}{{Dh{Df}}}{{H`{AEb}}}{{H`{AEb}}}Af}0{{{d{Ib}}cegi{Bl{AEd}}Cd{Bl{AAl}}}{{`{{A`{}{{f{{Cf{k}}}}}}}}}{{Dh{Df}}}{{Dh{Df}}}{{H`{AEb}}}{{H`{AEb}}}Af}0{{{d{Ib}}ce}{{`{{A`{}{{f{{Cf{g}}}}}}}}}{{Dh{Df}}}{{H`{Hd}}}Af}07799{{{d{Ib}}cI`I`}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}0::{{{d{Ib}}cI`I`Cd}{{`{{A`{}{{f{{Cf{e}}}}}}}}}{{Dh{Df}}}Af}055442222==<<````````````````````````````````{d{{d{c}}}{}}{{{d{Ah}}}{{d{Ahc}}}{}}`{{{d{AEf}}}AEf}{{d{d{Ahc}}}Bj{}}{dBj}``{C`{{d{c}}}{}}{C`{{d{Ahc}}}{}}{C`Bj}{{{d{AEf}}{d{AEf}}}Cd}{{{d{AEf}}{d{AhCh}}}Cj}0{cc{}}{{}C`}{{}c{}}{Dn{{n{{`{{Dd{}{{D`{AEf}}}}}}l}}}}`{dc{}}{dFh}{c{{n{e}}}{}{}}{{}{{n{c}}}{}}{dFl}6`````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````{{{d{AhKn}}}{{n{BjAEh}}}}`````````````{{{d{Hd}}}{{Bl{C`}}}}{{{d{Kn}}}{{Bl{Cd}}}}{{{d{Hd}}}{{Bl{Cd}}}}{{{d{Kn}}}{{Bl{{d{{An{Kj}}}}}}}}{{{d{Df}}}{{d{{An{Kj}}}}}}{{{d{Hd}}}{{Bl{{d{{An{Kj}}}}}}}}{{{d{Df}}}{{Bl{Dj}}}}{{{d{Hd}}}{{Bl{Dj}}}}{{{d{Hd}}}{{Bl{Hn}}}}{{{d{Hd}}{d{Gd}}}{{n{{j{AEj}}l}}}}{{{d{Hd}}}{{n{{Bl{Nf}}l}}}}{{{d{Hd}}}{{Bl{I`}}}}{{{d{Kn}}}{{Bl{{d{Gd}}}}}}{{{d{Df}}}{{Bl{{d{Gd}}}}}}{{{d{Hd}}}{{Bl{{Gh{Gd}}}}}}{{{d{Df}}}{{Gh{Gd}}}}1{{{d{Hd}}}{{Bl{Fh}}}}{{{d{Hd}}}{{Bl{Dl}}}}{{{d{Hd}}}{{Bl{C`}}}}{{{d{Ed}}}Cn}````{{{d{Kn}}}{{Bl{d}}}}{{{d{AhKn}}}{{Bl{{d{Ah}}}}}}```````{d{{d{c}}}{}}000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000{{{d{Ah}}}{{d{Ahc}}}{}}000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000```{{{d{AEl}}}{{n{All}}}}`{{{d{AEl}}C`}{{n{bl}}}}{{{d{AEl}}}{{n{Bdl}}}}{{{d{AEl}}}{{n{Bfl}}}}`{{{d{Hd}}}Cd}```{{{d{AAb}}}AAb}{{{d{Nj}}}Nj}{{{d{Kn}}}Kn}{{{d{AEn}}}AEn}{{{d{AF`}}}AF`}{{{d{AFb}}}AFb}{{{d{AFd}}}AFd}{{{d{h}}}h}{{{d{AFf}}}AFf}{{{d{On}}}On}{{{d{AFh}}}AFh}{{{d{Ih}}}Ih}{{{d{AFj}}}AFj}{{{d{AFl}}}AFl}{{{d{Df}}}Df}{{{d{Nl}}}Nl}{{{d{AFn}}}AFn}{{{d{Hd}}}Hd}{{{d{AEl}}}AEl}{{{d{J`}}}J`}{{{d{In}}}In}{{{d{Jb}}}Jb}{{{d{Jd}}}Jd}{{{d{Jf}}}Jf}{{{d{Ob}}}Ob}{{{d{AG`}}}AG`}{{{d{AGb}}}AGb}{{{d{K`}}}K`}{{{d{Kb}}}Kb}{{{d{Kd}}}Kd}{{{d{AGd}}}AGd}{{{d{Ed}}}Ed}{{{d{AGf}}}AGf}{{{d{AGh}}}AGh}{{{d{AGj}}}AGj}{{{d{AGl}}}AGl}{{{d{AGn}}}AGn}{{{d{AH`}}}AH`}{{{d{Eb}}}Eb}{{{d{E`}}}E`}{{{d{Dn}}}Dn}{{{d{AHb}}}AHb}{{{d{AHd}}}AHd}{{{d{Ef}}}Ef}{{{d{Ej}}}Ej}{{{d{Nf}}}Nf}{{{d{N`}}}N`}{{{d{AHf}}}AHf}{{{d{Mn}}}Mn}{{{d{AHh}}}AHh}{{{d{Hl}}}Hl}{{{d{Oj}}}Oj}{{{d{AAf}}}AAf}{{{d{Oh}}}Oh}{{{d{Nn}}}Nn}{{{d{Kl}}}Kl}{{{d{Jn}}}Jn}{{{d{Ml}}}Ml}{{{d{A@f}}}A@f}{{{d{AAd}}}AAd}{{{d{AAn}}}AAn}{{{d{AHj}}}AHj}{{{d{AHl}}}AHl}{{{d{AHn}}}AHn}{{{d{AA`}}}AA`}{{{d{AAh}}}AAh}{{{d{Nd}}}Nd}{{{d{Mh}}}Mh}{{{d{Jh}}}Jh}{{{d{Fn}}}Fn}{{{d{AI`}}}AI`}{{{d{AIb}}}AIb}{{{d{AId}}}AId}{{{d{AIf}}}AIf}{{{d{AIh}}}AIh}{{{d{AIj}}}AIj}{{{d{AIl}}}AIl}{{{d{Lj}}}Lj}{{{d{AIn}}}AIn}{{{d{AJ`}}}AJ`}{{{d{AJb}}}AJb}{{{d{AJd}}}AJd}{{{d{AJf}}}AJf}{{{d{Mb}}}Mb}{{{d{AJh}}}AJh}{{{d{Ln}}}Ln}{{{d{AJj}}}AJj}{{{d{M`}}}M`}{{{d{Ll}}}Ll}{{{d{Md}}}Md}{{{d{En}}}En}{{{d{AJl}}}AJl}{{{d{AJn}}}AJn}{{{d{AEj}}}AEj}{{{d{AK`}}}AK`}{{{d{Id}}}Id}{{{d{ADj}}}ADj}{{{d{AEd}}}AEd}{{{d{AKb}}}AKb}{{{d{AKd}}}AKd}{{{d{AEb}}}AEb}{{{d{AKf}}}AKf}{{{d{ACl}}}ACl}{{{d{ACf}}}ACf}{{{d{AKh}}}AKh}{{{d{ACh}}}ACh}{{{d{ACj}}}ACj}{{{d{ADf}}}ADf}{{{d{ABb}}}ABb}{{{d{ABd}}}ABd}{{{d{ABf}}}ABf}{{{d{ABh}}}ABh}{{{d{ABl}}}ABl}{{{d{ACb}}}ACb}{{{d{ABn}}}ABn}{{{d{AKj}}}AKj}{{{d{AC`}}}AC`}{{{d{AKl}}}AKl}{{d{d{Ahc}}}Bj{}}000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000{dBj}000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000``{{{d{Df}}}Cl}`````{{{d{Df}}{d{c}}}{{Bl{h}}}Ad}`````````{{{d{AAb}}{d{AAb}}}AKn}{{{d{h}}{d{h}}}AKn}{{{d{Df}}{d{Df}}}AKn}{{{d{Oh}}{d{Oh}}}AKn}{{{d{AJl}}{d{AJl}}}AKn}{{{d{AJn}}{d{AJn}}}AKn}{{{d{AEj}}{d{AEj}}}AKn}{{{d{AK`}}{d{AK`}}}AKn}7`````{Df{{n{cl}}}AD`}{Hd{{n{cl}}}Af}```{{{d{{ALb{}{{AL`{c}}}}}}}Al{}}{{{d{F`}}}Al}{{{d{Db}}}Al}{{{d{Ff}}}Al}{{{d{G`}}}Al}{{{d{{ALb{}{{AL`{c}}}}}}}{{Bl{{Gh{Gd}}}}}{}}{{{d{F`}}}{{Bl{{Gh{Gd}}}}}}{{{d{Db}}}{{Bl{{Gh{Gd}}}}}}{{{d{Ff}}}{{Bl{{Gh{Gd}}}}}}{{{d{G`}}}{{Bl{{Gh{Gd}}}}}}``````{{}AEn}{{}AFj}{{}AEl}{{}AG`}{{}AGb}{{}Ed}{{}AGf}{{}AGh}{{}AGj}{{}AGl}{{}AGn}{{}AH`}{{}Eb}{{}E`}{{}Dn}{{}AHb}{{}AHd}{{}Ef}{{}Ej}{{}AHh}{{}AHj}{{}AHl}{{}Mh}{{}Lj}{{}AJb}{{}AJd}{{}Mb}{{}Ln}{{}AKd}{{}ABf}{{}AEl}?0?`{{}{{n{AFdl}}}}0{{}AGh}`{C`{{d{c}}}{}}000000000000000000000000{{{d{Nl}}}{{d{c}}}{}}111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111{C`{{d{Ahc}}}{}}000000000000000000000000{{{d{AhNl}}}{{d{Ahc}}}{}}111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111`````{C`Bj}000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000```{{{d{Kn}}}C`}``{{{d{AAb}}{d{AAb}}}Cd}{{{d{Nj}}{d{Nj}}}Cd}{{{d{Kn}}{d{Kn}}}Cd}{{{d{AEn}}{d{AEn}}}Cd}{{{d{AF`}}{d{AF`}}}Cd}{{{d{AFb}}{d{AFb}}}Cd}{{{d{AFd}}{d{AFd}}}Cd}{{{d{h}}{d{h}}}Cd}{{{d{AFf}}{d{AFf}}}Cd}{{{d{On}}{d{On}}}Cd}{{{d{AFh}}{d{AFh}}}Cd}{{{d{AFj}}{d{AFj}}}Cd}{{{d{AFl}}{d{AFl}}}Cd}{{{d{Df}}{d{Df}}}Cd}{{{d{Nl}}{d{Nl}}}Cd}{{{d{AFn}}{d{AFn}}}Cd}{{{d{Hd}}{d{Hd}}}Cd}{{{d{J`}}{d{J`}}}Cd}{{{d{In}}{d{In}}}Cd}{{{d{Jb}}{d{Jb}}}Cd}{{{d{Jd}}{d{Jd}}}Cd}{{{d{Jf}}{d{Jf}}}Cd}{{{d{Ob}}{d{Ob}}}Cd}{{{d{AG`}}{d{AG`}}}Cd}{{{d{AGb}}{d{AGb}}}Cd}{{{d{K`}}{d{K`}}}Cd}{{{d{Kb}}{d{Kb}}}Cd}{{{d{Kd}}{d{Kd}}}Cd}{{{d{AGd}}{d{AGd}}}Cd}{{{d{Ed}}{d{Ed}}}Cd}{{{d{AGf}}{d{AGf}}}Cd}{{{d{AGh}}{d{AGh}}}Cd}{{{d{AGj}}{d{AGj}}}Cd}{{{d{AGl}}{d{AGl}}}Cd}{{{d{AGn}}{d{AGn}}}Cd}{{{d{AH`}}{d{AH`}}}Cd}{{{d{Eb}}{d{Eb}}}Cd}{{{d{E`}}{d{E`}}}Cd}{{{d{Dn}}{d{Dn}}}Cd}{{{d{AHb}}{d{AHb}}}Cd}{{{d{Ej}}{d{Ej}}}Cd}{{{d{Nf}}{d{Nf}}}Cd}{{{d{N`}}{d{N`}}}Cd}{{{d{AHf}}{d{AHf}}}Cd}{{{d{Mn}}{d{Mn}}}Cd}{{{d{AHh}}{d{AHh}}}Cd}{{{d{Hl}}{d{Hl}}}Cd}{{{d{Oj}}{d{Oj}}}Cd}{{{d{AAf}}{d{AAf}}}Cd}{{{d{Oh}}{d{Oh}}}Cd}{{{d{Nn}}{d{Nn}}}Cd}{{{d{Kl}}{d{Kl}}}Cd}{{{d{Jn}}{d{Jn}}}Cd}{{{d{Ml}}{d{Ml}}}Cd}{{{d{A@f}}{d{A@f}}}Cd}{{{d{AAd}}{d{AAd}}}Cd}{{{d{AAn}}{d{AAn}}}Cd}{{{d{AHj}}{d{AHj}}}Cd}{{{d{AHl}}{d{AHl}}}Cd}{{{d{AHn}}{d{AHn}}}Cd}{{{d{AA`}}{d{AA`}}}Cd}{{{d{AAh}}{d{AAh}}}Cd}{{{d{Nd}}{d{Nd}}}Cd}{{{d{Mh}}{d{Mh}}}Cd}{{{d{Jh}}{d{Jh}}}Cd}{{{d{Fn}}{d{Fn}}}Cd}{{{d{AI`}}{d{AI`}}}Cd}{{{d{AIb}}{d{AIb}}}Cd}{{{d{AId}}{d{AId}}}Cd}{{{d{AIf}}{d{AIf}}}Cd}{{{d{AIh}}{d{AIh}}}Cd}{{{d{AIj}}{d{AIj}}}Cd}{{{d{AIl}}{d{AIl}}}Cd}{{{d{AJb}}{d{AJb}}}Cd}{{{d{AJd}}{d{AJd}}}Cd}{{{d{AJf}}{d{AJf}}}Cd}{{{d{AJh}}{d{AJh}}}Cd}{{{d{En}}{d{En}}}Cd}{{{d{AJl}}{d{AJl}}}Cd}{{{d{AJn}}{d{AJn}}}Cd}{{{d{AEj}}{d{AEj}}}Cd}{{{d{AK`}}{d{AK`}}}Cd}{{{d{Id}}{d{Id}}}Cd}{{{d{ADj}}{d{ADj}}}Cd}{{{d{AEd}}{d{AEd}}}Cd}{{{d{AKd}}{d{AKd}}}Cd}{{{d{AKf}}{d{AKf}}}Cd}{{{d{ACl}}{d{ACl}}}Cd}{{{d{ACf}}{d{ACf}}}Cd}{{{d{AKh}}{d{AKh}}}Cd}{{{d{ACh}}{d{ACh}}}Cd}{{{d{ACj}}{d{ACj}}}Cd}{{{d{ADf}}{d{ADf}}}Cd}{{{d{ABb}}{d{ABb}}}Cd}{{{d{ABd}}{d{ABd}}}Cd}{{{d{ABf}}{d{ABf}}}Cd}{{{d{ABh}}{d{ABh}}}Cd}{{{d{ABl}}{d{ABl}}}Cd}{{{d{ACb}}{d{ACb}}}Cd}{{{d{ABn}}{d{ABn}}}Cd}{{{d{AKj}}{d{AKj}}}Cd}{{{d{AC`}}{d{AC`}}}Cd}{{{d{AKl}}{d{AKl}}}Cd}{{{d{AJl}}{d{c}}eg}{{Cf{i}}}{LbALdALf}{{Dh{Fn}}ALd}{{H`{Gn}}ALd}Af}{{{d{AJl}}{d{Al}}ce}{{Cf{g}}}{{Dh{Fn}}ALd}{{H`{Gn}}ALd}Af}``{{{d{AhEj}}{d{Ej}}}{{d{AhEj}}}}``{{{d{AEj}}{d{c}}eg}{{Cf{i}}}{LfALdALf}{{Dh{Fn}}ALd}{{H`{Gn}}ALd}Af}0```{{{d{ALh}}{d{h}}{d{h}}}{{ALl{{ALj{A`}}}}}}0```{{{d{AEn}}{d{{An{Hd}}}}}{{Bl{{d{{An{Kj}}}}}}}}{{{d{AEj}}}{{d{{An{AJn}}}}}}{{HdC`}Hd}{{{d{AAb}}{d{AhCh}}}{{n{BjALn}}}}0{{{d{Nj}}{d{AhCh}}}{{n{BjALn}}}}{{{d{Kn}}{d{AhCh}}}{{n{BjALn}}}}{{{d{AEn}}{d{AhCh}}}Cj}{{{d{AF`}}{d{AhCh}}}Cj}{{{d{AFb}}{d{AhCh}}}Cj}{{{d{AFd}}{d{AhCh}}}Cj}{{{d{h}}{d{AhCh}}}Cj}0{{{d{AFf}}{d{AhCh}}}Cj}{{{d{On}}{d{AhCh}}}Cj}{{{d{AFh}}{d{AhCh}}}Cj}{{{d{Ih}}{d{AhCh}}}Cj}{{{d{AFj}}{d{AhCh}}}Cj}{{{d{AFl}}{d{AhCh}}}Cj}{{{d{Df}}{d{AhCh}}}Cj}{{{d{Nl}}{d{AhCh}}}Cj}{{{d{AFn}}{d{AhCh}}}Cj}0{{{d{Hd}}{d{AhCh}}}Cj}{{{d{AEl}}{d{AhCh}}}Cj}{{{d{J`}}{d{AhCh}}}Cj}{{{d{In}}{d{AhCh}}}Cj}{{{d{Jb}}{d{AhCh}}}Cj}{{{d{Jd}}{d{AhCh}}}Cj}{{{d{Jf}}{d{AhCh}}}Cj}{{{d{Ob}}{d{AhCh}}}Cj}{{{d{AG`}}{d{AhCh}}}Cj}{{{d{AGb}}{d{AhCh}}}Cj}{{{d{K`}}{d{AhCh}}}Cj}{{{d{Kb}}{d{AhCh}}}Cj}{{{d{Kd}}{d{AhCh}}}Cj}{{{d{AGd}}{d{AhCh}}}Cj}{{{d{Ed}}{d{AhCh}}}Cj}{{{d{AGf}}{d{AhCh}}}Cj}{{{d{AGh}}{d{AhCh}}}Cj}{{{d{AGj}}{d{AhCh}}}Cj}{{{d{AGl}}{d{AhCh}}}Cj}{{{d{AGn}}{d{AhCh}}}Cj}{{{d{AH`}}{d{AhCh}}}Cj}{{{d{Eb}}{d{AhCh}}}Cj}{{{d{E`}}{d{AhCh}}}Cj}{{{d{Dn}}{d{AhCh}}}Cj}{{{d{AHb}}{d{AhCh}}}Cj}{{{d{AHd}}{d{AhCh}}}Cj}{{{d{Ef}}{d{AhCh}}}Cj}{{{d{Ej}}{d{AhCh}}}Cj}{{{d{Nf}}{d{AhCh}}}Cj}{{{d{N`}}{d{AhCh}}}Cj}{{{d{AHf}}{d{AhCh}}}Cj}{{{d{Mn}}{d{AhCh}}}Cj}{{{d{AHh}}{d{AhCh}}}Cj}{{{d{Hl}}{d{AhCh}}}Cj}{{{d{Oj}}{d{AhCh}}}Cj}{{{d{AAf}}{d{AhCh}}}Cj}{{{d{Oh}}{d{AhCh}}}Cj}{{{d{Nn}}{d{AhCh}}}Cj}{{{d{Kl}}{d{AhCh}}}Cj}{{{d{Jn}}{d{AhCh}}}Cj}{{{d{Ml}}{d{AhCh}}}Cj}{{{d{A@f}}{d{AhCh}}}Cj}{{{d{AAd}}{d{AhCh}}}Cj}{{{d{AAn}}{d{AhCh}}}Cj}0{{{d{AHj}}{d{AhCh}}}Cj}{{{d{AHl}}{d{AhCh}}}Cj}{{{d{AHn}}{d{AhCh}}}Cj}{{{d{AA`}}{d{AhCh}}}Cj}{{{d{AAh}}{d{AhCh}}}Cj}{{{d{Nd}}{d{AhCh}}}Cj}{{{d{Mh}}{d{AhCh}}}Cj}{{{d{Jh}}{d{AhCh}}}Cj}{{{d{Fn}}{d{AhCh}}}Cj}{{{d{AI`}}{d{AhCh}}}Cj}{{{d{AIb}}{d{AhCh}}}Cj}{{{d{AId}}{d{AhCh}}}Cj}{{{d{AIf}}{d{AhCh}}}Cj}{{{d{AIh}}{d{AhCh}}}Cj}{{{d{AIj}}{d{AhCh}}}Cj}{{{d{AIl}}{d{AhCh}}}Cj}{{{d{Lj}}{d{AhCh}}}Cj}{{{d{AIn}}{d{AhCh}}}Cj}{{{d{AJ`}}{d{AhCh}}}Cj}{{{d{AJb}}{d{AhCh}}}Cj}{{{d{AJd}}{d{AhCh}}}Cj}{{{d{AJf}}{d{AhCh}}}Cj}{{{d{Mb}}{d{AhCh}}}Cj}{{{d{AJh}}{d{AhCh}}}Cj}{{{d{Ln}}{d{AhCh}}}Cj}{{{d{AJj}}{d{AhCh}}}Cj}{{{d{M`}}{d{AhCh}}}Cj}{{{d{Ll}}{d{AhCh}}}Cj}{{{d{Md}}{d{AhCh}}}Cj}{{{d{En}}{d{AhCh}}}Cj}{{{d{AJl}}{d{AhCh}}}Cj}0{{{d{AJn}}{d{AhCh}}}Cj}{{{d{AEj}}{d{AhCh}}}Cj}0{{{d{AK`}}{d{AhCh}}}Cj}0{{{d{Id}}{d{AhCh}}}Cj}{{{d{ADj}}{d{AhCh}}}Cj}{{{d{AEd}}{d{AhCh}}}Cj}{{{d{AKb}}{d{AhCh}}}Cj}{{{d{AKd}}{d{AhCh}}}Cj}{{{d{AEb}}{d{AhCh}}}Cj}{{{d{AKf}}{d{AhCh}}}Cj}{{{d{ACl}}{d{AhCh}}}Cj}{{{d{ACf}}{d{AhCh}}}Cj}{{{d{AKh}}{d{AhCh}}}Cj}{{{d{ACh}}{d{AhCh}}}Cj}{{{d{ACj}}{d{AhCh}}}Cj}{{{d{ADf}}{d{AhCh}}}Cj}{{{d{ABb}}{d{AhCh}}}Cj}{{{d{ABd}}{d{AhCh}}}Cj}{{{d{ABf}}{d{AhCh}}}Cj}{{{d{ABh}}{d{AhCh}}}Cj}{{{d{ABl}}{d{AhCh}}}Cj}{{{d{ACb}}{d{AhCh}}}Cj}{{{d{ABn}}{d{AhCh}}}Cj}{{{d{AKj}}{d{AhCh}}}Cj}{{{d{AC`}}{d{AhCh}}}Cj}{{{d{AKl}}{d{AhCh}}}Cj}```{cc{}}0{CdKn}1{HnKn}{I`Kn}33{{{d{{An{Cl}}}}}Jj}{{{AM`{Cl}}}Jj}5{{{j{Cl}}}Jj}{ClJj}7777{HnAE`}{{{Bl{Hn}}}AE`}9{{{j{Hn}}}AE`}{{{AM`{Hn}}}AE`};{{{Bl{Cl}}}AEn}<{ClAEn}{{{d{Gd}}}AEn}{{{d{{An{Kj}}}}}AEn}?{cAFb{{Dh{AFd}}}}{cc{}}{AMbAFd}1{AMdAFd}{AMfAFd}{AMhAFd}{{{Ld{FhCl}}}h}{{{Ld{{d{Gd}}Cl}}}h}{{{d{h}}}h}777777{DlAFl}{FhAFl}{AMjAFl}{KjAFl}{ClAFl}{AMlAFl}{CnAFl}{I`AFl}{{{d{Gd}}}AFl}{DjAFl}{C`AFl}{AMnAFl}{AN`AFl}{cc{}}{HnAFl}{ANbAFl}{HnDf}3{DjDf}{I`Df}{CnDf}{{{d{Fh}}}Df}{{{d{Gd}}}Df}{FhDf}{{{d{{An{Kj}}}}}Df}{{{ALj{{An{Kj}}}}}Df}{MfDf}{ANdDf}{AMjDf}{{{d{Dj}}}Df}{AMlDf}{ClDf}{{{d{Df}}}Df}{CdDf}{ANbDf}{AN`Df}{C`Df}{ANfDf}{KjDf}{DlDf}{AMnDf}{cc{}}{{{d{Nl}}}Nl}{BjNl}2{DfHd}{{{Ld{ceg}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{{{Ld{cegikmoAa}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{{{Ld{cegikmoAaAc}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}6{{{Ld{cegikmo}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{{{Ld{cegikmoAaAcAe}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{{{Ld{cegikmoAaAcAeAg}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{KjHd}{{{Ld{cegikmoAaAcAeAgAi}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{ClHd}{CnHd}{AN`Hd}{ANbHd}{AMlHd}{I`Hd}{{{Ld{cegikmoAaAcAeAgAiAk}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{AMnHd}{HnHd}{DjHd}{MfHd}{{{ALj{{An{Kj}}}}}Hd}{{{Ld{cegikm}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{FhHd}{{{Ld{cegikmoAaAcAeAgAiAkAm}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{{{d{Fh}}}Hd}{{{d{Gd}}}Hd}{{{d{{An{Kj}}}}}Hd}{CdHd}{{{Ld{cegik}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{{{Ld{cegikmoAaAcAeAgAiAkAmAo}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{{{Ld{ce}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}}{{{Ld{cegikmoAaAcAeAgAiAkAmAoBa}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{NlHd}{{{Ld{cegi}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{{{Ld{cegikmoAaAcAeAgAiAkAmAoBaBc}}}Hd{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}{{Dh{Hd}}}}{BjHd}{cc{}}0000{CdJf}11111111111111111111111{{{Ld{HnHn}}}Nf}22{AHfMn}{{{AM`{AHf}}}Mn}4{{{j{AHf}}}Mn}55555555555555555555{{{j{c}}}Fn{{Dh{Df}}}}{{{Ld{cegi}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{{{Ld{cegikmoAaAcAeAgAiAkAm}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{{{Ld{cegikmoAaAcAeAgAiAkAmAoBa}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{{{Ld{cegikmoAaAcAeAgAiAk}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{{{Ld{cegikmoAaAcAe}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{{{Ld{cegikmoAaAc}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{{{Ld{cegikmo}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{{{Ld{cegikmoAaAcAeAgAi}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{{{Ld{cegik}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}?{{{Ld{cegikm}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{{{Ld{ce}}}Fn{{Dh{Df}}}{{Dh{Df}}}}{{{Ld{ceg}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{{{Bl{Df}}}Fn}{{{AM`{c}}}Fn{{Dh{Df}}}}{BjFn}{{{Ld{cegikmoAa}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{{{Ld{cegikmoAaAcAeAgAiAkAmAo}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{cFn{{Dh{Df}}}}{{{Ld{cegikmoAaAcAeAg}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{{{d{{ANh{c}}}}}Fn{{Dh{Df}}Bb}}{{{Ld{cegikmoAaAcAeAgAiAkAmAoBaBc}}}Fn{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}{{Dh{Df}}}}{cc{}}000000000000000000000000000{I`AKb}{{{d{Gd}}}AKb}2{FhAKb}{{{d{Fh}}}AKb}44{I`AEb}{{{d{Gd}}}AEb}{FhAEb}{{{d{Fh}}}AEb}{{{d{AEb}}}AEb}99{BjACl}{cACf{{Dh{ACj}}}}{{{j{c}}}ACf{{Dh{ACj}}}}<{{{AM`{c}}}ACf{{Dh{ACj}}}}={{{Bl{Bj}}}ACh}>{{{d{Fh}}}ACj}{DjACj}{cc{}}{FhACj}{{{d{Gd}}}ACj}{BjADf}{{{Ld{ceDl}}}ADf{{Dh{ACj}}}{{Dh{ACj}}}}{{{Ld{DlceDl}}}ADf{{Dh{ACj}}}{{Dh{ACj}}}}{{{Ld{DlceDlg}}}ADf{{Dh{ACj}}}{{Dh{ACj}}}{{Dh{Dj}}}}6{{{Ld{ceDlg}}}ADf{{Dh{ACj}}}{{Dh{ACj}}}{{Dh{Dj}}}}77{I`ABf}88{{{j{c}}}ABl{{Dh{Dj}}}}9{{{ANh{c}}}ABl{{Dh{Dj}}}}{I`ACb};;{{{Ld{ABhDl}}}ABn}<{{{Ld{cAKj}}}AC`{{Dh{Dj}}}}=={{ANjcBl}{{n{KnAEh}}}{{ABj{}{{D`{Kn}}}}}}{{Hdc}{{n{Ihl}}}{{Dh{Dj}}}}{{{d{Al}}c}{{n{AK`l}}}{{Dh{Dj}}}}{DnAEl}{cAJl{{Dh{Dj}}}}{cJj{{ABj{}{{D`{Cl}}}}}}{cAE`{{ABj{}{{D`{Hn}}}}}}{eADl{{Dh{Hd}}}{{ABj{}{{D`{{Ld{Hnc}}}}}}}}{gNl{{Dh{Df}}}{{Dh{Hd}}}{{ABj{}{{D`{{Ld{ce}}}}}}}}{eHd{{Dh{Hd}}}{{ABj{}{{D`{c}}}}}}{eFn{{Dh{Df}}}{{ABj{}{{D`{c}}}}}}{eABl{{Dh{Dj}}}{{ABj{}{{D`{c}}}}}}{Df{{n{AD`l}}}}{Df{{n{Dfl}}}}{Df{{n{Hdl}}}}:<{{HdCdCdCd}{{n{AHhl}}}}{{{d{{An{Kj}}}}}Df}{{{d{{An{Kj}}}}}Hd}{{{d{Gd}}}AFl}{{{d{Gd}}}Df}{{{d{Gd}}}Hd}{{{d{Gd}}}{{n{AAb}}}}{{{d{Gd}}}{{Bl{AJn}}}}{{{d{Gd}}}{{n{Dnl}}}}000{Hd{{n{Afl}}}}{Hd{{n{Dfl}}}}{Hd{{n{Hdl}}}}{Hd{{n{AGbl}}}}{Hd{{n{Nfl}}}}{Hd{{n{AHjl}}}}{Hd{{n{AHll}}}}{Hd{{n{AHnl}}}}``{{{d{AK`}}}{{d{{Eh{DjAEj}}}}}}`{{{d{AEl}}}{{Bl{{d{Dn}}}}}}{{{d{AEl}}}{{d{Eb}}}}{{{d{c}}{d{e}}}Dl{ADbANl}ANn}000000000{{{d{AEl}}}{{d{E`}}}}{{{d{AEl}}}{{Bl{{d{Ed}}}}}}4{{{d{Ih}}Cl}{{Bl{{d{h}}}}}}`{{{d{{ALb{}{{AL`{c}}}}}}}Cd{}}{{{d{F`}}}Cd}{{{d{Db}}}Cd}{{{d{Ff}}}Cd}{{{d{G`}}}Cd}{{{d{AAb}}{d{Ahc}}}BjAO`}{{{d{Kn}}{d{Ahc}}}BjAO`}{{{d{AEn}}{d{{An{Hd}}}}}{{Bl{Cl}}}}{{{d{h}}{d{Ahc}}}BjAO`}{{{d{Df}}{d{Ahc}}}BjAO`}{{{d{Hd}}{d{Ahc}}}BjAO`}{{{d{Oh}}{d{Ahc}}}BjAO`}{{{d{AJl}}{d{Ahc}}}BjAO`}{{{d{AJn}}{d{Ahc}}}BjAO`}{{{d{AEj}}{d{Ahc}}}BjAO`}{{{d{AK`}}{d{Ahc}}}BjAO`}`{{{d{{An{Kj}}}}}Cl}````{{{d{AHb}}}{{j{h}}}}``````{{}C`}000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000`{Jj{{j{Cl}}}}{AE`{{j{Hn}}}}{ADl{{j{{Ld{HnHd}}}}}}{{{d{Df}}}{{d{Mf}}}}{Nl{{Eh{DfHd}}}}{Mn{{j{AHf}}}}{Fn{{j{Df}}}}{ACl{{j{{Ld{DfHd}}}}}}{ACf{{j{ACj}}}}```{{}c{}}000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000{Hd{{j{Hd}}}}{DfMf}{Hd{{Bl{Mf}}}}{Hd{{Bl{Dj}}}}{{HdCdCdCd}{{n{{j{AHh}}l}}}}{Hd{{n{HdHd}}}}{Hd{{n{Ofl}}}}{Hd{{n{Nll}}}}{Hd{{Bl{{j{Kj}}}}}}{Hd{{n{{AOb{Hd}}l}}}}{Df{{Bl{Fh}}}}{Hd{{Bl{Fh}}}}{Fn{{j{Hd}}}}{Hd{{n{{Ld{Fh{j{{ACn{ceg}}}}}}l}}}Af{AD`ADbADd}Af}{Hd{{n{{ADh{cegi}}l}}}{AD`ADbADd}Af{AD`ADbADd}Af}{Hd{{n{{j{{ACn{ceg}}}}l}}}Af{AD`ADbADd}Af}{Hd{{n{{j{{Ld{HdHn}}}}l}}}}`{{{d{Hd}}}Cd}000{{{d{AHb}}}Cd}01{{{d{Kn}}}Cd}222022021021`````{{{d{Kn}}}ANj}{{{d{Hd}}}AFn}````````{{{d{Kn}}}C`}{{{d{Jj}}}C`}{{{d{AE`}}}C`}{{{d{ADl}}}C`}{{{d{Ih}}}C`}{{{d{Nl}}}C`}{{{d{Mn}}}C`}{{{d{Fn}}}C`}{{{d{ACl}}}C`}{{{d{ACf}}}C`}```{{{d{AJl}}{d{Al}}}{{Cf{Bj}}}}``{{{d{AJl}}}{{Bl{{d{Dj}}}}}}``{{{d{AOd}}{d{A@`}}{d{Gd}}}{{Bl{Fh}}}}`````````````````{{{d{AEj}}}{{d{Dj}}}}{{{d{AK`}}}{{d{Dj}}}}```{{DlDlDl}AAb}{{}AE`}{{}ADl}{{cCl}h{{Dh{Dj}}}}{{}Ih}{{}Nl}{CdAHd}{{ceCd}Kl{{Dh{Dj}}}{{Dh{AEn}}}}{{}Fn}{{c{j{AJn}}}AEj{{Dh{Dj}}}}{{cCl}AHb{{Dh{Fh}}}}{{{j{{Ld{cCl}}}}}AHb{{Dh{Fh}}}}{{CnCn}Ed}{{}Kn}0{{CnCnCnCn}Ed}{{CnCnCn}Ed}{{}Hd}{{{j{{Ld{cCl}}}}e}AHb{{Dh{Fh}}}{{Dh{Fh}}}}{{{d{Gd}}cCd}Kl{{Dh{AEn}}}}{{cCl{Bl{Fh}}}h{{Dh{Dj}}}}{{{ALb{}{{AL`{c}}}}}{{n{Bjl}}}{}}{F`{{n{Bjl}}}}{Db{{n{Bjl}}}}{Ff{{n{Bjl}}}}{G`{{n{Bjl}}}}{{{d{AhEd}}}{{Bl{Dl}}}}``````````````````{{{d{Gd}}}{{n{AAbAOf}}}}{{{d{AAb}}{d{AAb}}}{{Bl{AKn}}}}{{{d{h}}{d{h}}}{{Bl{AKn}}}}{{{d{Df}}{d{Df}}}{{Bl{AKn}}}}{{{d{Oh}}{d{Oh}}}{{Bl{AKn}}}}{{{d{AJl}}{d{AJl}}}{{Bl{AKn}}}}{{{d{AJn}}{d{AJn}}}{{Bl{AKn}}}}{{{d{AEj}}{d{AEj}}}{{Bl{AKn}}}}{{{d{AK`}}{d{AK`}}}{{Bl{AKn}}}}```````````````{{{d{Ih}}}{{AOh{h{Ld{{j{{Ld{ClCl}}}}{Fj{h}}}}}}}}````{{{d{Ih}}}{{Bl{{d{h}}}}}}{{{d{Ih}}}{{Bl{{d{AFh}}}}}}`````{{{d{Ih}}{d{h}}}{{j{h}}}}``{{{d{Fb}}DjCl}{{ALl{{ALj{A`}}}}}}{{{d{{ALb{}{{AL`{c}}}}}}}{{d{{Bl{c}}}}}{}}{{{d{F`}}}{{d{{Bl{c}}}}}{}}{{{d{Db}}}{{d{{Bl{c}}}}}{}}{{{d{Ff}}}{{d{{Bl{c}}}}}{}}{{{d{G`}}}{{d{{Bl{c}}}}}{}}````````````{{{d{AhAHb}}AH`}{{n{Bjl}}}}{{{d{AhAEl}}Dn}{{d{AhAEl}}}}{{{d{AhAEl}}Eb}{{d{AhAEl}}}}{{{d{AhEd}}Cn}Bj}{{{d{AhAEl}}E`}{{d{AhAEl}}}}{{{d{AhAEl}}Ed}{{d{AhAEl}}}}{{{d{AhAEl}}Ef}{{d{AhAEl}}}}{{{d{AJl}}}{{d{Dj}}}}```{{{d{Ih}}}{{d{{An{AFh}}}}}}`````````{{{d{AhKn}}}Kn}{{{d{AhDf}}}Mf}{{{d{AhNl}}}Nl}{{{d{AhHd}}}Hd}{{{d{AhKn}}}Bl}{{{d{Ah{ALb{}{{AL`{c}}}}}}}{{Bl{c}}}{}}{{{d{AhF`}}}{{Bl{c}}}{}}{{{d{AhDb}}}{{Bl{c}}}{}}{{{d{AhFf}}}{{Bl{c}}}{}}{{{d{AhG`}}}{{Bl{c}}}{}}`````````{{{d{Nj}}}Kj}{dc{}}000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000{{{d{Kn}}}AOj}{{{d{AJn}}}{{d{Gd}}}}{dFh}{{{d{Kn}}}{{Bl{Fh}}}}111111```{c{{n{e}}}{}{}}00{{{Ld{ANjc}}}{{n{Kn}}}{{Dh{Mf}}}}111111111{{{j{{Ld{Hnc}}}}}{{n{ADle}}}{{H`{Hd}}}{}}{{{Ld{Hnc}}}{{n{ADle}}}{{H`{Hd}}}{}}{{{AM`{{Ld{Hnc}}}}}{{n{ADle}}}{{H`{Hd}}}{}}4444{AOl{{n{AFdc}}}{}}{{{d{Gd}}}{{n{hc}}}{}}{Fh{{n{hc}}}{}}7777777{Hd{{n{AFlc}}}{}}{Hd{{n{Dfc}}}{}}9{{{j{{Ld{ce}}}}}{{n{Nlg}}}{{H`{Df}}}{{H`{Hd}}}{}}{{{AM`{{Ld{ce}}}}}{{n{Nlg}}}{{H`{Df}}}{{H`{Hd}}}{}}{{{d{{ANh{{Ld{ce}}}}}}}{{n{Nlg}}}{{H`{Df}}Bb}{{H`{Hd}}Bb}{}}{{{ANh{{Ld{ce}}}}}{{n{Nlg}}}{{H`{Df}}}{{H`{Hd}}}{}}{{{Ld{ce}}}{{n{Nlg}}}{{H`{Df}}}{{H`{Hd}}}{}}{{{AOh{ce}}}{{n{Nlg}}}{{H`{Df}}}{{H`{Hd}}}{}}{{{Eh{ce}}}{{n{Nlg}}}{{H`{Df}}}{{H`{Hd}}}{}}{c{{n{e}}}{}{}}0{ANf{{n{Hdc}}}{}}{Kn{{n{Hdc}}}{}}{{{Bl{c}}}{{n{Hde}}}{{H`{Hd}}}{}}3{{{d{{ANh{c}}}}}{{n{Hde}}}{{H`{Hd}}Bb}{}}{C`{{n{Hdc}}}{}}{{{ANh{c}}}{{n{Hde}}}{{H`{Hd}}Bb}{}}{ANd{{n{Hdc}}}{}}{{{j{c}}}{{n{Hde}}}{{H`{Hd}}}{}}{{{AM`{c}}}{{n{Hde}}}{{H`{Hd}}}{}}{{{Eh{ce}}}{{n{Hdg}}}{{H`{Df}}}{{H`{Hd}}}{}}{Dl{{n{Hdc}}}{}}{{{AOh{ce}}}{{n{Hdg}}}{{H`{Df}}}{{H`{Hd}}}{}}<<<<<{Fh{{n{Jfc}}}{}}={{{d{Gd}}}{{n{Jfc}}}{}}{{{d{Fh}}}{{n{Jfc}}}{}}???{Hd{{n{AGbc}}}{}}{c{{n{e}}}{}{}}000000000000000000{Hd{{n{Nfc}}}{}}11{{{Ld{HnHnc}}}{{n{AHfe}}}{{H`{Hd}}}{}}22222222222222{Hd{{n{AHjc}}}{}}{Hd{{n{AHlc}}}{}}4{Hd{{n{AHnc}}}{}}5555{Dj{{n{Mhc}}}{}}{{{d{Dj}}}{{n{Mhc}}}{}}7{Fh{{n{Mhc}}}{}}{{{d{Fh}}}{{n{Mhc}}}{}}{{{d{Gd}}}{{n{Mhc}}}{}}:::::::::::::::::::::::::::::::{Hn{{n{AKbc}}}{}};{Hn{{n{AEbc}}}{}}<<{{{d{Gd}}}{{n{AKfc}}}{}}={{{Eh{ce}}}{{n{AClg}}}{{Dh{Df}}}{{H`{Hd}}}{}}{{{AM`{{Ld{ce}}}}}{{n{AClg}}}{{Dh{Df}}}{{H`{Hd}}}{}}{{{j{{Ld{ce}}}}}{{n{AClg}}}{{Dh{Df}}}{{H`{Hd}}}{}}{{{Ld{ce}}}{{n{AClg}}}{{Dh{Df}}}{{H`{Hd}}}{}}{c{{n{e}}}{}{}}{{{d{Gd}}}{{n{AKhc}}}{}}1{{{Ld{ceg{Bl{I`}}}}}{{n{AChi}}}{{H`{AKh}}}{{H`{AKf}}}{{Dh{AFl}}}{}}2{{{Ld{ceg}}}{{n{AChi}}}{{H`{AKh}}}{{H`{AKf}}}{{Dh{AFl}}}{}}{{{Ld{ce}}}{{n{AChg}}}{{H`{AKh}}}{{Dh{AFl}}}{}}44444{{{d{Gd}}}{{n{ABfc}}}{}}{Fh{{n{ABfc}}}{}}{Dj{{n{ABfc}}}{}}777{{{d{Gd}}}{{n{ACbc}}}{}}888{{{d{Gd}}}{{n{AKlc}}}{}}9{{}{{n{c}}}{}}000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000`{dFl}000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000{{{d{Ih}}}{{j{Cl}}}}{{{d{Ih}}}{{j{h}}}}````{{{d{Dn}}}Cd}00````{{{d{Kn}}}{{Bl{{d{AOn}}}}}}`{{}c{}}000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000{{{d{AhAEl}}c}{{d{AhAEl}}}{{B@`{{d{AhDn}}}}}}{{{d{AhAEl}}c}{{d{AhAEl}}}{{B@`{{d{AhEb}}}}}}{{{d{AhAEl}}c}{{d{AhAEl}}}{{B@`{{d{AhE`}}}}}}{{{d{AhAEl}}c}{{d{AhAEl}}}{{B@`{{d{AhEf}}}}}}`````````````````````````````````````````````````````````````````````````````````````````````{Hn{{n{Hdl}}}}{e{{n{{AOh{Cl{AM`{Df}}}}l}}}{{H`{Df}}}{{ABj{}{{D`{c}}}}}}{{{d{{An{Kj}}}}}Cl}{{{d{Gd}}}{{n{Hnl}}}}{{{d{Gd}}}Fh}{{{d{{An{Kj}}}}}Mf}{{{d{Gd}}}Dj}","D":"MHn","p":[[5,"RedisPool",17,4752],[1,"reference"],[17,"Output"],[5,"Server",1236,4753],[5,"Vec",4754],[5,"RedisError",227],[6,"Result",4755],[10,"Future",4756],[5,"Pipeline",17,4757],[10,"ClientLike",296,4758],[10,"FromRedis",1236,4759],[0,"mut"],[5,"Replicas",17,4760],[5,"RedisClient",17,4761],[1,"slice"],[5,"WithOptions",17,4762],[10,"Clone",4763],[5,"SentinelClient",17,4764],[5,"SubscriberClient",17,4765],[5,"Transaction",17,4766],[1,"unit"],[6,"Option",4767],[8,"ConnectHandle",1236],[1,"usize"],[5,"Duration",4768],[1,"bool"],[8,"RedisResult",296],[5,"Formatter",4769],[8,"Result",4769],[1,"u16"],[1,"u32"],[17,"Item"],[5,"HScanResult",1236,4770],[10,"Stream",4771],[5,"RedisKey",1236,4772],[10,"Into",4773],[8,"Str",4774],[1,"u64"],[5,"RedisConfig",1236,4775],[5,"PerformanceConfig",1236,4775],[5,"ConnectionConfig",1236,4775],[6,"ReconnectPolicy",1236,4775],[5,"SentinelConfig",1236,4775],[5,"HashMap",4776],[5,"Options",1236,4775],[8,"MultipleStrings",1236,4777],[6,"ScanType",1236,4770],[5,"ScanResult",1236,4770],[10,"Resolve",1236,4753],[5,"Rc",4778],[5,"SScanResult",1236,4770],[5,"String",4779],[5,"BTreeSet",4780],[5,"TypeId",4781],[5,"MultipleKeys",1236,4777],[5,"ZScanResult",1236,4770],[6,"RedisErrorKind",227],[1,"str"],[5,"Error",4782],[6,"Cow",4783],[10,"Error",4784],[10,"AclInterface",296,4785],[8,"MultipleValues",1236,4777],[10,"TryInto",4773],[10,"KeysInterface",296,4786],[6,"RedisValue",1236,4772],[10,"AuthInterface",296],[10,"ServerInterface",296,4787],[10,"ListInterface",296,4788],[6,"LMoveDirection",1236,4789],[1,"f64"],[1,"i64"],[10,"SortedSetsInterface",296,4790],[6,"ZCmp",1236,4791],[10,"ClusterInterface",296,4792],[5,"ClusterRouting",1236,4753],[10,"SentinelInterface",296,4793],[10,"ClientInterface",296,4794],[6,"ClientKillFilter",1236,4795],[6,"ClientKillType",1236,4795],[6,"ClientPauseKind",1236,4795],[6,"ClientReplyFlag",1236,4795],[6,"Toggle",1236,4795],[6,"ClientUnblockFlag",1236,4796],[5,"MultipleHashSlots",1236,4777],[10,"EventInterface",296],[6,"ClusterStateChange",1236,4796],[6,"ClusterFailoverFlag",1236,4797],[6,"ClusterResetFlag",1236,4797],[6,"ClusterSetSlotState",1236,4797],[10,"MetricsInterface",296,4798],[10,"ConfigInterface",296,4799],[1,"u8"],[5,"CustomCommand",1236,4796],[6,"Resp3Frame",1236],[10,"HeartbeatInterface",296],[10,"LuaInterface",296,4800],[1,"tuple"],[10,"FunctionInterface",296,4800],[10,"RediSearchInterface",296,4801],[5,"FtAggregateOptions",1236,4802],[5,"FtAlterOptions",1236,4802],[5,"FtCreateOptions",1236,4802],[5,"SearchSchema",1236,4802],[5,"FtSearchOptions",1236,4802],[6,"SpellcheckTerms",1236,4802],[5,"Bytes",4803],[6,"FnPolicy",1236,4796],[10,"GeoInterface",296,4804],[6,"SetOptions",1236,4796],[5,"MultipleGeoValues",1236,4805],[6,"GeoUnit",1236,4805],[8,"Any",1236],[6,"SortOrder",1236,4796],[5,"GeoPosition",1236,4805],[10,"HashesInterface",296,4806],[6,"RespVersion",1236,4807],[5,"RedisMap",1236,4772],[6,"InfoKind",1236,4796],[10,"TrackingInterface",296,4808],[5,"Invalidation",1236,4795],[10,"RedisJsonInterface",296,4809],[6,"Value",4810],[5,"KeyspaceEvent",1236,4796],[6,"ListLocation",1236,4789],[10,"MemoryInterface",296,4811],[5,"Message",1236,4753],[6,"IpAddr",4812],[10,"TransactionInterface",296,4813],[10,"Fn",4814],[6,"ExpireOptions",1236,4796],[10,"HyperloglogInterface",296,4815],[10,"PubsubInterface",296,4816],[5,"Stats",1236,4817],[10,"SetsInterface",296,4818],[6,"ScriptDebugFlag",1236,4796],[5,"Version",1236,4819],[6,"Expiration",1236,4796],[6,"ShutdownFlags",1236,4796],[6,"SentinelFailureKind",1236,4796],[10,"SlowlogInterface",296,4820],[8,"Limit",1236],[6,"ClientState",1236,4796],[10,"TimeSeriesInterface",296,4821],[6,"Encoding",1236,4822],[6,"DuplicatePolicy",1236,4822],[6,"Timestamp",1236,4822],[6,"Aggregator",1236,4822],[10,"IntoIterator",4823],[6,"GetLabels",1236,4822],[5,"RangeAggregation",1236,4822],[5,"GroupBy",1236,4822],[6,"GetTimestamp",1236,4822],[10,"StreamsInterface",296,4824],[5,"MultipleIDs",1236,4825],[5,"XCap",1236,4825],[6,"XID",1236,4825],[5,"MultipleOrderedPairs",1236,4825],[8,"XReadValue",1236,4825],[10,"FromRedisKey",1236,4759],[10,"Hash",4826],[10,"Eq",4827],[5,"XPendingArgs",1236,4825],[8,"XReadResponse",1236,4825],[6,"Ordering",1236,4791],[5,"MultipleZaddValues",1236,4791],[6,"AggregateOptions",1236,4796],[5,"MultipleWeights",1236,4791],[5,"ZRange",1236,4791],[6,"ZSort",1236,4791],[5,"Command",1186],[5,"RedisProtocolError",4828],[5,"Function",1236,4829],[5,"Builder",1236,4830],[6,"ClusterHash",1236,4831],[6,"TlsHostMapping",1236,4832],[5,"TlsConfig",1236,4832],[6,"TlsConnector",1236,4832],[6,"MessageKind",1236,4753],[5,"SlotRange",1236,4753],[5,"ReplicaConfig",1236,4833],[6,"StringOrNumber",1236,4772],[6,"RedisValueKind",1236,4772],[6,"ClusterState",1236,4797],[5,"ClusterInfo",1236,4797],[6,"ReconnectError",1236,4775],[6,"Blocking",1236,4775],[6,"BackpressurePolicy",1236,4775],[5,"BackpressureConfig",1236,4775],[5,"TcpConfig",1236,4775],[5,"UnresponsiveConfig",1236,4775],[6,"ClusterDiscoveryPolicy",1236,4775],[6,"ServerConfig",1236,4775],[5,"TracingConfig",1236,4775],[5,"GeoValue",1236,4805],[5,"GeoRadiusInfo",1236,4805],[5,"DatabaseMemoryStats",1236,4796],[5,"MemoryStats",1236,4796],[5,"SlowlogEntry",1236,4796],[6,"ReducerFunc",1236,4802],[5,"SearchReducer",1236,4802],[5,"SearchField",1236,4802],[6,"Load",1236,4802],[5,"WithCursor",1236,4802],[5,"SearchParameter",1236,4802],[6,"AggregateOperation",1236,4802],[5,"SearchFilter",1236,4802],[5,"SearchGeoFilter",1236,4802],[5,"SearchSummarize",1236,4802],[5,"SearchHighlight",1236,4802],[5,"SearchSortBy",1236,4802],[6,"IndexKind",1236,4802],[6,"SearchSchemaKind",1236,4802],[5,"Script",1236,4829],[6,"FunctionFlag",1236,4829],[5,"Library",1236,4829],[6,"ZRangeBound",1236,4791],[6,"ZRangeKind",1236,4791],[6,"XCapTrim",1236,4825],[6,"XCapKind",1236,4825],[6,"Reducer",1236,4822],[6,"BucketTimestamp",1236,4822],[6,"Ordering",4827],[17,"Page"],[10,"Scanner",1236,4770],[10,"Send",4834],[10,"Sync",4834],[10,"ReplicaFilter",1236,4833],[5,"Box",4835],[5,"Pin",4836],[5,"Error",4769],[5,"VecDeque",4837],[5,"TlsConnector",4838],[5,"TlsConnector",4839],[5,"TlsConnector",4840],[5,"ClientConfig",4841],[1,"isize"],[1,"i32"],[1,"f32"],[1,"i8"],[1,"i16"],[1,"i128"],[1,"u128"],[1,"array"],[6,"FrameKind",4807],[10,"Sized",4834],[10,"BuildHasher",4826],[10,"Hasher",4826],[5,"HashSet",4842],[10,"HostMapping",1236,4832],[5,"Error",4843],[5,"BTreeMap",4844],[6,"OwnedFrame",4807],[5,"TlsConnectorBuilder",4838],[6,"VerbatimStringFormat",4807],[10,"FnOnce",4814],[15,"BlobString",1156],[15,"BlobError",1156],[15,"SimpleString",1156],[15,"SimpleError",1156],[15,"Boolean",1156],[15,"Number",1156],[15,"Double",1156],[15,"BigNumber",1156],[15,"VerbatimString",1156],[15,"Array",1156],[15,"Map",1156],[15,"Set",1156],[15,"Push",1156],[15,"Hello",1156],[8,"LimitCount",1236],[15,"Filter",4656],[15,"Apply",4656],[15,"GroupBy",4656],[15,"SortBy",4656],[15,"Limit",4656],[15,"Sleep",4665],[15,"Constant",4667],[15,"Linear",4667],[15,"Exponential",4667],[15,"Custom",4712],[15,"Tag",4712],[15,"Text",4712],[15,"Numeric",4712],[15,"Geo",4712],[15,"Vector",4712],[15,"GeoShape",4712],[15,"Clustered",4735],[15,"Sentinel",4735],[15,"Centralized",4735],[15,"Include",4740],[15,"Exclude",4740]],"r":[[17,4757],[18,4761],[19,4752],[20,4760],[21,4764],[22,4765],[23,4766],[24,4762],[296,4785],[304,4794],[305,4758],[306,4792],[307,4799],[311,4800],[312,4804],[313,4806],[316,4815],[317,4786],[318,4788],[319,4800],[321,4811],[322,4798],[326,4816],[328,4801],[329,4809],[332,4793],[333,4787],[335,4818],[338,4820],[339,4790],[340,4824],[341,4821],[342,4808],[343,4813],[948,4758],[1213,4775],[1214,4830],[1215,4775],[1216,4796],[1217,4759],[1218,4775],[1219,4775],[1220,4775],[1221,4761],[1222,4775],[1223,227],[1224,227],[1225,4772],[1226,4752],[1227,4772],[1228,4772],[1229,4753],[1230,4775],[1231,4796],[1232,4775],[1233,4832],[1234,4832],[1235,4775],[1239,4802],[1240,4796],[1241,4822],[1261,4775],[1262,4775],[1269,4775],[1274,4822],[1275,4830],[1284,4795],[1285,4795],[1286,4795],[1287,4795],[1288,4796],[1289,4796],[1292,4775],[1294,4797],[1295,4831],[1296,4797],[1297,4797],[1298,4753],[1299,4797],[1300,4797],[1301,4796],[1309,4775],[1326,4796],[1327,4775],[1328,4796],[1340,4822],[1344,4822],[1351,4796],[1352,4796],[1363,4796],[1365,4759],[1366,4759],[1367,4802],[1368,4802],[1369,4802],[1370,4802],[1371,4829],[1372,4829],[1375,4805],[1376,4805],[1378,4805],[1379,4805],[1380,4822],[1381,4822],[1383,4822],[1385,4770],[1391,4832],[1397,4802],[1400,4796],[1405,4795],[1409,4796],[1412,4789],[1420,4829],[1426,4789],[1427,4802],[1445,4796],[1446,4753],[1448,4753],[1461,4805],[1462,4777],[1463,4825],[1464,4777],[1465,4825],[1466,4777],[1467,4777],[1468,4791],[1469,4791],[1499,4775],[1500,4791],[1505,4775],[1519,4822],[1522,4775],[1523,4775],[1524,4775],[1525,4772],[1526,4772],[1527,4772],[1528,4772],[1529,4822],[1530,4802],[1534,4833],[1535,4833],[1537,4753],[1538,4822],[1540,4822],[1541,4807],[1545,4770],[1547,4770],[1548,4770],[1549,4770],[1551,4829],[1552,4796],[1553,4802],[1554,4802],[1555,4802],[1556,4802],[1557,4802],[1558,4802],[1559,4802],[1560,4802],[1561,4802],[1562,4802],[1565,4775],[1566,4796],[1567,4753],[1569,4775],[1572,4796],[1573,4796],[1579,4753],[1580,4796],[1585,4796],[1586,4802],[1589,4817],[1602,4772],[1612,4775],[1615,4822],[1616,4832],[1617,4832],[1618,4832],[1620,4795],[1621,4775],[1624,4775],[1633,4819],[1634,4802],[1637,4825],[1638,4825],[1639,4825],[1640,4825],[1641,4825],[1642,4825],[1643,4825],[1647,4791],[1648,4791],[1649,4791],[1650,4791],[1651,4770],[1653,4791],[4745,4845],[4747,4846],[4748,4845],[4750,4845],[4751,4845]],"b":[[105,"impl-Debug-for-RedisClient"],[106,"impl-Display-for-RedisClient"],[266,"impl-Debug-for-RedisError"],[267,"impl-Display-for-RedisError"],[1200,"impl-Debug-for-Command"],[1201,"impl-Display-for-Command"],[2932,"impl-Display-for-Version"],[2933,"impl-Debug-for-Version"],[2940,"impl-Debug-for-Server"],[2941,"impl-Display-for-Server"],[2950,"impl-Debug-for-RedisValueKind"],[2951,"impl-Display-for-RedisValueKind"],[2995,"impl-Debug-for-ClientState"],[2996,"impl-Display-for-ClientState"],[3027,"impl-Debug-for-Script"],[3028,"impl-Display-for-Script"],[3030,"impl-Display-for-Function"],[3031,"impl-Debug-for-Function"],[3032,"impl-Debug-for-Library"],[3033,"impl-Display-for-Library"],[3062,"impl-From%3Cbool%3E-for-BytesFrame"],[3064,"impl-From%3Cf64%3E-for-BytesFrame"],[3065,"impl-From%3Ci64%3E-for-BytesFrame"],[3068,"impl-From%3C%26%5Bu16%5D%3E-for-MultipleHashSlots"],[3069,"impl-From%3CVecDeque%3Cu16%3E%3E-for-MultipleHashSlots"],[3071,"impl-From%3CVec%3Cu16%3E%3E-for-MultipleHashSlots"],[3072,"impl-From%3Cu16%3E-for-MultipleHashSlots"],[3077,"impl-From%3Cf64%3E-for-MultipleWeights"],[3078,"impl-From%3COption%3Cf64%3E%3E-for-MultipleWeights"],[3080,"impl-From%3CVec%3Cf64%3E%3E-for-MultipleWeights"],[3081,"impl-From%3CVecDeque%3Cf64%3E%3E-for-MultipleWeights"],[3083,"impl-From%3COption%3Cu16%3E%3E-for-ClusterHash"],[3085,"impl-From%3Cu16%3E-for-ClusterHash"],[3086,"impl-From%3C%26str%3E-for-ClusterHash"],[3087,"impl-From%3C%26%5Bu8%5D%3E-for-ClusterHash"],[3091,"impl-From%3CTlsConnector%3E-for-TlsConnector"],[3093,"impl-From%3CTlsConnector%3E-for-TlsConnector"],[3094,"impl-From%3CTlsConnector%3E-for-TlsConnector"],[3095,"impl-From%3CClientConfig%3E-for-TlsConnector"],[3096,"impl-From%3C(String,+u16)%3E-for-Server"],[3097,"impl-From%3C(%26str,+u16)%3E-for-Server"],[3098,"impl-From%3C%26Server%3E-for-Server"],[3105,"impl-From%3Cu64%3E-for-StringOrNumber"],[3106,"impl-From%3CString%3E-for-StringOrNumber"],[3107,"impl-From%3Cisize%3E-for-StringOrNumber"],[3108,"impl-From%3Cu8%3E-for-StringOrNumber"],[3109,"impl-From%3Cu16%3E-for-StringOrNumber"],[3110,"impl-From%3Ci32%3E-for-StringOrNumber"],[3111,"impl-From%3Cu32%3E-for-StringOrNumber"],[3112,"impl-From%3Ci64%3E-for-StringOrNumber"],[3113,"impl-From%3C%26str%3E-for-StringOrNumber"],[3114,"impl-From%3CStrInner%3CBytes%3E%3E-for-StringOrNumber"],[3115,"impl-From%3Cusize%3E-for-StringOrNumber"],[3116,"impl-From%3Cf32%3E-for-StringOrNumber"],[3117,"impl-From%3Ci8%3E-for-StringOrNumber"],[3119,"impl-From%3Cf64%3E-for-StringOrNumber"],[3120,"impl-From%3Ci16%3E-for-StringOrNumber"],[3121,"impl-From%3Cf64%3E-for-RedisKey"],[3123,"impl-From%3CStrInner%3CBytes%3E%3E-for-RedisKey"],[3124,"impl-From%3Ci64%3E-for-RedisKey"],[3125,"impl-From%3Cu32%3E-for-RedisKey"],[3126,"impl-From%3C%26String%3E-for-RedisKey"],[3127,"impl-From%3C%26str%3E-for-RedisKey"],[3128,"impl-From%3CString%3E-for-RedisKey"],[3129,"impl-From%3C%26%5Bu8%5D%3E-for-RedisKey"],[3130,"impl-From%3CBox%3C%5Bu8%5D%3E%3E-for-RedisKey"],[3131,"impl-From%3CBytes%3E-for-RedisKey"],[3132,"impl-From%3Ci128%3E-for-RedisKey"],[3133,"impl-From%3Cisize%3E-for-RedisKey"],[3134,"impl-From%3C%26StrInner%3CBytes%3E%3E-for-RedisKey"],[3135,"impl-From%3Ci32%3E-for-RedisKey"],[3136,"impl-From%3Cu16%3E-for-RedisKey"],[3137,"impl-From%3C%26RedisKey%3E-for-RedisKey"],[3138,"impl-From%3Cbool%3E-for-RedisKey"],[3139,"impl-From%3Ci16%3E-for-RedisKey"],[3140,"impl-From%3Ci8%3E-for-RedisKey"],[3141,"impl-From%3Cusize%3E-for-RedisKey"],[3142,"impl-From%3Cu128%3E-for-RedisKey"],[3143,"impl-From%3Cu8%3E-for-RedisKey"],[3144,"impl-From%3Cu64%3E-for-RedisKey"],[3145,"impl-From%3Cf32%3E-for-RedisKey"],[3147,"impl-From%3C%26RedisMap%3E-for-RedisMap"],[3148,"impl-From%3C()%3E-for-RedisMap"],[3150,"impl-From%3CRedisKey%3E-for-RedisValue"],[3151,"impl-From%3C(A0,+A1,+A2)%3E-for-RedisValue"],[3152,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7)%3E-for-RedisValue"],[3153,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8)%3E-for-RedisValue"],[3155,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6)%3E-for-RedisValue"],[3156,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9)%3E-for-RedisValue"],[3157,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10)%3E-for-RedisValue"],[3158,"impl-From%3Cu8%3E-for-RedisValue"],[3159,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,+A11)%3E-for-RedisValue"],[3160,"impl-From%3Cu16%3E-for-RedisValue"],[3161,"impl-From%3Cu32%3E-for-RedisValue"],[3162,"impl-From%3Ci8%3E-for-RedisValue"],[3163,"impl-From%3Ci16%3E-for-RedisValue"],[3164,"impl-From%3Ci32%3E-for-RedisValue"],[3165,"impl-From%3Ci64%3E-for-RedisValue"],[3166,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,+A11,+A12)%3E-for-RedisValue"],[3167,"impl-From%3Cf32%3E-for-RedisValue"],[3168,"impl-From%3Cf64%3E-for-RedisValue"],[3169,"impl-From%3CStrInner%3CBytes%3E%3E-for-RedisValue"],[3170,"impl-From%3CBytes%3E-for-RedisValue"],[3171,"impl-From%3CBox%3C%5Bu8%5D%3E%3E-for-RedisValue"],[3172,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5)%3E-for-RedisValue"],[3173,"impl-From%3CString%3E-for-RedisValue"],[3174,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,+A11,+A12,+A13)%3E-for-RedisValue"],[3175,"impl-From%3C%26String%3E-for-RedisValue"],[3176,"impl-From%3C%26str%3E-for-RedisValue"],[3177,"impl-From%3C%26%5Bu8%5D%3E-for-RedisValue"],[3178,"impl-From%3Cbool%3E-for-RedisValue"],[3179,"impl-From%3C(A0,+A1,+A2,+A3,+A4)%3E-for-RedisValue"],[3180,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,+A11,+A12,+A13,+A14)%3E-for-RedisValue"],[3181,"impl-From%3C(A0,+A1)%3E-for-RedisValue"],[3182,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,+A11,+A12,+A13,+A14,+A15)%3E-for-RedisValue"],[3183,"impl-From%3CRedisMap%3E-for-RedisValue"],[3184,"impl-From%3C(A0,+A1,+A2,+A3)%3E-for-RedisValue"],[3185,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,+A11,+A12,+A13,+A14,+A15,+A16)%3E-for-RedisValue"],[3186,"impl-From%3C()%3E-for-RedisValue"],[3219,"impl-From%3CGeoValue%3E-for-MultipleGeoValues"],[3220,"impl-From%3CVecDeque%3CGeoValue%3E%3E-for-MultipleGeoValues"],[3222,"impl-From%3CVec%3CGeoValue%3E%3E-for-MultipleGeoValues"],[3243,"impl-From%3CVec%3CT%3E%3E-for-MultipleKeys"],[3244,"impl-From%3C(A0,+A1,+A2,+A3)%3E-for-MultipleKeys"],[3245,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,+A11,+A12,+A13)%3E-for-MultipleKeys"],[3246,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,+A11,+A12,+A13,+A14,+A15)%3E-for-MultipleKeys"],[3247,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,+A11,+A12)%3E-for-MultipleKeys"],[3248,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9)%3E-for-MultipleKeys"],[3249,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8)%3E-for-MultipleKeys"],[3250,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6)%3E-for-MultipleKeys"],[3251,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,+A11)%3E-for-MultipleKeys"],[3252,"impl-From%3C(A0,+A1,+A2,+A3,+A4)%3E-for-MultipleKeys"],[3254,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5)%3E-for-MultipleKeys"],[3255,"impl-From%3C(A0,+A1)%3E-for-MultipleKeys"],[3256,"impl-From%3C(A0,+A1,+A2)%3E-for-MultipleKeys"],[3257,"impl-From%3COption%3CRedisKey%3E%3E-for-MultipleKeys"],[3258,"impl-From%3CVecDeque%3CT%3E%3E-for-MultipleKeys"],[3259,"impl-From%3C()%3E-for-MultipleKeys"],[3260,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7)%3E-for-MultipleKeys"],[3261,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,+A11,+A12,+A13,+A14)%3E-for-MultipleKeys"],[3262,"impl-From%3CT%3E-for-MultipleKeys"],[3263,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10)%3E-for-MultipleKeys"],[3264,"impl-From%3C%26%5BK;+N%5D%3E-for-MultipleKeys"],[3265,"impl-From%3C(A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,+A11,+A12,+A13,+A14,+A15,+A16)%3E-for-MultipleKeys"],[3294,"impl-From%3Ci64%3E-for-ZRangeBound"],[3295,"impl-From%3C%26str%3E-for-ZRangeBound"],[3297,"impl-From%3CString%3E-for-ZRangeBound"],[3298,"impl-From%3C%26String%3E-for-ZRangeBound"],[3301,"impl-From%3Ci64%3E-for-ZRange"],[3302,"impl-From%3C%26str%3E-for-ZRange"],[3303,"impl-From%3CString%3E-for-ZRange"],[3304,"impl-From%3C%26String%3E-for-ZRange"],[3305,"impl-From%3C%26ZRange%3E-for-ZRange"],[3309,"impl-From%3CT%3E-for-MultipleIDs"],[3310,"impl-From%3CVec%3CT%3E%3E-for-MultipleIDs"],[3312,"impl-From%3CVecDeque%3CT%3E%3E-for-MultipleIDs"],[3316,"impl-From%3C%26String%3E-for-XID"],[3317,"impl-From%3CStrInner%3CBytes%3E%3E-for-XID"],[3319,"impl-From%3CString%3E-for-XID"],[3320,"impl-From%3C%26str%3E-for-XID"],[3321,"impl-From%3C()%3E-for-XPendingArgs"],[3322,"impl-From%3C(S,+E,+u64)%3E-for-XPendingArgs"],[3323,"impl-From%3C(u64,+S,+E,+u64)%3E-for-XPendingArgs"],[3324,"impl-From%3C(u64,+S,+E,+u64,+C)%3E-for-XPendingArgs"],[3326,"impl-From%3C(S,+E,+u64,+C)%3E-for-XPendingArgs"],[3332,"impl-From%3CVec%3CS%3E%3E-for-GetLabels"],[3334,"impl-From%3C%5BS;+N%5D%3E-for-GetLabels"],[4079,"impl-TryFrom%3CVec%3C(f64,+T)%3E%3E-for-MultipleZaddValues"],[4080,"impl-TryFrom%3C(f64,+T)%3E-for-MultipleZaddValues"],[4081,"impl-TryFrom%3CVecDeque%3C(f64,+T)%3E%3E-for-MultipleZaddValues"],[4087,"impl-TryFrom%3C%26str%3E-for-Server"],[4088,"impl-TryFrom%3CString%3E-for-Server"],[4099,"impl-TryFrom%3CVec%3C(K,+V)%3E%3E-for-RedisMap"],[4100,"impl-TryFrom%3CVecDeque%3C(K,+V)%3E%3E-for-RedisMap"],[4101,"impl-TryFrom%3C%26%5B(K,+V);+N%5D%3E-for-RedisMap"],[4102,"impl-TryFrom%3C%5B(K,+V);+N%5D%3E-for-RedisMap"],[4103,"impl-TryFrom%3C(K,+V)%3E-for-RedisMap"],[4104,"impl-TryFrom%3CBTreeMap%3CK,+V%3E%3E-for-RedisMap"],[4105,"impl-TryFrom%3CHashMap%3CK,+V%3E%3E-for-RedisMap"],[4108,"impl-TryFrom%3Cu128%3E-for-RedisValue"],[4109,"impl-TryFrom%3CBytesFrame%3E-for-RedisValue"],[4110,"impl-TryFrom%3COption%3CT%3E%3E-for-RedisValue"],[4112,"impl-TryFrom%3C%26%5BT;+N%5D%3E-for-RedisValue"],[4113,"impl-TryFrom%3Cusize%3E-for-RedisValue"],[4114,"impl-TryFrom%3C%5BT;+N%5D%3E-for-RedisValue"],[4115,"impl-TryFrom%3Ci128%3E-for-RedisValue"],[4116,"impl-TryFrom%3CVec%3CT%3E%3E-for-RedisValue"],[4117,"impl-TryFrom%3CVecDeque%3CT%3E%3E-for-RedisValue"],[4118,"impl-TryFrom%3CHashMap%3CK,+V%3E%3E-for-RedisValue"],[4119,"impl-TryFrom%3Cu64%3E-for-RedisValue"],[4120,"impl-TryFrom%3CBTreeMap%3CK,+V%3E%3E-for-RedisValue"],[4126,"impl-TryFrom%3CString%3E-for-Toggle"],[4128,"impl-TryFrom%3C%26str%3E-for-Toggle"],[4129,"impl-TryFrom%3C%26String%3E-for-Toggle"],[4179,"impl-TryFrom%3CStrInner%3CBytes%3E%3E-for-FnPolicy"],[4180,"impl-TryFrom%3C%26StrInner%3CBytes%3E%3E-for-FnPolicy"],[4182,"impl-TryFrom%3CString%3E-for-FnPolicy"],[4183,"impl-TryFrom%3C%26String%3E-for-FnPolicy"],[4184,"impl-TryFrom%3C%26str%3E-for-FnPolicy"],[4223,"impl-TryFrom%3CHashMap%3CK,+V%3E%3E-for-MultipleOrderedPairs"],[4224,"impl-TryFrom%3CVecDeque%3C(K,+V)%3E%3E-for-MultipleOrderedPairs"],[4225,"impl-TryFrom%3CVec%3C(K,+V)%3E%3E-for-MultipleOrderedPairs"],[4226,"impl-TryFrom%3C(K,+V)%3E-for-MultipleOrderedPairs"],[4230,"impl-TryFrom%3C(K,+T,+S,+Option%3Ci64%3E)%3E-for-XCap"],[4232,"impl-TryFrom%3C(K,+T,+S)%3E-for-XCap"],[4233,"impl-TryFrom%3C(K,+S)%3E-for-XCap"],[4239,"impl-TryFrom%3C%26str%3E-for-Timestamp"],[4240,"impl-TryFrom%3CString%3E-for-Timestamp"],[4241,"impl-TryFrom%3CStrInner%3CBytes%3E%3E-for-Timestamp"]],"c":"OjAAAAAAAAA=","e":"OzAAAAEAAMkL7gABAAEACQAAAAsABAAcAA8ALgAOAD8ABwBKABsAZwAIAH0AAgCBAAQAnQABAKoAAgCvAAgAvAAYANcABwD3AAMA/AAJAAcBBgAPAQAAEQEBABwBDAAyAQAAtQMAAIUEHQClBAEAqAQCAK0EBQC0BAAAuAQcANYEAQDbBAYA4wQBAOcEAADpBAAA6wQCAPAEAAD1BAAA+QQAAP4EAQABBQAAAwUAAAsFAQAXBQIAHAUBACAFBwArBQEALgUAADIFAAA0BQIAOQUBAEIFAABEBQMASwUEAFIFAQBVBQAAXgUBAGIFAABnBQAAaQUAAGsFAgBvBQAAcQUDAHsFAAB/BQAAgQUAAIMFAQCGBQIAigUBAI8FAACSBQAAmQUAAJsFAACdBQIAoQUEAKoFCgC/BQIAxQUBAMgFAgDNBQAA0QUAANQFAwDZBQIA4wUBAOYFAADoBQAA6gUBAO0FAgD9BQEAAQYAAAcGAQALBgAAHAYBACEGAAAkBgAAKQYBAC4GAAAwBgEANAYBADcGBgA/BgEAQgYAAEQGCABOBgEAVAYAAFcGAQBbBgUAZAYBAG0GAgB1BgAAeAYMAIYGAACIBgAAkQYAAJoGBQCiBgAApwb9AKYHAQCpBwAAsAdjARUJAAAZCQEAHQkHACYJBwAzCQAANgkCADoJAwA/CQQARQkhAHAJAQF0CoEA9woAAPkKZwBjCwEAagsCAHALAQB1C38A9wsAAPkLAQD9CwEAAAwBAAYMAQAJDAEADAwAAA4MAgASDAAAFAwAABYMBQAiDAwAMAwCADQMFgBMDAEATwwDAFQMHwB5DAAAkQwAAJQMAQCXDAAArAwJALcMCwDfDAEA4gwBAOYMBADtDAIA8QwAAPMMAAD1DAEA+AwFAP8MAAACDQAABQ0AAAcNAQALDQAADQ0AABANAAAVDQkAJw0AAC0NBwA2DQAAOA0AADsNCQBJDQAASw0FAFINCABcDQAAYg0CAGYNgwDsDQQAfg4AAIMOAACLDgAAjw4AAJIOAACVDgAAmQ4EAKAOBQCnDgMArQ4FALUOAQC4DgEAuw4BAMEOAADGDgUAzg4CANIOAQDXDgAA2Q4AAN4OAQDnDgMA7Q4AAO8ODgD/DgcACQ8FABIPAwAZDwEAHQ8AACAPAAAkDwAAJw8EAC0PBgA1DwAAPw8CAEMPAABFDwcAUA8AAFIPAwBXDwAAWQ8CAF8PdgDYDwgA4w83ARwRfgCdEQAApREDAKoRfgAtEgUANBIFADwSQwCFEgMA"}]]')); +if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; +else if (window.initSearch) window.initSearch(searchIndex); +//{"start":39,"fragment_lengths":[111582]} \ No newline at end of file diff --git a/doc/search.desc/fred/fred-desc-0-.js b/doc/search.desc/fred/fred-desc-0-.js new file mode 100644 index 00000000..17d63e55 --- /dev/null +++ b/doc/search.desc/fred/fred-desc-0-.js @@ -0,0 +1 @@ +searchState.loadedDescShard("fred", 0, "Fred\nRedis client implementations.\nShorthand to create a CustomCommand.\nError structs returned by Redis commands.\nTraits that implement portions of the Redis interface.\nA helper macro to wrap a string value in quotes via the …\nAn interface to run the MONITOR command.\nConvenience module to import a RedisClient, all possible …\nThe structs and enums used by the Redis client.\nVarious client utility functions.\nSend a series of commands in a pipeline.\nA cheaply cloneable Redis client struct.\nA cheaply cloneable round-robin client pool.\nA struct for interacting with cluster replica nodes.\nA struct for interacting directly with Sentinel nodes.\nA subscriber client that will manage subscription state to …\nA cheaply cloneable transaction block.\nA client interface used to customize command configuration …\nRead the set of active connections across all clients in …\nSend the pipeline and respond with an array of all …\nRead the underlying RedisClient that interacts with …\nRead the individual clients in the pool.\nCreate a new RedisClient from the config provided to this …\nCreate a new SubscriberClient from the config provided to …\nRead the server ID against which this transaction will …\nConnect each client to the server.\nConnect each client to the server, returning the task …\nExecutes all previously queued commands in a transaction.\nForce a reconnection to the server(s) for each client.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCreate a new pool from an existing set of clients.\nRead the hash slot against which this transaction will …\nIncrementally iterate over pages of the hash map stored at …\nAn ID identifying the underlying transaction state.\nInitialize a new routing and connection task for each …\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nSend the pipeline and respond with only the result of the …\nRead the client that ran the last command.\nRead the number of commands queued to run.\nSpawn a task that will automatically re-subscribe to any …\nCreate a new pool without connecting to the server.\nCreate a new client instance without connecting to the …\nCreate a new client instance without connecting to the …\nCreate a new client instance without connecting to the …\nRead the client that should run the next command.\nRead the next connected client that should run the next …\nRead a mapping of replica server IDs to primary server IDs.\nRead the options that will be applied to commands.\nSend a series of commands in a pipeline.\nSend a series of commands in a pipeline.\nSet whether the client will use next_connected or next …\nClose the connection to the Redis server for each client. …\nCreate a client that interacts with the replica nodes …\nCreate a client that interacts with replica nodes.\nClear the internal command buffer and watched keys.\nRe-subscribe to any tracked channels and patterns.\nIncrementally iterate over a set of keys matching the …\nRun the SCAN command on each primary/main node in a …\nOverride the DNS resolution logic for all clients in the …\nRead the size of the pool.\nSplit a clustered Redis client into a set of centralized …\nIncrementally iterate over pages of the set stored at key, …\nSync the cached replica routing table with the server(s).\nCreate a new RedisClient, reusing the existing …\nRead the set of channels that this client will manage.\nRead the set of channel patterns that this client will …\nRead the set of shard channels that this client will …\nSend the pipeline and respond with each individual result.\nUnsubscribe from all tracked channels and patterns, and …\nUpdate the internal PerformanceConfig on each client in …\nWait for all the clients to connect to the server.\nSend the WATCH command with the provided keys before …\nRead the number of keys to WATCH before the starting the …\nShorthand to route subsequent commands to the provided …\nIncrementally iterate over pages of the sorted set stored …\nAn authentication error.\nAn error indicating that the caller should apply …\nAn error indicating the request was canceled.\nAn error used to indicate that the cluster’s state has …\nA fatal client configuration error. These errors will …\nAn IO error with the underlying connection.\nAn invalid argument or set of arguments to a command.\nAn invalid command, such as trying to perform a set …\nAn error indicating a value was not found, often used when …\nA parser error.\nA protocol error such as an invalid or unexpected frame …\nAn error from Redis.\nAn enum representing the type of error from Redis.\nAn error associated with a replica node.\nAn error communicating with redis sentinel.\nA timeout error.\nA TLS error.\nAn unknown error.\nAn invalid URL error.\nChange the kind of the error.\nRead details about the error.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self).\nCalls U::from(self).\nWhether the error is a Canceled error.\nWhether the error is a Cluster error.\nWhether the error is a NotFound error.\nWhether the error is a Replica error.\nRead the type of error without any associated data.\nCreate a new Redis error with the provided details.\nCreate a new empty Canceled error.\nFunctions that implement the ACL interface.\nAn array of frames.\nFunctions for authenticating clients.\nA large number not representable as a Number or Double.\nA blob representing an error.\nA blob of bytes.\nA boolean type.\nOne chunk of a streaming blob.\nFunctions that implement the client interface.\nFunctions that implement the cluster interface.\nFunctions that implement the config interface.\nA signed 64-bit floating point number.\nContains the error value\nAn interface that exposes various client and connection …\nFunctions that implement the function interface.\nFunctions that implement the geo interface.\nFunctions that implement the hashes interface.\nFunctions that provide a connection heartbeat interface.\nA special frame type used when first connecting to the …\nFunctions that implement the HyperLogLog interface.\nFunctions that implement the generic keys interface.\nFunctions that implement the lists interface.\nFunctions that implement the lua interface.\nAn unordered map of key-value pairs.\nFunctions that implement the memory interface.\nFunctions that implement the internal metrics interface.\nA null type.\nA signed 64-bit integer.\nContains the success value\nFunctions that implement the pubsub interface.\nOut-of-band data.\nA RediSearch interface.\nThe client commands in the RedisJSON interface.\nType alias for Result<T, RedisError>.\nA RESP3 frame that uses Bytes and Str as the underlying …\nFunctions that implement the sentinel interface.\nFunctions that implement the server interface.\nAn unordered collection of other frames with a uniqueness …\nFunctions that implement the sets interface.\nA small string representing an error.\nA small string.\nFunctions that implement the slowlog interface.\nFunctions that implement the sorted sets interface.\nFunctions that implement the streams interface.\nA Redis Timeseries interface.\nA high level interface that supports client side caching …\nFunctions that implement the transactions interface.\nA string to be displayed without any escaping or filtering.\nThe command shows the available ACL categories if called …\nThe command shows the available ACL categories if called …\nDelete all the specified ACL users and terminate all the …\nDelete all the specified ACL users and terminate all the …\nGenerate a password with length bits, returning the …\nGenerate a password with length bits, returning the …\nThe command returns all the rules defined for an existing …\nThe command returns all the rules defined for an existing …\nThe command shows the currently active ACL rules in the …\nThe command shows the currently active ACL rules in the …\nWhen Redis is configured to use an ACL file (with the …\nWhen Redis is configured to use an ACL file (with the …\nRead count recent ACL security events.\nRead count recent ACL security events.\nClear the ACL security events logs.\nClear the ACL security events logs.\nWhen Redis is configured to use an ACL file (with the …\nWhen Redis is configured to use an ACL file (with the …\nCreate an ACL user with the specified rules or modify the …\nCreate an ACL user with the specified rules or modify the …\nThe command shows a list of all the usernames of the …\nThe command shows a list of all the usernames of the …\nReturn the username the current connection is …\nReturn the username the current connection is …\nRead the set of active connections managed by the client.\nRead the set of active connections managed by the client.\nAppend value to key if it’s a string.\nAppend value to key if it’s a string.\nRequest for authentication in a password-protected Redis …\nInstruct Redis to start an Append Only File rewrite …\nInstruct Redis to start an Append Only File rewrite …\nSave the DB in background.\nSave the DB in background.\nThe blocking equivalent of Self::lmove.\nThe blocking equivalent of Self::lmove.\nThe blocking variant of Self::lmpop.\nThe blocking variant of Self::lmpop.\nBLPOP is a blocking list pop primitive. It is the blocking …\nBLPOP is a blocking list pop primitive. It is the blocking …\nBRPOP is a blocking list pop primitive. It is the blocking …\nBRPOP is a blocking list pop primitive. It is the blocking …\nThe blocking equivalent of Self::rpoplpush.\nThe blocking equivalent of Self::rpoplpush.\nThe blocking variant of Self::zmpop.\nThe blocking variant of Self::zmpop.\nThe blocking variant of Self::zpopmax.\nThe blocking variant of Self::zpopmax.\nThe blocking variant of Self::zpopmin.\nThe blocking variant of Self::zpopmin.\nRead the cached cluster state used for routing commands to …\nRead the cached cluster state used for routing commands to …\nCheck if the current Sentinel configuration is able to …\nCheck if the current Sentinel configuration is able to …\nThis command controls the tracking of the keys in the next …\nThis command controls the tracking of the keys in the next …\nRead the config used to initialize the client.\nRead the config used to initialize the client.\nThe CLIENT GETNAME returns the name of the current …\nThe CLIENT GETNAME returns the name of the current …\nThis command returns the client ID we are redirecting our …\nThis command returns the client ID we are redirecting our …\nReturn the ID of the current connection.\nReturn the ID of the current connection.\nThe command returns information and statistics about the …\nThe command returns information and statistics about the …\nClose a given connection or set of connections.\nClose a given connection or set of connections.\nThe CLIENT LIST command returns information and statistics …\nThe CLIENT LIST command returns information and statistics …\nCLIENT PAUSE is a connections control command able to …\nCLIENT PAUSE is a connections control command able to …\nRead the reconnect policy used to initialize the client.\nRead the reconnect policy used to initialize the client.\nThe CLIENT REPLY command controls whether the server will …\nThe CLIENT REPLY command controls whether the server will …\nAssign a name to the current connection.\nAssign a name to the current connection.\nThis command enables the tracking feature of the Redis …\nThis command enables the tracking feature of the Redis …\nThe command returns information about the current client …\nThe command returns information about the current client …\nThis command can unblock, from a different connection, a …\nThis command can unblock, from a different connection, a …\nCLIENT UNPAUSE is used to resume command processing for …\nCLIENT UNPAUSE is used to resume command processing for …\nThis command is useful in order to modify a node’s view …\nThis command is useful in order to modify a node’s view …\nAdvances the cluster config epoch.\nAdvances the cluster config epoch.\nListen for notifications whenever the cluster state …\nThe command returns the number of failure reports for the …\nThe command returns the number of failure reports for the …\nReturns the number of keys in the specified Redis Cluster …\nReturns the number of keys in the specified Redis Cluster …\nThe CLUSTER DELSLOTS command asks a particular Redis …\nThe CLUSTER DELSLOTS command asks a particular Redis …\nThis command, that can only be sent to a Redis Cluster …\nThis command, that can only be sent to a Redis Cluster …\nDeletes all slots from a node.\nDeletes all slots from a node.\nThe command is used in order to remove a node, specified …\nThe command is used in order to remove a node, specified …\nThe command returns an array of keys names stored in the …\nThe command returns an array of keys names stored in the …\nCLUSTER INFO provides INFO style information about Redis …\nCLUSTER INFO provides INFO style information about Redis …\nReturns an integer identifying the hash slot the specified …\nReturns an integer identifying the hash slot the specified …\nCLUSTER MEET is used in order to connect different Redis …\nCLUSTER MEET is used in order to connect different Redis …\nReturns the node’s id.\nReturns the node’s id.\nRead the current cluster node configuration.\nRead the current cluster node configuration.\nThe command provides a list of replica nodes replicating …\nThe command provides a list of replica nodes replicating …\nThe command reconfigures a node as a replica of the …\nThe command reconfigures a node as a replica of the …\nReset a Redis Cluster node, in a more or less drastic way …\nReset a Redis Cluster node, in a more or less drastic way …\nForces a node to save the nodes.conf configuration on disk.\nForces a node to save the nodes.conf configuration on disk.\nThis command sets a specific config epoch in a fresh node.\nThis command sets a specific config epoch in a fresh node.\nCLUSTER SETSLOT is responsible for changing the state of a …\nCLUSTER SETSLOT is responsible for changing the state of a …\nCLUSTER SLOTS returns details about which cluster slots …\nCLUSTER SLOTS returns details about which cluster slots …\nRead the number of buffered commands that have not yet …\nRead the number of buffered commands that have not yet …\nThe CONFIG GET command is used to read the configuration …\nThe CONFIG GET command is used to read the configuration …\nGet the current value of a global Sentinel configuration …\nGet the current value of a global Sentinel configuration …\nResets the statistics reported by Redis using the INFO …\nResets the statistics reported by Redis using the INFO …\nThe CONFIG REWRITE command rewrites the redis.conf file …\nThe CONFIG REWRITE command rewrites the redis.conf file …\nThe CONFIG SET command is used in order to reconfigure the …\nThe CONFIG SET command is used in order to reconfigure the …\nSet the value of a global Sentinel configuration parameter.\nSet the value of a global Sentinel configuration parameter.\nConnect to the server.\nConnect to the server.\nRead the connection config used to initialize the client.\nRead the connection config used to initialize the client.\nRead the connection IDs for the active connections to each …\nRead the connection IDs for the active connections to each …\nThis command copies the value stored at the source key to …\nThis command copies the value stored at the source key to …\nRun a custom command that is not yet supported via another …\nRun a custom command that is not yet supported via another …\nRun a custom command similar to custom, but return the …\nRun a custom command similar to custom, but return the …\nReturn the number of keys in the selected database.\nReturn the number of keys in the selected database.\nDecrements the number stored at key by one. If the key …\nDecrements the number stored at key by one. If the key …\nDecrements the number stored at key by val. If the key …\nDecrements the number stored at key by val. If the key …\nRemoves the specified keys. A key is ignored if it does …\nRemoves the specified keys. A key is ignored if it does …\nSerialize the value stored at key in a Redis-specific …\nSerialize the value stored at key in a Redis-specific …\nReturn a future that will ping the server on an interval.\nListen for protocol and connection errors. This stream can …\nEvaluate a Lua script on the server.\nEvaluate a Lua script on the server.\nEvaluates a script cached on the server side by its SHA1 …\nEvaluates a script cached on the server side by its SHA1 …\nReturns number of keys that exist from the keys arguments.\nReturns number of keys that exist from the keys arguments.\nSet a timeout on key. After the timeout has expired, the …\nSet a timeout on key. After the timeout has expired, the …\nSet a timeout on a key based on a UNIX timestamp.\nSet a timeout on a key based on a UNIX timestamp.\nForce a failover as if the master was not reachable, and …\nForce a failover as if the master was not reachable, and …\nThis command will start a coordinated failover between the …\nThis command will start a coordinated failover between the …\nInvoke a function.\nInvoke a function.\nThis is a read-only variant of the FCALL command that …\nThis is a read-only variant of the FCALL command that …\nDelete the keys in all databases.\nDelete the keys in all databases.\nDelete the keys on all nodes in the cluster. This is a …\nDelete the keys on all nodes in the cluster. This is a …\nForce Sentinel to rewrite its configuration on disk, …\nForce Sentinel to rewrite its configuration on disk, …\nForce a reconnection to the server(s).\nForce a reconnection to the server(s).\nRun a search query on an index, and perform aggregate …\nRun a search query on an index, and perform aggregate …\nAdd an alias to an index.\nAdd an alias to an index.\nRemove an alias from an index.\nRemove an alias from an index.\nAdd an alias to an index. If the alias is already …\nAdd an alias to an index. If the alias is already …\nAdd a new attribute to the index.\nAdd a new attribute to the index.\nRetrieve configuration options.\nRetrieve configuration options.\nSet the value of a RediSearch configuration parameter.\nSet the value of a RediSearch configuration parameter.\nCreate an index with the given specification.\nCreate an index with the given specification.\nDelete a cursor.\nDelete a cursor.\nRead next results from an existing cursor.\nRead next results from an existing cursor.\nAdd terms to a dictionary.\nAdd terms to a dictionary.\nRemove terms from a dictionary.\nRemove terms from a dictionary.\nDump all terms in the given dictionary.\nDump all terms in the given dictionary.\nDelete an index.\nDelete an index.\nReturn the execution plan for a complex query.\nReturn the execution plan for a complex query.\nReturn information and statistics on the index.\nReturn information and statistics on the index.\nReturns a list of all existing indexes.\nReturns a list of all existing indexes.\nSearch the index with a textual query, returning either …\nSearch the index with a textual query, returning either …\nPerform spelling correction on a query, returning …\nPerform spelling correction on a query, returning …\nAdd a suggestion string to an auto-complete suggestion …\nAdd a suggestion string to an auto-complete suggestion …\nDelete a string from a suggestion index.\nDelete a string from a suggestion index.\nGet completion suggestions for a prefix.\nGet completion suggestions for a prefix.\nGet the size of an auto-complete suggestion dictionary.\nGet the size of an auto-complete suggestion dictionary.\nDump the contents of a synonym group.\nDump the contents of a synonym group.\nUpdate a synonym group.\nUpdate a synonym group.\nReturn a distinct set of values indexed in a Tag field.\nReturn a distinct set of values indexed in a Tag field.\nDelete a library and all its functions.\nDelete a library and all its functions.\nDelete a library and all its functions from each cluster …\nDelete a library and all its functions from each cluster …\nReturn the serialized payload of loaded libraries.\nReturn the serialized payload of loaded libraries.\nDeletes all the libraries.\nDeletes all the libraries.\nDeletes all the libraries on all cluster nodes …\nDeletes all the libraries on all cluster nodes …\nKill a function that is currently executing.\nKill a function that is currently executing.\nReturn information about the functions and libraries.\nReturn information about the functions and libraries.\nLoad a library to Redis.\nLoad a library to Redis.\nLoad a library to Redis on all cluster nodes concurrently.\nLoad a library to Redis on all cluster nodes concurrently.\nRestore libraries from the serialized payload.\nRestore libraries from the serialized payload.\nRestore libraries from the serialized payload on all …\nRestore libraries from the serialized payload on all …\nReturn information about the function that’s currently …\nReturn information about the function that’s currently …\nAdds the specified geospatial items (longitude, latitude, …\nAdds the specified geospatial items (longitude, latitude, …\nReturn the distance between two members in the geospatial …\nReturn the distance between two members in the geospatial …\nReturn valid Geohash strings representing the position of …\nReturn valid Geohash strings representing the position of …\nReturn the positions (longitude,latitude) of all the …\nReturn the positions (longitude,latitude) of all the …\nReturn the members of a sorted set populated with …\nReturn the members of a sorted set populated with …\nThis command is exactly like GEORADIUS with the sole …\nThis command is exactly like GEORADIUS with the sole …\nReturn the members of a sorted set populated with …\nReturn the members of a sorted set populated with …\nThis command is like GEOSEARCH, but stores the result in …\nThis command is like GEOSEARCH, but stores the result in …\nRead a value from the server.\nRead a value from the server.\nReturn the ip and port number of the master with that name.\nReturn the ip and port number of the master with that name.\nGet the value of key and delete the key. This command is …\nGet the value of key and delete the key. This command is …\nReturns the substring of the string value stored at key …\nReturns the substring of the string value stored at key …\nAtomically sets key to value and returns the old value …\nAtomically sets key to value and returns the old value …\nWhether the client has a reconnection policy.\nWhether the client has a reconnection policy.\nRemoves the specified fields from the hash stored at key.\nRemoves the specified fields from the hash stored at key.\nSwitch to a different protocol, optionally authenticating …\nReturns if field is an existing field in the hash stored …\nReturns if field is an existing field in the hash stored …\nReturns the value associated with field in the hash stored …\nReturns the value associated with field in the hash stored …\nReturns all fields and values of the hash stored at key.\nReturns all fields and values of the hash stored at key.\nIncrements the number stored at field in the hash stored …\nIncrements the number stored at field in the hash stored …\nIncrement the specified field of a hash stored at key, and …\nIncrement the specified field of a hash stored at key, and …\nReturns all field names in the hash stored at key.\nReturns all field names in the hash stored at key.\nReturns the number of fields contained in the hash stored …\nReturns the number of fields contained in the hash stored …\nReturns the values associated with the specified fields in …\nReturns the values associated with the specified fields in …\nSets the specified fields to their respective values in …\nSets the specified fields to their respective values in …\nWhen called with just the key argument, return a random …\nWhen called with just the key argument, return a random …\nSets fields in the hash stored at key to their provided …\nSets fields in the hash stored at key to their provided …\nSets field in the hash stored at key to value, only if …\nSets field in the hash stored at key to value, only if …\nReturns the string length of the value associated with …\nReturns the string length of the value associated with …\nReturns all values in the hash stored at key.\nReturns all values in the hash stored at key.\nThe unique ID identifying this client and underlying …\nThe unique ID identifying this client and underlying …\nIncrements the number stored at key by one. If the key …\nIncrements the number stored at key by one. If the key …\nIncrements the number stored at key by val. If the key …\nIncrements the number stored at key by val. If the key …\nIncrement the string representing a floating point number …\nIncrement the string representing a floating point number …\nRead info about the server.\nRead info about the server.\nReturn cached INFO output from masters and replicas.\nReturn cached INFO output from masters and replicas.\nInitialize a new routing and connection task and wait for …\nInitialize a new routing and connection task and wait for …\nSubscribe to invalidation messages from the server(s).\nSubscribe to invalidation messages from the server(s).\nWhether the client is connected to a cluster.\nWhether the client is connected to a cluster.\nWhether all underlying connections are healthy.\nWhether all underlying connections are healthy.\nWhether the client will automatically pipeline commands.\nWhether the client will automatically pipeline commands.\nAppend the json values into the array at path after the …\nAppend the json values into the array at path after the …\nSearch for the first occurrence of a JSON value in an …\nSearch for the first occurrence of a JSON value in an …\nInsert the json values into the array at path before the …\nInsert the json values into the array at path before the …\nReport the length of the JSON array at path in key.\nReport the length of the JSON array at path in key.\nRemove and return an element from the index in the array\nRemove and return an element from the index in the array\nTrim an array so that it contains only the specified …\nTrim an array so that it contains only the specified …\nClear container values (arrays/objects) and set numeric …\nClear container values (arrays/objects) and set numeric …\nReport a value’s memory usage in bytes\nReport a value’s memory usage in bytes\nDelete a value.\nDelete a value.\nReturn the value at path in JSON serialized form.\nReturn the value at path in JSON serialized form.\nMerge a given JSON value into matching paths.\nMerge a given JSON value into matching paths.\nReturn the values at path from multiple key arguments.\nReturn the values at path from multiple key arguments.\nSet or update one or more JSON values according to the …\nSet or update one or more JSON values according to the …\nIncrement the number value stored at path by number\nIncrement the number value stored at path by number\nReturn the keys in the object that’s referenced by path.\nReturn the keys in the object that’s referenced by path.\nReport the number of keys in the JSON object at path in …\nReport the number of keys in the JSON object at path in …\nReturn the JSON in key in Redis serialization protocol …\nReturn the JSON in key in Redis serialization protocol …\nSet the JSON value at path in key.\nSet the JSON value at path in key.\nAppend the json-string values to the string at path.\nAppend the json-string values to the string at path.\nReport the length of the JSON String at path in key.\nReport the length of the JSON String at path in key.\nToggle a Boolean value stored at path.\nToggle a Boolean value stored at path.\nReport the type of JSON value at path.\nReport the type of JSON value at path.\nListen for keyspace and keyevent notifications on the …\nReturn the UNIX TIME of the last DB save executed with …\nReturn the UNIX TIME of the last DB save executed with …\nRuns the longest common subsequence algorithm on two keys.\nRuns the longest common subsequence algorithm on two keys.\nReturns the element at index in the list stored at key.\nReturns the element at index in the list stored at key.\nInserts element in the list stored at key either before or …\nInserts element in the list stored at key either before or …\nReturns the length of the list stored at key.\nReturns the length of the list stored at key.\nAtomically returns and removes the first/last element …\nAtomically returns and removes the first/last element …\nPops one or more elements from the first non-empty list …\nPops one or more elements from the first non-empty list …\nRemoves and returns the first elements of the list stored …\nRemoves and returns the first elements of the list stored …\nThe command returns the index of matching elements inside …\nThe command returns the index of matching elements inside …\nInsert all the specified values at the head of the list …\nInsert all the specified values at the head of the list …\nInserts specified values at the head of the list stored at …\nInserts specified values at the head of the list stored at …\nReturns the specified elements of the list stored at key.\nReturns the specified elements of the list stored at key.\nRemoves the first count occurrences of elements equal to …\nRemoves the first count occurrences of elements equal to …\nSets the list element at index to element.\nSets the list element at index to element.\nTrim an existing list so that it will contain only the …\nTrim an existing list so that it will contain only the …\nShow the state and info of the specified master.\nShow the state and info of the specified master.\nShow a list of monitored masters and their state.\nShow a list of monitored masters and their state.\nThe MEMORY DOCTOR command reports about different …\nThe MEMORY DOCTOR command reports about different …\nThe MEMORY MALLOC-STATS command provides an internal …\nThe MEMORY MALLOC-STATS command provides an internal …\nThe MEMORY PURGE command attempts to purge dirty pages so …\nThe MEMORY PURGE command attempts to purge dirty pages so …\nThe MEMORY STATS command returns an Array reply about the …\nThe MEMORY STATS command returns an Array reply about the …\nThe MEMORY USAGE command reports the number of bytes that …\nThe MEMORY USAGE command reports the number of bytes that …\nListen for messages on the publish-subscribe interface.\nReturns the values of all specified keys. For every key …\nReturns the values of all specified keys. For every key …\nStart Sentinel’s monitoring.\nStart Sentinel’s monitoring.\nSets the given keys to their respective values.\nSets the given keys to their respective values.\nSets the given keys to their respective values. MSETNX …\nSets the given keys to their respective values. MSETNX …\nEnter a MULTI block, executing subsequent commands as a …\nEnter a MULTI block, executing subsequent commands as a …\nReturn the ID of the Sentinel instance.\nReturn the ID of the Sentinel instance.\nRead the number of known primary cluster nodes, or 0 if …\nRead the number of known primary cluster nodes, or 0 if …\nSpawn one task that listens for all connection management …\nSpawn a task that runs the provided function on each …\nSpawn a task that runs the provided function on each …\nSpawn a task that processes invalidation messages from the …\nSpawn a task that processes invalidation messages from the …\nSpawn a task that runs the provided function on each …\nSpawn a task that runs the provided function on each …\nSpawn a task that runs the provided function on each …\nSpawn a task that runs the provided function whenever the …\nThis command returns information about pending scripts.\nThis command returns information about pending scripts.\nRead the PerformanceConfig associated with this client.\nRead the PerformanceConfig associated with this client.\nRemove the existing timeout on a key, turning the key from …\nRemove the existing timeout on a key, turning the key from …\nThis command works exactly like EXPIRE but the time to …\nThis command works exactly like EXPIRE but the time to …\nPEXPIREAT has the same effect and semantic as EXPIREAT, …\nPEXPIREAT has the same effect and semantic as EXPIREAT, …\nAdds all the element arguments to the HyperLogLog data …\nAdds all the element arguments to the HyperLogLog data …\nWhen called with a single key, returns the approximated …\nWhen called with a single key, returns the approximated …\nMerge multiple HyperLogLog values into an unique value …\nMerge multiple HyperLogLog values into an unique value …\nPing the Redis server.\nPing the Redis server.\nRead the RESP version used by the client when …\nRead the RESP version used by the client when …\nSubscribes the client to the given patterns.\nSubscribes the client to the given patterns.\nReturns the remaining time to live of a key that has a …\nReturns the remaining time to live of a key that has a …\nPublish a message on the PubSub interface, returning the …\nPublish a message on the PubSub interface, returning the …\nLists the currently active channels.\nLists the currently active channels.\nReturns the number of unique patterns that are subscribed …\nReturns the number of unique patterns that are subscribed …\nReturns the number of subscribers (exclusive of clients …\nReturns the number of subscribers (exclusive of clients …\nLists the currently active shard channels.\nLists the currently active shard channels.\nReturns the number of subscribers for the specified shard …\nReturns the number of subscribers for the specified shard …\nUnsubscribes the client from the given patterns, or from …\nUnsubscribes the client from the given patterns, or from …\nClose the connection to the Redis server. The returned …\nClose the connection to the Redis server. The returned …\nReturn a random key from the currently selected database.\nReturn a random key from the currently selected database.\nRead latency metrics across all commands.\nRead latency metrics across all commands.\nRead network latency metrics across all commands.\nRead network latency metrics across all commands.\nRead the number of request redeliveries.\nRead the number of request redeliveries.\nRead request payload size metrics across all commands.\nRead request payload size metrics across all commands.\nRead response payload size metrics across all commands.\nRead response payload size metrics across all commands.\nListen for reconnection notifications.\nStop Sentinel’s monitoring.\nStop Sentinel’s monitoring.\nRenames source key to destination.\nRenames source key to destination.\nRenames source key to destination if destination does not …\nRenames source key to destination if destination does not …\nShow a list of replicas for this master, and their state.\nShow a list of replicas for this master, and their state.\nThis command will reset all the masters with matching name.\nThis command will reset all the masters with matching name.\nCreate a key associated with a value that is obtained by …\nCreate a key associated with a value that is obtained by …\nRemoves and returns the last elements of the list stored …\nRemoves and returns the last elements of the list stored …\nAtomically returns and removes the last element (tail) of …\nAtomically returns and removes the last element (tail) of …\nInsert all the specified values at the tail of the list …\nInsert all the specified values at the tail of the list …\nInserts specified values at the tail of the list stored at …\nInserts specified values at the tail of the list stored at …\nAdd the specified members to the set stored at key.\nAdd the specified members to the set stored at key.\nReturns the set cardinality (number of elements) of the …\nReturns the set cardinality (number of elements) of the …\nSet the debug mode for subsequent scripts executed with …\nSet the debug mode for subsequent scripts executed with …\nReturns information about the existence of the scripts in …\nReturns information about the existence of the scripts in …\nFlush the Lua scripts cache.\nFlush the Lua scripts cache.\nA clustered variant of script_flush that flushes the …\nA clustered variant of script_flush that flushes the …\nKills the currently executing Lua script, assuming no …\nKills the currently executing Lua script, assuming no …\nA clustered variant of the script_kill command that issues …\nA clustered variant of the script_kill command that issues …\nLoad a script into the scripts cache, without executing …\nLoad a script into the scripts cache, without executing …\nA clustered variant of script_load that loads the script …\nA clustered variant of script_load that loads the script …\nReturns the members of the set resulting from the …\nReturns the members of the set resulting from the …\nThis command is equal to SDIFF, but instead of returning …\nThis command is equal to SDIFF, but instead of returning …\nSelect the database this client should use.\nSelect the database this client should use.\nRead the set of known sentinel nodes.\nRead the set of known sentinel nodes.\nRead the primary Redis server identifier returned from the …\nRead the primary Redis server identifier returned from the …\nShow a list of sentinel instances for this master, and …\nShow a list of sentinel instances for this master, and …\nRead the server version, if known.\nRead the server version, if known.\nSet a value with optional NX|XX, EX|PX|EXAT|PXAT|KEEPTTL, …\nSet a value with optional NX|XX, EX|PX|EXAT|PXAT|KEEPTTL, …\nSet Sentinel’s monitoring configuration.\nSet Sentinel’s monitoring configuration.\nOverride the DNS resolution logic for the client.\nOverride the DNS resolution logic for the client.\nOverwrites part of the string stored at key, starting at …\nOverwrites part of the string stored at key, starting at …\nShut down the server and quit the client.\nShut down the server and quit the client.\nThis command simulates different Sentinel crash scenarios.\nThis command simulates different Sentinel crash scenarios.\nReturns the members of the set resulting from the …\nReturns the members of the set resulting from the …\nThis command is equal to SINTER, but instead of returning …\nThis command is equal to SINTER, but instead of returning …\nReturns if member is a member of the set stored at key.\nReturns if member is a member of the set stored at key.\nThis command is used to read the slow queries log.\nThis command is used to read the slow queries log.\nThis command is used to read length of the slow queries …\nThis command is used to read length of the slow queries …\nThis command is used to reset the slow queries log.\nThis command is used to reset the slow queries log.\nReturns all the members of the set value stored at key.\nReturns all the members of the set value stored at key.\nReturns whether each member is a member of the set stored …\nReturns whether each member is a member of the set stored …\nMove member from the set at source to the set at …\nMove member from the set at source to the set at …\nReturns or stores the elements contained in the list, set …\nReturns or stores the elements contained in the list, set …\nRead-only variant of the SORT command. It is exactly like …\nRead-only variant of the SORT command. It is exactly like …\nRemoves and returns one or more random members from the …\nRemoves and returns one or more random members from the …\nPosts a message to the given shard channel.\nPosts a message to the given shard channel.\nWhen called with just the key argument, return a random …\nWhen called with just the key argument, return a random …\nRemove the specified members from the set stored at key.\nRemove the specified members from the set stored at key.\nSubscribes the client to the specified shard channels.\nSubscribes the client to the specified shard channels.\nSend the CLIENT TRACKING command to all connected servers, …\nSend the CLIENT TRACKING command to all connected servers, …\nRead the state of the underlying connection(s).\nRead the state of the underlying connection(s).\nDisable client tracking on all connections.\nDisable client tracking on all connections.\nReturns the length of the string value stored at key. An …\nReturns the length of the string value stored at key. An …\nSubscribe to a channel on the publish-subscribe interface.\nSubscribe to a channel on the publish-subscribe interface.\nReturns the members of the set resulting from the union of …\nReturns the members of the set resulting from the union of …\nThis command is equal to SUNION, but instead of returning …\nThis command is equal to SUNION, but instead of returning …\nUnsubscribes the client from the given shard channels, or …\nUnsubscribes the client from the given shard channels, or …\nUpdate the cached cluster state and add or remove any …\nUpdate the cached cluster state and add or remove any …\nRead and consume latency metrics, resetting their values …\nRead and consume latency metrics, resetting their values …\nRead and consume network latency metrics, resetting their …\nRead and consume network latency metrics, resetting their …\nRead and reset the number of request redeliveries.\nRead and reset the number of request redeliveries.\nRead and consume request payload size metrics, resetting …\nRead and consume request payload size metrics, resetting …\nRead and consume response payload size metrics, resetting …\nRead and consume response payload size metrics, resetting …\nAppend a sample to a time series.\nAppend a sample to a time series.\nUpdate the retention, chunk size, duplicate policy, and …\nUpdate the retention, chunk size, duplicate policy, and …\nCreate a new time series.\nCreate a new time series.\nCreate a compaction rule.\nCreate a compaction rule.\nDecrease the value of the sample with the maximum existing …\nDecrease the value of the sample with the maximum existing …\nDelete all samples between two timestamps for a given time …\nDelete all samples between two timestamps for a given time …\nDelete a compaction rule.\nDelete a compaction rule.\nGet the sample with the highest timestamp from a given …\nGet the sample with the highest timestamp from a given …\nIncrease the value of the sample with the maximum existing …\nIncrease the value of the sample with the maximum existing …\nReturn information and statistics for a time series.\nReturn information and statistics for a time series.\nAppend new samples to one or more time series.\nAppend new samples to one or more time series.\nGet the sample with the highest timestamp from each time …\nGet the sample with the highest timestamp from each time …\nQuery a range across multiple time series by filters in …\nQuery a range across multiple time series by filters in …\nQuery a range across multiple time series by filters in …\nQuery a range across multiple time series by filters in …\nGet all time series keys matching a filter list.\nGet all time series keys matching a filter list.\nQuery a range in forward direction.\nQuery a range in forward direction.\nQuery a range in reverse direction.\nQuery a range in reverse direction.\nReturns the remaining time to live of a key that has a …\nReturns the remaining time to live of a key that has a …\nA convenience function to unblock any blocked connection …\nA convenience function to unblock any blocked connection …\nUnlinks the specified keys. A key is ignored if it does …\nUnlinks the specified keys. A key is ignored if it does …\nReceive a message when the client initiates a reconnection …\nUnsubscribe from a channel on the PubSub interface.\nUnsubscribe from a channel on the PubSub interface.\nFlushes all the previously watched keys for a transaction.\nFlushes all the previously watched keys for a transaction.\nUpdate the internal PerformanceConfig in place with new …\nUpdate the internal PerformanceConfig in place with new …\nWhether the client uses the sentinel interface.\nWhether the client uses the sentinel interface.\nThis command blocks the current client until all the …\nThis command blocks the current client until all the …\nWait for the result of the next connection attempt.\nWait for the result of the next connection attempt.\nMarks the given keys to be watched for conditional …\nMarks the given keys to be watched for conditional …\nCustomize various configuration options on commands.\nCustomize various configuration options on commands.\nRemove one or more messages from the Pending Entries List …\nRemove one or more messages from the Pending Entries List …\nAppends the specified stream entry to the stream at the …\nAppends the specified stream entry to the stream at the …\nThis command transfers ownership of pending stream entries …\nThis command transfers ownership of pending stream entries …\nThis command transfers ownership of pending stream entries …\nThis command transfers ownership of pending stream entries …\nIn the context of a stream consumer group, this command …\nIn the context of a stream consumer group, this command …\nA variation of xclaim with a less verbose return type.\nA variation of xclaim with a less verbose return type.\nRemoves the specified entries from a stream, and returns …\nRemoves the specified entries from a stream, and returns …\nThis command creates a new consumer group uniquely …\nThis command creates a new consumer group uniquely …\nCreate a consumer named consumername in the consumer group …\nCreate a consumer named consumername in the consumer group …\nDelete a consumer named consumername in the consumer group …\nDelete a consumer named consumername in the consumer group …\nCompletely destroy a consumer group.\nCompletely destroy a consumer group.\nSet the last delivered ID for a consumer group.\nSet the last delivered ID for a consumer group.\nThis command returns the list of consumers that belong to …\nThis command returns the list of consumers that belong to …\nThis command returns the list of all consumers groups of …\nThis command returns the list of all consumers groups of …\nThis command returns information about the stream stored …\nThis command returns information about the stream stored …\nReturns the number of entries inside a stream.\nReturns the number of entries inside a stream.\nInspect the list of pending messages in a consumer group.\nInspect the list of pending messages in a consumer group.\nThe command returns the stream entries matching a given …\nThe command returns the stream entries matching a given …\nReturn the stream entries matching the provided range of …\nReturn the stream entries matching the provided range of …\nRead data from one or multiple streams, only returning …\nRead data from one or multiple streams, only returning …\nRead data from one or multiple streams, only returning …\nRead data from one or multiple streams, only returning …\nA special version of the XREAD command with support for …\nA special version of the XREAD command with support for …\nA special version of the XREAD command with support for …\nA special version of the XREAD command with support for …\nSimilar to XRANGE, but with the results returned in …\nSimilar to XRANGE, but with the results returned in …\nSimilar to XRANGE, but with the results returned in …\nSimilar to XRANGE, but with the results returned in …\nTrims the stream by evicting older entries (entries with …\nTrims the stream by evicting older entries (entries with …\nAdds all the specified members with the specified scores …\nAdds all the specified members with the specified scores …\nReturns the sorted set cardinality (number of elements) of …\nReturns the sorted set cardinality (number of elements) of …\nReturns the number of elements in the sorted set at key …\nReturns the number of elements in the sorted set at key …\nThis command is similar to ZDIFFSTORE, but instead of …\nThis command is similar to ZDIFFSTORE, but instead of …\nComputes the difference between the first and all …\nComputes the difference between the first and all …\nIncrements the score of member in the sorted set stored at …\nIncrements the score of member in the sorted set stored at …\nThis command is similar to ZINTERSTORE, but instead of …\nThis command is similar to ZINTERSTORE, but instead of …\nComputes the intersection of the sorted sets given by the …\nComputes the intersection of the sorted sets given by the …\nWhen all the elements in a sorted set are inserted with …\nWhen all the elements in a sorted set are inserted with …\nPops one or more elements, that are member-score pairs, …\nPops one or more elements, that are member-score pairs, …\nReturns the scores associated with the specified members …\nReturns the scores associated with the specified members …\nRemoves and returns up to count members with the highest …\nRemoves and returns up to count members with the highest …\nRemoves and returns up to count members with the lowest …\nRemoves and returns up to count members with the lowest …\nWhen called with just the key argument, return a random …\nWhen called with just the key argument, return a random …\nReturns the specified range of elements in the sorted set …\nReturns the specified range of elements in the sorted set …\nWhen all the elements in a sorted set are inserted with …\nWhen all the elements in a sorted set are inserted with …\nReturns all the elements in the sorted set at key with a …\nReturns all the elements in the sorted set at key with a …\nThis command is like ZRANGE, but stores the result in the …\nThis command is like ZRANGE, but stores the result in the …\nReturns the rank of member in the sorted set stored at key…\nReturns the rank of member in the sorted set stored at key…\nRemoves the specified members from the sorted set stored …\nRemoves the specified members from the sorted set stored …\nWhen all the elements in a sorted set are inserted with …\nWhen all the elements in a sorted set are inserted with …\nRemoves all elements in the sorted set stored at key with …\nRemoves all elements in the sorted set stored at key with …\nRemoves all elements in the sorted set stored at key with …\nRemoves all elements in the sorted set stored at key with …\nReturns the specified range of elements in the sorted set …\nReturns the specified range of elements in the sorted set …\nWhen all the elements in a sorted set are inserted with …\nWhen all the elements in a sorted set are inserted with …\nReturns all the elements in the sorted set at key with a …\nReturns all the elements in the sorted set at key with a …\nReturns the rank of member in the sorted set stored at key…\nReturns the rank of member in the sorted set stored at key…\nReturns the score of member in the sorted set at key.\nReturns the score of member in the sorted set at key.\nThis command is similar to ZUNIONSTORE, but instead of …\nThis command is similar to ZUNIONSTORE, but instead of …\nComputes the union of the sorted sets given by the …\nComputes the union of the sorted sets given by the …\nA command parsed from a MONITOR stream.\nArguments passed to the command.\nThe host and port of the client that ran the command, or …\nThe command run by the server.\nThe database against which the command was run.\nReturns the argument unchanged.\nCalls U::from(self).\nRun the MONITOR command against the provided server.\nWhen the command was run on the server.\nA node was added to the cluster.\nAn aggregation operation used in FT.AGGREGATE.\nAggregate options for the zinterstore (and related) …\nAn aggregation policy to use with certain timeseries …\nThe ANY flag used on certain GEO commands.\nAn array of frames.\nAn ordered list of values.\nAn ordered list of values.\nThe auto-generated key symbol “*”.\nConfiguration options for backpressure features in the …\nBackpressure policies to apply when the max number of …\nA large number not representable as a Number or Double.\nA blob representing an error.\nA blob of bytes.\nWait to send the command until the blocked command …\nDescribes how the client should respond when a command is …\nA boolean type.\nA boolean value.\nA boolean value.\nA BUCKETTIMESTAMP argument in commands such as TS.MRANGE.\nA client and pool builder interface.\nThe BUSY prefix.\nA byte array value.\nA byte array value.\nOne chunk of a streaming blob.\nFilters provided to the CLIENT KILL command.\nThe type of clients to close.\nFilters for the CLIENT PAUSE command.\nArguments for the CLIENT REPLY command.\nThe state of the underlying connection to the Redis server.\nArguments to the CLIENT UNBLOCK command.\nA policy that determines how clustered clients initially …\nThe CLUSTERDOWN prefix.\nOptions for the CLUSTER FAILOVER command.\nA cluster hashing policy.\nA parsed response from the CLUSTER INFO command.\nFlags for the CLUSTER RESET command.\nThe cached view of the cluster used by the client to route …\nFlags for the CLUSTER SETSLOT command.\nThe state of the cluster from the CLUSTER INFO command.\nAn enum describing the possible ways in which a Redis …\nAlways use the endpoint(s) provided in the client’s …\nThe result from any of the connect functions showing the …\nConfiguration options related to the creation or …\nWait a constant amount of time between reconnect attempts, …\nProvide a custom hash slot value.\nProvide a custom mapping from IP address to hostname to be …\nA case-sensitive prefix on an error message.\nUnix time (milliseconds since epoch).\nConfiguration for custom redis commands, primarily used …\nThe default amount of jitter when waiting to reconnect.\nThe parsed result of the MEMORY STATS command for a …\nReplace any IP addresses in the CLUSTER SLOTS response …\nA signed 64-bit floating point number.\nA double floating point number.\nA double floating point number.\nWait for all in-flight commands to finish before sending …\nThe duplicate policy used with certain timeseries commands.\nExpiration in seconds.\nExpiration time, in seconds.\nEquivalent to -.\nEncoding arguments for certain timeseries commands.\nReturn an error to the caller.\nExpiration options for the set command.\nOptions for certain expiration commands (PEXPIRE, etc).\nBackoff reconnection attempts exponentially, multiplying …\nHash the first string or bytes value in the arguments. …\nHash the first argument regardless of type.\nThe policy type for the FUNCTION RESTORE command.\nA trait used to convert various forms of RedisValue into …\nA trait used to convert RedisKey values to various types.\nArguments to the FT.AGGREGATE command.\nArguments to FT.ALTER.\nArguments for FT.CREATE.\nArguments to FT.SEARCH.\nAn individual function within a Library.\nPossible flags associated with a Function.\nA struct describing the longitude and latitude coordinates …\nA typed struct representing the full output of the …\nUnits for the GEO DIST command.\nA struct describing the value inside a GEO data structure.\nArguments equivalent to …\nA timestamp query used in commands such as TS.MRANGE.\nA struct representing GROUPBY label REDUCE reducer in …\nThe result of a HSCAN operation.\nA special frame type used when first connecting to the …\nA trait used for mapping IP addresses to hostnames when …\nIndex ranges (https://redis.io/commands/zrange#index-ranges…\nIndex arguments for FT.CREATE.\nShortcut for the + character.\nShortcut for the +inf range bound.\nOptions for the info command.\nAn integer value.\nAn integer value.\nInterrupt the blocked command by automatically sending …\nA client tracking invalidation message from the provided …\nDo not reset the TTL.\nAn event on the publish-subscribe interface describing a …\nThe direction to move elements in a *LMOVE command.\nEquivalent to +\nLexicographical ranges (…\nA helper struct for interacting with libraries and …\nA tuple of (offset, count) values for commands that allow …\nAn argument type equivalent to “[LIMIT count]”.\nBackoff reconnection attempts linearly, adding delay each …\nLocation flag for the LINSERT command.\nArguments to LOAD in FT.AGGREGATE.\nThe LOADING prefix.\nAn ID specified by the user such as “12345-0”.\nAn unordered map of key-value pairs.\nA map of key/value pairs, primarily used in RESP3 mode.\nA map of key/value pairs, primarily used in RESP3 mode.\nThe MASTERDOWN prefix.\nThe highest ID in a stream (“$”).\nThe parsed result of the MEMORY STATS command.\nA publish-subscribe message.\nA message from a subscribe command.\nThe kind of pubsub message.\nThe MISCONF prefix.\nA convenience struct for commands that take one or more …\nA convenience struct for functions that take one or more …\nOne or more IDs for elements in a stream.\nConvenience struct for commands that take 1 or more keys.\nOne or more ordered key-value pairs, typically used as an …\nConvenience interface for commands that take 1 or more …\nConvenience interface for commands that take 1 or more …\nConvenience struct for ZINTERSTORE and ZUNIONSTORE when …\nConvenience struct for the ZADD command to accept 1 or …\nShortcut for the -inf range bound.\nShortcut for the - character.\nFor XREADGROUP, only return new IDs (“>”).\nThe NOREPLICAS prefix.\nNo value.\nDo not modify or replace hostnames or IP addresses in the …\nThe server’s current time, equivalent to “*”.\nA null type.\nA nil value.\nA nil value.\nA signed 64-bit integer.\nHash the value with the provided offset in the arguments …\nOptions to configure or overwrite for individual commands.\nOrdering options for the ZADD (and related) commands.\nA message from a pattern psubscribe command.\nExpiration in milliseconds.\nExpiration time, in milliseconds.\nThe type of results from the scan operation.\nConfiguration options that can affect the performance of …\nOut-of-band data.\nA special value used to indicate a MULTI block command was …\nA special value used to indicate a MULTI block command was …\nUse a random node in the cluster.\nA struct representing …\nThe READONLY prefix, which can happen if a primary node is …\nHash slots were rebalanced across the cluster and/or local …\nSpecial errors that can trigger reconnection logic, which …\nThe type of reconnection policy to use. This will apply to …\nConfiguration options for a RedisClient.\nA key in Redis.\nA map of (RedisKey, RedisValue) pairs.\nA value used in a Redis command.\nThe kind of value from Redis.\nA REDUCER argument in commands such as TS.MRANGE.\nGROUPBY reducer functions.\nA node was removed from the cluster.\nConfiguration options for replica node connections.\nAn interface used to filter the list of available replica …\nA trait that can be used to override DNS resolution logic.\nShorthand for the result of commands such as MGET, MRANGE, …\nA RESP3 frame that uses Bytes and Str as the underlying …\nThe RESP3 equivalent of Resp2TimeSeriesValues.\nThe RESP version used in the HELLO request.\nA message from a sharded ssubscribe command.\nThe result of a SSCAN operation.\nThe result of a SCAN operation.\nThe types of values supported by the type command.\nAn interface for interacting with the results of a scan …\nScore ranges (https://redis.io/commands/zrange#score-ranges…\nAn interface for caching and running lua scripts.\nFlags for the SCRIPT DEBUG command.\nA search field with an optional property.\nArguments for FILTER in FT.SEARCH.\nArguments for GEOFILTER in FT.SEARCH.\nArguments used in HIGHLIGHT values.\nArguments for PARAMS in FT.AGGREGATE.\nREDUCE arguments in FT.AGGREGATE.\nArguments for SCHEMA in FT.CREATE.\nOne of the available schema types used with FT.CREATE or …\nArguments for SORTBY in FT.SEARCH.\nArguments used in SUMMARIZE values.\nConfiguration options for sentinel clients.\nArguments for the SENTINEL SIMULATE-FAILURE command.\nState necessary to identify or connect to a server.\nConnection configuration for the Redis server.\nAn unordered collection of other frames with a uniqueness …\nOptions for the set command.\nArguments passed to the SHUTDOWN command.\nA small string representing an error.\nA small string.\nSleep for some amount of time before sending the next …\nA slot range and associated cluster node information from …\nThe output of an entry in the slow queries log.\nSome value of type T.\nThe sort order for redis commands that take or return a …\nArguments to TERMS in FT.SPELLCHECK,\nStats describing a distribution of samples.\nA string value.\nA string value.\nAn argument representing a string or number.\nTCP configuration options.\nA timestamp used in most timeseries commands.\nTLS configuration for a client.\nAn enum for interacting with various TLS libraries and …\nAn optional enum used to describe how the client should …\nAn ON|OFF flag used with client tracking commands.\nConfiguration options for tracing.\nConfiguration options used to detect potentially …\nTry connecting to nodes specified in both the client’s …\nA string to be displayed without any escaping or filtering.\nSemVer version as defined by https://semver.org.\nArguments for WITHCURSOR in FT.AGGREGATE.\nStream cap arguments for XADD, XTRIM, etc.\nThe MAXLEN or MINID argument for a stream cap.\nRepresentation for the “=” or “~” operator in XADD…\nStream ID arguments for XADD, XREAD, etc.\nA struct representing the trailing optional arguments to …\nA generic helper type describing the top level response …\nA generic helper type describing the ID and associated map …\nMIN|MAX arguments for BZMPOP, etc.\nA wrapper struct for a range bound in a sorted set command.\nAn index, score, lexicographical, or +|-|+inf|-inf range …\nThe type of range interval bound.\nThe result of a ZSCAN operation.\nOptions for the ZRANGE (and related) commands.\nAttempt to add attributes to the frame, extending the …\nReturn the length of the inner array if the value is an …\nAttempt to convert the value to a bool.\nRead the key as a byte slice.\nRead the inner value as an array of bytes, if possible.\nParse and return the key as a Str without copying the …\nRead the inner value as a Str.\nRead and return the inner value as a f64, if possible.\nParse the value as the response from FUNCTION LIST, …\nConvert the value into a GeoPosition, if possible.\nRead and return the inner value as a i64, if possible.\nRead the key as a str slice if it can be parsed as a UTF8 …\nRead the inner value as a string slice.\nRead the key as a lossy UTF8 string with …\nRead the inner value as a string, using …\nRead and return the inner String if the value is a string …\nRead and return the inner value as a u64, if possible.\nRead and return the inner value as a usize, if possible.\nRead the number of reconnection attempts.\nAutomatically send CLIENT SETNAME on each connection …\nWhether the client should automatically pipeline commands …\nConfiguration options for backpressure features in the …\nThe default behavior of the client when a command is sent …\nWhether the command should block the connection while …\nThe minimum size, in bytes, of frames that should be …\nThe default capacity used when creating broadcast channels …\nCreate a new client.\nCreate a new client pool.\nCreate a new sentinel client.\nCreate a new subscriber client.\nWhether to send CLIENT CACHING yes|no before the command.\nWhether the value can be hashed.\nThe channel on which the message was sent.\nThe amount of time to wait after a MOVED error is received …\nHash the key to find the associated cluster hash slot.\nThe cluster hashing policy to use, if applicable.\nThe cluster hashing policy to use, if any.\nThe cluster node that should receive the command.\nRead the host:port of the cluster node that owns the key …\nThe command name, sent directly to the server.\nCompare the major, minor, patch, and pre-release value of …\nThe number of times a command can fail with a replica …\nThe task queue onto which connection reader tasks will be …\nThe timeout to apply when attempting to create a new TCP …\nThe TLS connector from either native-tls or rustls.\nAttempt to convert the key to any type that implements …\nAttempt to convert this value to any value that implements …\nA lightweight function to create a Redis client from the …\nRead the cursor returned from the last scan operation.\nAn optional database number that the client will …\nCreate a new builder instance with default config values …\nCreate a centralized config with default settings for a …\nCreate a new builder instance with default config values …\nCreate a clustered config with the same defaults as …\nAn optional timeout to apply to all commands.\nCreate a default TLS connector from the native-tls module.\nCreate a default TLS connector with the rustls module with …\nCreate a new Sleep policy with the legacy default values.\nSet the tracing::Level of spans under partial-tracing …\nWhether to disable the automatic backpressure features …\nDisable the CLUSTER INFO health check when initializing …\nWhether to enable tracing for this client.\nThe end of the hash slot range.\nSend EVALSHA to the server with the provided arguments.\nSend EVALSHA to the server with the provided arguments. …\nSet the non-null values from other onto self.\nWhether the client should return an error if it cannot …\nWhether the command should fail quickly if the connection …\nSend the fcall command via the provided client.\nSend the fcall_ro command via the provided client.\nReturns whether the replica node mapping can be used when …\nReturns whether the replica node mapping can be used when …\nAn optional interface for filtering available replica …\nFind the key to hash with the provided arguments.\nRead the flags associated with the function.\nFlatten adjacent nested arrays to the provided depth.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCreate a new routing table from the result of the …\nCreate a new Library with the provided code, loading it on …\nCreate a new builder instance from the provided client …\nCreate a new Script from a lua hash.\nCreate a new Script from a lua script.\nCreate a new Library with the associated name, inspecting …\nParse the value with context from the calling command.\nCreate a new RedisKey from static bytes without copying.\nCreate a new RedisValue::Bytes from a static byte slice …\nAn optimized way to convert from &'static str that avoids …\nCreate a new RedisKey from a &'static str without copying.\nCreate a new RedisValue::String from a static str without …\nParse the string representation of the flag.\nParse a URL string into a RedisConfig.\nCreate a centralized RedisConfig struct from a URL.\nCreate a clustered RedisConfig struct from a URL.\nCreate a sentinel RedisConfig struct from a URL.\nSet the tracing::Level of spans under full-tracing feature.\nRead the functions contained within this library.\nRead the client config.\nRead the connection config.\nRead the performance config.\nRead the reconnection policy.\nRead the sentinel client config.\nFind the primary server that owns the provided hash slot.\nWhether the scan call will continue returning results. If …\nHash the provided arguments.\nCalculate the cluster hash slot for the provided key.\nThe hostname or IP address for the server.\nThe hostname for the sentinel node.\nThe hostname modification or mapping policy to use when …\nRead the server hosts or sentinel hosts if using the …\nThe internal ID assigned by the server.\nWhether the client should ignore errors from replicas that …\nRead the inner Bytes struct.\nTake the inner HashMap.\nThe timeout to apply when sending internal commands such …\nThe frequency at which the client checks for unresponsive …\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nConvert this value to an array if it’s an array or map.\nRead the inner bytes making up the key.\nConvert the value into a Bytes view.\nRead and return the inner data as a Str from the bytes …\nParse the value as the response to any of the relevant GEO …\nAttempt to convert the value into an integer, returning …\nConvert the value to JSON.\nAttempt to convert this value to a Redis map if it’s an …\nConvert the value to an array of bytes, if possible.\nConvert the array value to a set, if possible.\nConvert the key to a UTF8 string, if possible.\nRead and return the inner String if the value is a string …\nA utility function to convert the response from XAUTOCLAIM …\nA utility function to convert the response from XREAD or …\nA utility function to convert the response from XCLAIM, …\nConvert a RedisValue to Vec<(RedisValue, f64)>, if …\nWhether the value is an array or map.\nWhether the value is an array.\nWhether the value is a boolean value or can be parsed as a …\nCheck if the value is an array of bytes.\nWhether the config is for a centralized server.\nWhether the config uses a clustered deployment.\nWhether the inner value is a double or can be parsed as a …\nCheck if the value is an integer.\nWhether the value is a RedisMap.\nWhether the value is a RedisMap or an array with an even …\nCheck if the value is null.\nWhether the value is a simple string OK value.\nCheck if the value is a QUEUED response.\nWhether the config is for a centralized server behind a …\nCheck if the value is a string.\nWhether the config uses a Unix socket.\nSet the TCP keepalive values.\nRead the type of the value without any associated data.\nThe type of message subscription.\nWhether the client should lazily connect to replica nodes.\nRead the number of hash slot ranges in the cluster.\nRead the number of (key, value) pairs in the map.\nSet the SO_LINGER value.\nCall SCRIPT LOAD on all the associated servers. This must …\nRead the lua script contents.\nMap the provided IP address to a hostname that should be …\nSet the max number of write attempts for a command.\nThe maximum number of times the client will attempt to …\nLimit the size of the internal in-memory command queue.\nThe maximum number of frames that will be fed to a socket …\nThe maximum number of in-flight commands (per connection) …\nThe maximum number of times the client will attempt to …\nSet the max number of cluster redirections to follow for a …\nIf provided, the amount of time a frame can wait without a …\nRead the name of the function.\nRead the name of the library.\nCreate Version with an empty pre-release and build …\nCreate a new Server from parts.\nCreate a new empty routing table.\nCreate a new empty map.\nCreate a new custom command.\nCreate a new Function.\nCreate a new centralized config with the provided host and …\nCreate a new clustered config with the provided set of …\nCreate a new reconnect policy with a constant backoff.\nCreate a new reconnect policy with an exponential backoff.\nCreate a new reconnect policy with a linear backoff.\nCreate a new RedisValue with the OK status.\nCreate a new sentinel config with the provided set of …\nCreate a new custom command specified by a &'static str.\nCreate a new Server from parts with a TLS server name.\nMove on to the next page of results from the SCAN …\nCalculate the next delay, incrementing attempts in the …\nWhether to skip backpressure checks for a command.\nSet the TCP_NODELAY value.\nCreate Version by parsing from string representation.\nAn optional password for the client to use when …\nAn optional password for the client to use when …\nThe backpressure policy to apply when the max number of …\nThe port for the server.\nThe port on which the sentinel node is listening.\nPrint the contents of the routing table as a …\nThe primary server owner.\nWhether the client should use the associated primary node …\nRead a random primary node from the cluster cache.\nRead a random primary node hash slot range from the …\nErrors that should trigger reconnection logic.\nAn unexpected NOAUTH error is treated the same as a …\nConfiguration options for replica nodes.\nRead the replicas associated with the provided primary …\nReplica node owners.\nResolve a hostname.\nReturn a reference to the last page of results.\nThe task queue onto which routing tasks will be spawned.\nThe server that sent the message.\nConnection configuration for the server(s).\nSet the ClusterDiscoveryPolicy, if possible.\nOverwrite the client config on the builder.\nOverwrite the connection config on the builder.\nSet the amount of jitter to add to each reconnect delay.\nOverwrite the performance config on the builder.\nOverwrite the reconnection policy on the builder.\nOverwrite the sentinel config on the builder.\nRead the SHA-1 hash for the script.\nRead the hash slot ranges in the cluster.\nThe start of the hash slot range.\nReplace this key with an empty byte array, returning the …\nReplace the value an empty map, returning the original …\nReplace this value with RedisValue::Null, returning the …\nTake ownership over the results of the SCAN operation. …\nTCP connection options.\nSet the timeout duration for a command.\nTLS configuration options.\nTLS configuration fields. If None the connection will not …\nThe server name used during the TLS handshake.\nCopy the frame contents into a new OwnedFrame.\nConvert to the string representation of the flag.\nTracing configuration options.\nWhether to enable tracing for this client.\nSet the IP_TTL value.\nRead a set of unique hash slots that each map to a …\nRead the set of unique primary nodes in the cluster.\nUnresponsive connection configuration options.\nAn optional ACL username for the client to use when …\nAn optional ACL username for the client to use when …\nWhether the client uses a native-tls connector.\nWhether the client uses a rustls connector.\nWhether the client uses TLS.\nThe message contents.\nThe protocol version to use when communicating with the …\nModify the client config in place, creating a new one with …\nModify the connection config in place, creating a new one …\nModify the performance config in place, creating a new one …\nModify the sentinel config in place, creating a new one …\nAn empty array is equivalent to GROUPBY 0\nDisable the backpressure scaling logic used to calculate …\nThe minimum amount of time to wait when applying …\nThe known cluster node Server identifiers.\nAn array of Server identifiers for each known sentinel …\nThe cluster discovery policy to use when connecting or …\nThe Server identifier.\nThe service name for primary/main instances.\nA convenience constant for None values used as generic …\nConvert an f64 to a redis string, supporting “+inf” …\nGroup the provided arguments by their cluster hash slot.\nMap a key to the corresponding cluster key slot.\nConvert a redis string to an f64, supporting “+inf” …\nCalculate the SHA1 hash output as a hex string. This is …\nCreate a Bytes from static bytes without copying.\nCreate a Str from a static str slice without copying.") \ No newline at end of file diff --git a/doc/settings.html b/doc/settings.html new file mode 100644 index 00000000..ae7cc9cb --- /dev/null +++ b/doc/settings.html @@ -0,0 +1 @@ +Settings

Rustdoc settings

Back
\ No newline at end of file diff --git a/doc/src-files.js b/doc/src-files.js new file mode 100644 index 00000000..d42ab281 --- /dev/null +++ b/doc/src-files.js @@ -0,0 +1,3 @@ +var srcIndex = new Map(JSON.parse('[["fred",["",[["clients",[],["mod.rs","options.rs","pipeline.rs","pool.rs","pubsub.rs","redis.rs","replica.rs","sentinel.rs","transaction.rs"]],["commands",[["impls",[],["acl.rs","client.rs","cluster.rs","config.rs","geo.rs","hashes.rs","hyperloglog.rs","keys.rs","lists.rs","lua.rs","memory.rs","mod.rs","pubsub.rs","redis_json.rs","redisearch.rs","scan.rs","sentinel.rs","server.rs","sets.rs","slowlog.rs","sorted_sets.rs","streams.rs","strings.rs","timeseries.rs","tracking.rs"]],["interfaces",[],["acl.rs","client.rs","cluster.rs","config.rs","geo.rs","hashes.rs","hyperloglog.rs","keys.rs","lists.rs","lua.rs","memory.rs","metrics.rs","mod.rs","pubsub.rs","redis_json.rs","redisearch.rs","scan.rs","sentinel.rs","server.rs","sets.rs","slowlog.rs","sorted_sets.rs","streams.rs","strings.rs","timeseries.rs","tracking.rs","transactions.rs"]]],["mod.rs"]],["glommio",[],["broadcast.rs","interfaces.rs","io_compat.rs","mod.rs","mpsc.rs","sync.rs"]],["modules",[],["backchannel.rs","inner.rs","metrics.rs","mod.rs","response.rs"]],["monitor",[],["mod.rs","parser.rs","utils.rs"]],["protocol",[],["cluster.rs","codec.rs","command.rs","connection.rs","debug.rs","hashers.rs","mod.rs","responders.rs","tls.rs","types.rs","utils.rs"]],["router",[],["centralized.rs","clustered.rs","commands.rs","mod.rs","reader.rs","replicas.rs","responses.rs","sentinel.rs","transactions.rs","types.rs","utils.rs"]],["trace",[],["disabled.rs","enabled.rs","mod.rs"]],["types",[],["args.rs","builder.rs","client.rs","cluster.rs","config.rs","from_tuple.rs","geo.rs","lists.rs","misc.rs","mod.rs","multiple.rs","redisearch.rs","scan.rs","scripts.rs","sorted_sets.rs","streams.rs","timeseries.rs"]]],["error.rs","interfaces.rs","lib.rs","macros.rs","utils.rs"]]]]')); +createSrcSidebar(); +//{"start":36,"fragment_lengths":[1739]} \ No newline at end of file diff --git a/doc/src/fred/clients/mod.rs.html b/doc/src/fred/clients/mod.rs.html new file mode 100644 index 00000000..a41587ab --- /dev/null +++ b/doc/src/fred/clients/mod.rs.html @@ -0,0 +1,73 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+
mod options;
+mod pipeline;
+mod pool;
+mod redis;
+
+pub use options::WithOptions;
+pub use pipeline::Pipeline;
+pub use pool::RedisPool;
+pub use redis::RedisClient;
+
+#[cfg(not(feature = "glommio"))]
+pub use pool::ExclusivePool;
+
+#[cfg(feature = "sentinel-client")]
+mod sentinel;
+#[cfg(feature = "sentinel-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))]
+pub use sentinel::SentinelClient;
+
+#[cfg(feature = "subscriber-client")]
+mod pubsub;
+#[cfg(feature = "subscriber-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "subscriber-client")))]
+pub use pubsub::SubscriberClient;
+
+#[cfg(feature = "replicas")]
+mod replica;
+#[cfg(feature = "replicas")]
+#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+pub use replica::Replicas;
+
+#[cfg(feature = "transactions")]
+mod transaction;
+#[cfg(feature = "transactions")]
+#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))]
+pub use transaction::Transaction;
+
\ No newline at end of file diff --git a/doc/src/fred/clients/options.rs.html b/doc/src/fred/clients/options.rs.html new file mode 100644 index 00000000..98aef3c1 --- /dev/null +++ b/doc/src/fred/clients/options.rs.html @@ -0,0 +1,321 @@ +options.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+
use crate::{
+  error::RedisError,
+  interfaces::*,
+  modules::inner::RedisClientInner,
+  protocol::command::RedisCommand,
+  runtime::RefCount,
+  types::Options,
+};
+use std::{fmt, ops::Deref};
+
+/// A client interface used to customize command configuration options.
+///
+/// See [Options](crate::types::Options) for more information.
+///
+/// ```rust
+/// # use fred::prelude::*;
+/// # use std::time::Duration;
+/// async fn example() -> Result<(), RedisError> {
+///   let client = RedisClient::default();
+///   client.init().await?;
+///
+///   let options = Options {
+///     max_redirections: Some(3),
+///     max_attempts: Some(1),
+///     timeout: Some(Duration::from_secs(10)),
+///     ..Default::default()
+///   };
+///   let foo: Option<String> = client.with_options(&options).get("foo").await?;
+///
+///   // reuse the options bindings
+///   let with_options = client.with_options(&options);
+///   let foo: () = with_options.get("foo").await?;
+///   let bar: () = with_options.get("bar").await?;
+///
+///   // combine with other client types
+///   let pipeline = client.pipeline().with_options(&options);
+///   let _: () = pipeline.get("foo").await?;
+///   let _: () = pipeline.get("bar").await?;
+///   // custom options will be applied to each command
+///   println!("results: {:?}", pipeline.all::<i64>().await?);
+///
+///   Ok(())
+/// }
+/// ```
+#[derive(Clone)]
+pub struct WithOptions<C: ClientLike> {
+  pub(crate) client:  C,
+  pub(crate) options: Options,
+}
+
+impl<C: ClientLike> WithOptions<C> {
+  /// Read the options that will be applied to commands.
+  pub fn options(&self) -> &Options {
+    &self.options
+  }
+}
+
+impl<C: ClientLike> Deref for WithOptions<C> {
+  type Target = C;
+
+  fn deref(&self) -> &Self::Target {
+    &self.client
+  }
+}
+
+impl<C: ClientLike> fmt::Debug for WithOptions<C> {
+  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    f.debug_struct("WithOptions")
+      .field("client", &self.client.id())
+      .field("options", &self.options)
+      .finish()
+  }
+}
+
+impl<C: ClientLike> ClientLike for WithOptions<C> {
+  #[doc(hidden)]
+  fn inner(&self) -> &RefCount<RedisClientInner> {
+    self.client.inner()
+  }
+
+  #[doc(hidden)]
+  fn change_command(&self, command: &mut RedisCommand) {
+    self.client.change_command(command);
+    self.options.apply(command);
+  }
+
+  #[doc(hidden)]
+  fn send_command<T>(&self, command: T) -> Result<(), RedisError>
+  where
+    T: Into<RedisCommand>,
+  {
+    let mut command: RedisCommand = command.into();
+    self.options.apply(&mut command);
+    self.client.send_command(command)
+  }
+}
+
+#[cfg(feature = "i-acl")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))]
+impl<C: AclInterface> AclInterface for WithOptions<C> {}
+#[cfg(feature = "i-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))]
+impl<C: ClientInterface> ClientInterface for WithOptions<C> {}
+#[cfg(feature = "i-cluster")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))]
+impl<C: ClusterInterface> ClusterInterface for WithOptions<C> {}
+#[cfg(feature = "i-pubsub")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))]
+impl<C: PubsubInterface> PubsubInterface for WithOptions<C> {}
+#[cfg(feature = "i-config")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))]
+impl<C: ConfigInterface> ConfigInterface for WithOptions<C> {}
+#[cfg(feature = "i-geo")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))]
+impl<C: GeoInterface> GeoInterface for WithOptions<C> {}
+#[cfg(feature = "i-hashes")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))]
+impl<C: HashesInterface> HashesInterface for WithOptions<C> {}
+#[cfg(feature = "i-hyperloglog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))]
+impl<C: HyperloglogInterface> HyperloglogInterface for WithOptions<C> {}
+#[cfg(feature = "i-keys")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))]
+impl<C: KeysInterface> KeysInterface for WithOptions<C> {}
+#[cfg(feature = "i-lists")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))]
+impl<C: ListInterface> ListInterface for WithOptions<C> {}
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+impl<C: MemoryInterface> MemoryInterface for WithOptions<C> {}
+#[cfg(feature = "i-server")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+impl<C: AuthInterface> AuthInterface for WithOptions<C> {}
+#[cfg(feature = "i-server")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+impl<C: ServerInterface> ServerInterface for WithOptions<C> {}
+#[cfg(feature = "i-slowlog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))]
+impl<C: SlowlogInterface> SlowlogInterface for WithOptions<C> {}
+#[cfg(feature = "i-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))]
+impl<C: SetsInterface> SetsInterface for WithOptions<C> {}
+#[cfg(feature = "i-sorted-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))]
+impl<C: SortedSetsInterface> SortedSetsInterface for WithOptions<C> {}
+#[cfg(feature = "i-streams")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))]
+impl<C: StreamsInterface> StreamsInterface for WithOptions<C> {}
+#[cfg(feature = "i-scripts")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+impl<C: FunctionInterface> FunctionInterface for WithOptions<C> {}
+#[cfg(feature = "i-redis-json")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))]
+impl<C: RedisJsonInterface> RedisJsonInterface for WithOptions<C> {}
+#[cfg(feature = "i-time-series")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+impl<C: TimeSeriesInterface> TimeSeriesInterface for WithOptions<C> {}
+#[cfg(feature = "i-redisearch")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))]
+impl<C: RediSearchInterface> RediSearchInterface for WithOptions<C> {}
+
\ No newline at end of file diff --git a/doc/src/fred/clients/pipeline.rs.html b/doc/src/fred/clients/pipeline.rs.html new file mode 100644 index 00000000..15c910b3 --- /dev/null +++ b/doc/src/fred/clients/pipeline.rs.html @@ -0,0 +1,673 @@ +pipeline.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+
use crate::{
+  error::RedisError,
+  interfaces::{self, *},
+  modules::{inner::RedisClientInner, response::FromRedis},
+  prelude::{RedisResult, RedisValue},
+  protocol::{
+    command::{RedisCommand, RouterCommand},
+    responders::ResponseKind,
+    utils as protocol_utils,
+  },
+  runtime::{oneshot_channel, Mutex, OneshotReceiver, RefCount},
+  utils,
+};
+use std::{collections::VecDeque, fmt, fmt::Formatter};
+
+fn clone_buffered_commands(buffer: &Mutex<VecDeque<RedisCommand>>) -> VecDeque<RedisCommand> {
+  let guard = buffer.lock();
+  let mut out = VecDeque::with_capacity(guard.len());
+
+  for command in guard.iter() {
+    out.push_back(command.duplicate(ResponseKind::Skip));
+  }
+
+  out
+}
+
+fn prepare_all_commands(
+  commands: VecDeque<RedisCommand>,
+  error_early: bool,
+) -> (RouterCommand, OneshotReceiver<Result<Resp3Frame, RedisError>>) {
+  let (tx, rx) = oneshot_channel();
+  let expected_responses = commands
+    .iter()
+    .fold(0, |count, cmd| count + cmd.response.expected_response_frames());
+
+  let mut response = ResponseKind::new_buffer_with_size(expected_responses, tx);
+  response.set_error_early(error_early);
+
+  let commands: Vec<RedisCommand> = commands
+    .into_iter()
+    .enumerate()
+    .map(|(idx, mut cmd)| {
+      cmd.response = response.duplicate().unwrap_or(ResponseKind::Skip);
+      cmd.response.set_expected_index(idx);
+      cmd
+    })
+    .collect();
+  let command = RouterCommand::Pipeline { commands };
+
+  (command, rx)
+}
+
+/// Send a series of commands in a [pipeline](https://redis.io/docs/manual/pipelining/).
+///
+/// See the [all](Self::all), [last](Self::last), and [try_all](Self::try_all) functions for more information.
+pub struct Pipeline<C: ClientLike> {
+  commands: RefCount<Mutex<VecDeque<RedisCommand>>>,
+  client:   C,
+}
+
+#[doc(hidden)]
+impl<C: ClientLike> Clone for Pipeline<C> {
+  fn clone(&self) -> Self {
+    Pipeline {
+      commands: self.commands.clone(),
+      client:   self.client.clone(),
+    }
+  }
+}
+
+impl<C: ClientLike> fmt::Debug for Pipeline<C> {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    f.debug_struct("Pipeline")
+      .field("client", &self.client.inner().id)
+      .field("length", &self.commands.lock().len())
+      .finish()
+  }
+}
+
+#[doc(hidden)]
+impl<C: ClientLike> From<C> for Pipeline<C> {
+  fn from(client: C) -> Self {
+    Pipeline {
+      client,
+      commands: RefCount::new(Mutex::new(VecDeque::new())),
+    }
+  }
+}
+
+impl<C: ClientLike> ClientLike for Pipeline<C> {
+  #[doc(hidden)]
+  fn inner(&self) -> &RefCount<RedisClientInner> {
+    self.client.inner()
+  }
+
+  #[doc(hidden)]
+  fn change_command(&self, command: &mut RedisCommand) {
+    self.client.change_command(command);
+  }
+
+  #[doc(hidden)]
+  #[allow(unused_mut)]
+  fn send_command<T>(&self, command: T) -> Result<(), RedisError>
+  where
+    T: Into<RedisCommand>,
+  {
+    let mut command: RedisCommand = command.into();
+    self.change_command(&mut command);
+
+    if let Some(mut tx) = command.take_responder() {
+      trace!(
+        "{}: Respond early to {} command in pipeline.",
+        &self.client.inner().id,
+        command.kind.to_str_debug()
+      );
+      let _ = tx.send(Ok(protocol_utils::queued_frame()));
+    }
+
+    self.commands.lock().push_back(command);
+    Ok(())
+  }
+}
+
+#[cfg(feature = "i-acl")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))]
+impl<C: AclInterface> AclInterface for Pipeline<C> {}
+#[cfg(feature = "i-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))]
+impl<C: ClientInterface> ClientInterface for Pipeline<C> {}
+#[cfg(feature = "i-cluster")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))]
+impl<C: ClusterInterface> ClusterInterface for Pipeline<C> {}
+#[cfg(feature = "i-pubsub")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))]
+impl<C: PubsubInterface> PubsubInterface for Pipeline<C> {}
+#[cfg(feature = "i-config")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))]
+impl<C: ConfigInterface> ConfigInterface for Pipeline<C> {}
+#[cfg(feature = "i-geo")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))]
+impl<C: GeoInterface> GeoInterface for Pipeline<C> {}
+#[cfg(feature = "i-hashes")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))]
+impl<C: HashesInterface> HashesInterface for Pipeline<C> {}
+#[cfg(feature = "i-hyperloglog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))]
+impl<C: HyperloglogInterface> HyperloglogInterface for Pipeline<C> {}
+#[cfg(feature = "i-keys")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))]
+impl<C: KeysInterface> KeysInterface for Pipeline<C> {}
+#[cfg(feature = "i-lists")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))]
+impl<C: ListInterface> ListInterface for Pipeline<C> {}
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+impl<C: MemoryInterface> MemoryInterface for Pipeline<C> {}
+#[cfg(feature = "i-server")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+impl<C: AuthInterface> AuthInterface for Pipeline<C> {}
+#[cfg(feature = "i-server")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+impl<C: ServerInterface> ServerInterface for Pipeline<C> {}
+#[cfg(feature = "i-slowlog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))]
+impl<C: SlowlogInterface> SlowlogInterface for Pipeline<C> {}
+#[cfg(feature = "i-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))]
+impl<C: SetsInterface> SetsInterface for Pipeline<C> {}
+#[cfg(feature = "i-sorted-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))]
+impl<C: SortedSetsInterface> SortedSetsInterface for Pipeline<C> {}
+#[cfg(feature = "i-streams")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))]
+impl<C: StreamsInterface> StreamsInterface for Pipeline<C> {}
+#[cfg(feature = "i-scripts")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+impl<C: FunctionInterface> FunctionInterface for Pipeline<C> {}
+#[cfg(feature = "i-redis-json")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))]
+impl<C: RedisJsonInterface> RedisJsonInterface for Pipeline<C> {}
+#[cfg(feature = "i-time-series")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+impl<C: TimeSeriesInterface> TimeSeriesInterface for Pipeline<C> {}
+#[cfg(feature = "i-redisearch")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))]
+impl<C: RediSearchInterface> RediSearchInterface for Pipeline<C> {}
+
+impl<C: ClientLike> Pipeline<C> {
+  /// Send the pipeline and respond with an array of all responses.
+  ///
+  /// ```rust no_run
+  /// # use fred::prelude::*;
+  /// async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  ///   let _ = client.mset(vec![("foo", 1), ("bar", 2)]).await?;
+  ///
+  ///   let pipeline = client.pipeline();
+  ///   let _: () = pipeline.get("foo").await?; // returns when the command is queued in memory
+  ///   let _: () = pipeline.get("bar").await?; // returns when the command is queued in memory
+  ///
+  ///   let results: Vec<i64> = pipeline.all().await?;
+  ///   assert_eq!(results, vec![1, 2]);
+  ///   Ok(())
+  /// }
+  /// ```
+  pub async fn all<R>(&self) -> Result<R, RedisError>
+  where
+    R: FromRedis,
+  {
+    let commands = clone_buffered_commands(&self.commands);
+    send_all(self.client.inner(), commands).await?.convert()
+  }
+
+  /// Send the pipeline and respond with each individual result.
+  ///
+  /// Note: use `RedisValue` as the return type (and [convert](crate::types::RedisValue::convert) as needed) to
+  /// support an array of different return types.
+  ///
+  /// ```rust no_run
+  /// # use fred::prelude::*;
+  /// async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  ///   let _ = client.mset(vec![("foo", 1), ("bar", 2)]).await?;  
+  ///
+  ///   let pipeline = client.pipeline();
+  ///   let _: () = pipeline.get("foo").await?;
+  ///   let _: () = pipeline.hgetall("bar").await?; // this will error since `bar` is an integer
+  ///
+  ///   let results = pipeline.try_all::<RedisValue>().await;
+  ///   assert_eq!(results[0].clone()?.convert::<i64>()?, 1);
+  ///   assert!(results[1].is_err());
+  ///
+  ///   Ok(())
+  /// }
+  /// ```
+  pub async fn try_all<R>(&self) -> Vec<RedisResult<R>>
+  where
+    R: FromRedis,
+  {
+    let commands = clone_buffered_commands(&self.commands);
+    try_send_all(self.client.inner(), commands)
+      .await
+      .into_iter()
+      .map(|v| v.and_then(|v| v.convert()))
+      .collect()
+  }
+
+  /// Send the pipeline and respond with only the result of the last command.
+  ///
+  /// ```rust no_run
+  /// # use fred::prelude::*;
+  /// async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  ///   let pipeline = client.pipeline();
+  ///   let _: () = pipeline.incr("foo").await?; // returns when the command is queued in memory
+  ///   let _: () = pipeline.incr("foo").await?; // returns when the command is queued in memory
+  ///
+  ///   assert_eq!(pipeline.last::<i64>().await?, 2);
+  ///   // pipelines can also be reused
+  ///   assert_eq!(pipeline.last::<i64>().await?, 4);
+  ///   Ok(())
+  /// }
+  /// ```
+  pub async fn last<R>(&self) -> Result<R, RedisError>
+  where
+    R: FromRedis,
+  {
+    let commands = clone_buffered_commands(&self.commands);
+    send_last(self.client.inner(), commands).await?.convert()
+  }
+}
+
+async fn try_send_all(
+  inner: &RefCount<RedisClientInner>,
+  commands: VecDeque<RedisCommand>,
+) -> Vec<Result<RedisValue, RedisError>> {
+  if commands.is_empty() {
+    return Vec::new();
+  }
+
+  let (mut command, rx) = prepare_all_commands(commands, false);
+  command.inherit_options(inner);
+  let timeout_dur = command.timeout_dur().unwrap_or_else(|| inner.default_command_timeout());
+
+  if let Err(e) = interfaces::send_to_router(inner, command) {
+    return vec![Err(e)];
+  };
+  let frame = match utils::timeout(rx, timeout_dur).await {
+    Ok(result) => match result {
+      Ok(f) => f,
+      Err(e) => return vec![Err(e)],
+    },
+    Err(e) => return vec![Err(e)],
+  };
+
+  if let Resp3Frame::Array { data, .. } = frame {
+    data.into_iter().map(protocol_utils::frame_to_results).collect()
+  } else {
+    vec![protocol_utils::frame_to_results(frame)]
+  }
+}
+
+async fn send_all(
+  inner: &RefCount<RedisClientInner>,
+  commands: VecDeque<RedisCommand>,
+) -> Result<RedisValue, RedisError> {
+  if commands.is_empty() {
+    return Ok(RedisValue::Array(Vec::new()));
+  }
+
+  let (mut command, rx) = prepare_all_commands(commands, true);
+  command.inherit_options(inner);
+  let timeout_dur = command.timeout_dur().unwrap_or_else(|| inner.default_command_timeout());
+
+  interfaces::send_to_router(inner, command)?;
+  let frame = utils::timeout(rx, timeout_dur).await??;
+  protocol_utils::frame_to_results(frame)
+}
+
+async fn send_last(
+  inner: &RefCount<RedisClientInner>,
+  commands: VecDeque<RedisCommand>,
+) -> Result<RedisValue, RedisError> {
+  if commands.is_empty() {
+    return Ok(RedisValue::Null);
+  }
+
+  let len = commands.len();
+  let (tx, rx) = oneshot_channel();
+  let mut commands: Vec<RedisCommand> = commands.into_iter().collect();
+  commands[len - 1].response = ResponseKind::Respond(Some(tx));
+  let mut command = RouterCommand::Pipeline { commands };
+  command.inherit_options(inner);
+  let timeout_dur = command.timeout_dur().unwrap_or_else(|| inner.default_command_timeout());
+
+  interfaces::send_to_router(inner, command)?;
+  let frame = utils::timeout(rx, timeout_dur).await??;
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/clients/pool.rs.html b/doc/src/fred/clients/pool.rs.html new file mode 100644 index 00000000..0e61ff0a --- /dev/null +++ b/doc/src/fred/clients/pool.rs.html @@ -0,0 +1,1357 @@ +pool.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+
use crate::{
+  clients::RedisClient,
+  error::{RedisError, RedisErrorKind},
+  interfaces::*,
+  modules::inner::RedisClientInner,
+  runtime::{sleep, spawn, AtomicBool, AtomicUsize, RefCount},
+  types::{ConnectHandle, ConnectionConfig, PerformanceConfig, ReconnectPolicy, RedisConfig, Server},
+  utils,
+};
+use futures::future::{join_all, try_join_all};
+use rm_send_macros::rm_send_if;
+use std::{fmt, future::Future, time::Duration};
+
+#[cfg(feature = "replicas")]
+use crate::clients::Replicas;
+#[cfg(feature = "dns")]
+use crate::protocol::types::Resolve;
+#[cfg(not(feature = "glommio"))]
+pub use tokio::sync::{Mutex as AsyncMutex, OwnedMutexGuard};
+
+struct RedisPoolInner {
+  clients:          Vec<RedisClient>,
+  counter:          AtomicUsize,
+  prefer_connected: AtomicBool,
+}
+
+/// A cheaply cloneable round-robin client pool.
+///
+/// ### Restrictions
+///
+/// The following interfaces are not implemented on `RedisPool`:
+/// * [MetricsInterface](crate::interfaces::MetricsInterface)
+/// * [PubsubInterface](crate::interfaces::PubsubInterface)
+/// * [EventInterface](crate::interfaces::EventInterface)
+/// * [ClientInterface](crate::interfaces::ClientInterface)
+/// * [AuthInterface](crate::interfaces::AuthInterface)
+///
+/// In many cases, such as [publish](crate::interfaces::PubsubInterface::publish), callers can work around this by
+/// adding a call to [next](Self::next), but in some scenarios this may not work. As a general rule, any commands
+/// that change or depend on local connection state will not be implemented directly on `RedisPool`. Callers can use
+/// [clients](Self::clients), [next](Self::next), or [last](Self::last) to operate on individual clients if needed.
+#[derive(Clone)]
+pub struct RedisPool {
+  inner: RefCount<RedisPoolInner>,
+}
+
+impl fmt::Debug for RedisPool {
+  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    f.debug_struct("RedisPool")
+      .field("size", &self.inner.clients.len())
+      .field(
+        "prefer_connected",
+        &utils::read_bool_atomic(&self.inner.prefer_connected),
+      )
+      .finish()
+  }
+}
+
+impl RedisPool {
+  /// Create a new pool from an existing set of clients.
+  pub fn from_clients(clients: Vec<RedisClient>) -> Result<Self, RedisError> {
+    if clients.is_empty() {
+      Err(RedisError::new(RedisErrorKind::Config, "Pool cannot be empty."))
+    } else {
+      Ok(RedisPool {
+        inner: RefCount::new(RedisPoolInner {
+          clients,
+          counter: AtomicUsize::new(0),
+          prefer_connected: AtomicBool::new(true),
+        }),
+      })
+    }
+  }
+
+  /// Create a new pool without connecting to the server.
+  ///
+  /// See the [builder](crate::types::Builder) interface for more information.
+  pub fn new(
+    config: RedisConfig,
+    perf: Option<PerformanceConfig>,
+    connection: Option<ConnectionConfig>,
+    policy: Option<ReconnectPolicy>,
+    size: usize,
+  ) -> Result<Self, RedisError> {
+    if size == 0 {
+      Err(RedisError::new(RedisErrorKind::Config, "Pool cannot be empty."))
+    } else {
+      let mut clients = Vec::with_capacity(size);
+      for _ in 0 .. size {
+        clients.push(RedisClient::new(
+          config.clone(),
+          perf.clone(),
+          connection.clone(),
+          policy.clone(),
+        ));
+      }
+
+      Ok(RedisPool {
+        inner: RefCount::new(RedisPoolInner {
+          clients,
+          counter: AtomicUsize::new(0),
+          prefer_connected: AtomicBool::new(true),
+        }),
+      })
+    }
+  }
+
+  /// Set whether the client will use [next_connected](Self::next_connected) or [next](Self::next) when routing
+  /// commands among the pooled clients.
+  pub fn prefer_connected(&self, val: bool) -> bool {
+    utils::set_bool_atomic(&self.inner.prefer_connected, val)
+  }
+
+  /// Read the individual clients in the pool.
+  pub fn clients(&self) -> &[RedisClient] {
+    &self.inner.clients
+  }
+
+  /// Connect each client to the server, returning the task driving each connection.
+  ///
+  /// Use the base [connect](Self::connect) function to return one handle that drives all connections via [join](https://docs.rs/futures/latest/futures/macro.join.html).
+  pub fn connect_pool(&self) -> Vec<ConnectHandle> {
+    self.inner.clients.iter().map(|c| c.connect()).collect()
+  }
+
+  /// Read the size of the pool.
+  pub fn size(&self) -> usize {
+    self.inner.clients.len()
+  }
+
+  /// Read the next connected client that should run the next command.
+  pub fn next_connected(&self) -> &RedisClient {
+    let mut idx = utils::incr_atomic(&self.inner.counter) % self.inner.clients.len();
+
+    for _ in 0 .. self.inner.clients.len() {
+      let client = &self.inner.clients[idx];
+      if client.is_connected() {
+        return client;
+      }
+      idx = (idx + 1) % self.inner.clients.len();
+    }
+
+    &self.inner.clients[idx]
+  }
+
+  /// Read the client that should run the next command.
+  pub fn next(&self) -> &RedisClient {
+    &self.inner.clients[utils::incr_atomic(&self.inner.counter) % self.inner.clients.len()]
+  }
+
+  /// Read the client that ran the last command.
+  pub fn last(&self) -> &RedisClient {
+    &self.inner.clients[utils::read_atomic(&self.inner.counter) % self.inner.clients.len()]
+  }
+
+  /// Create a client that interacts with the replica nodes associated with the [next](Self::next) client.
+  #[cfg(feature = "replicas")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+  pub fn replicas(&self) -> Replicas {
+    Replicas::from(self.inner())
+  }
+}
+
+#[rm_send_if(feature = "glommio")]
+impl ClientLike for RedisPool {
+  #[doc(hidden)]
+  fn inner(&self) -> &RefCount<RedisClientInner> {
+    if utils::read_bool_atomic(&self.inner.prefer_connected) {
+      &self.next_connected().inner
+    } else {
+      &self.next().inner
+    }
+  }
+
+  /// Update the internal [PerformanceConfig](crate::types::PerformanceConfig) on each client in place with new
+  /// values.
+  fn update_perf_config(&self, config: PerformanceConfig) {
+    for client in self.inner.clients.iter() {
+      client.update_perf_config(config.clone());
+    }
+  }
+
+  /// Read the set of active connections across all clients in the pool.
+  fn active_connections(&self) -> impl Future<Output = Result<Vec<Server>, RedisError>> + Send {
+    async move {
+      let all_connections = try_join_all(self.inner.clients.iter().map(|c| c.active_connections())).await?;
+      let total_size = if all_connections.is_empty() {
+        return Ok(Vec::new());
+      } else {
+        all_connections.len() * all_connections[0].len()
+      };
+      let mut out = Vec::with_capacity(total_size);
+
+      for connections in all_connections.into_iter() {
+        out.extend(connections);
+      }
+      Ok(out)
+    }
+  }
+
+  /// Override the DNS resolution logic for all clients in the pool.
+  #[cfg(feature = "dns")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "dns")))]
+  #[allow(refining_impl_trait)]
+  fn set_resolver(&self, resolver: RefCount<dyn Resolve>) -> impl Future + Send {
+    async move {
+      for client in self.inner.clients.iter() {
+        client.set_resolver(resolver.clone()).await;
+      }
+    }
+  }
+
+  /// Connect each client to the server.
+  ///
+  /// This function returns a `JoinHandle` to a task that drives **all** connections via [join](https://docs.rs/futures/latest/futures/macro.join.html).
+  ///
+  /// See [connect_pool](crate::clients::RedisPool::connect_pool) for a variation of this function that separates the
+  /// connection tasks.
+  ///
+  /// See [init](Self::init) for an alternative shorthand.
+  fn connect(&self) -> ConnectHandle {
+    let clients = self.inner.clients.clone();
+    spawn(async move {
+      let tasks: Vec<_> = clients.iter().map(|c| c.connect()).collect();
+      for result in join_all(tasks).await.into_iter() {
+        result??;
+      }
+
+      Ok::<(), RedisError>(())
+    })
+  }
+
+  /// Force a reconnection to the server(s) for each client.
+  ///
+  /// When running against a cluster this function will also refresh the cached cluster routing table.
+  fn force_reconnection(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move {
+      try_join_all(self.inner.clients.iter().map(|c| c.force_reconnection())).await?;
+      Ok(())
+    }
+  }
+
+  /// Wait for all the clients to connect to the server.
+  fn wait_for_connect(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move {
+      try_join_all(self.inner.clients.iter().map(|c| c.wait_for_connect())).await?;
+      Ok(())
+    }
+  }
+
+  /// Initialize a new routing and connection task for each client and wait for them to connect successfully.
+  ///
+  /// The returned [ConnectHandle](crate::types::ConnectHandle) refers to the task that drives the routing and
+  /// connection layer for each client via [join](https://docs.rs/futures/latest/futures/macro.join.html). It will not finish until the max reconnection count is reached.
+  ///
+  /// Callers can also use [connect](Self::connect) and [wait_for_connect](Self::wait_for_connect) separately if
+  /// needed.
+  ///
+  /// ```rust
+  /// use fred::prelude::*;
+  ///
+  /// #[tokio::main]
+  /// async fn main() -> Result<(), RedisError> {
+  ///   let pool = Builder::default_centralized().build_pool(5)?;
+  ///   let connection_task = pool.init().await?;
+  ///
+  ///   // ...
+  ///
+  ///   pool.quit().await?;
+  ///   connection_task.await?
+  /// }
+  /// ```
+  fn init(&self) -> impl Future<Output = RedisResult<ConnectHandle>> + Send {
+    async move {
+      let rxs: Vec<_> = self.inner.clients.iter().map(|c| c.wait_for_connect()).collect();
+
+      let connect_task = self.connect();
+      let init_err = futures::future::join_all(rxs).await.into_iter().find_map(|r| r.err());
+
+      if let Some(err) = init_err {
+        for client in self.inner.clients.iter() {
+          utils::reset_router_task(client.inner());
+        }
+
+        Err(err)
+      } else {
+        Ok(connect_task)
+      }
+    }
+  }
+
+  /// Close the connection to the Redis server for each client. The returned future resolves when the command has been
+  /// written to the socket, not when the connection has been fully closed. Some time after this future resolves the
+  /// future returned by [connect](Self::connect) will resolve which indicates that the connection has been fully
+  /// closed.
+  ///
+  /// This function will also close all error, pubsub message, and reconnection event streams on all clients in the
+  /// pool.
+  fn quit(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move {
+      join_all(self.inner.clients.iter().map(|c| c.quit())).await;
+
+      Ok(())
+    }
+  }
+}
+
+#[rm_send_if(feature = "glommio")]
+impl HeartbeatInterface for RedisPool {
+  fn enable_heartbeat(
+    &self,
+    interval: Duration,
+    break_on_error: bool,
+  ) -> impl Future<Output = RedisResult<()>> + Send {
+    async move {
+      loop {
+        sleep(interval).await;
+
+        if let Err(error) = try_join_all(self.inner.clients.iter().map(|c| c.ping::<()>())).await {
+          if break_on_error {
+            return Err(error);
+          }
+        }
+      }
+    }
+  }
+}
+
+#[cfg(feature = "i-acl")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))]
+impl AclInterface for RedisPool {}
+#[cfg(feature = "i-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))]
+impl ClientInterface for RedisPool {}
+#[cfg(feature = "i-cluster")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))]
+impl ClusterInterface for RedisPool {}
+#[cfg(feature = "i-config")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))]
+impl ConfigInterface for RedisPool {}
+#[cfg(feature = "i-geo")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))]
+impl GeoInterface for RedisPool {}
+#[cfg(feature = "i-hashes")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))]
+impl HashesInterface for RedisPool {}
+#[cfg(feature = "i-hyperloglog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))]
+impl HyperloglogInterface for RedisPool {}
+#[cfg(feature = "transactions")]
+#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))]
+impl TransactionInterface for RedisPool {}
+#[cfg(feature = "i-keys")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))]
+impl KeysInterface for RedisPool {}
+#[cfg(feature = "i-scripts")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+impl LuaInterface for RedisPool {}
+#[cfg(feature = "i-lists")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))]
+impl ListInterface for RedisPool {}
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+impl MemoryInterface for RedisPool {}
+#[cfg(feature = "i-server")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+impl ServerInterface for RedisPool {}
+#[cfg(feature = "i-slowlog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))]
+impl SlowlogInterface for RedisPool {}
+#[cfg(feature = "i-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))]
+impl SetsInterface for RedisPool {}
+#[cfg(feature = "i-sorted-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))]
+impl SortedSetsInterface for RedisPool {}
+#[cfg(feature = "i-streams")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))]
+impl StreamsInterface for RedisPool {}
+#[cfg(feature = "i-scripts")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+impl FunctionInterface for RedisPool {}
+#[cfg(feature = "i-redis-json")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))]
+impl RedisJsonInterface for RedisPool {}
+#[cfg(feature = "i-time-series")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+impl TimeSeriesInterface for RedisPool {}
+#[cfg(feature = "i-redisearch")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))]
+impl RediSearchInterface for RedisPool {}
+
+#[cfg(not(feature = "glommio"))]
+struct PoolInner {
+  clients: Vec<RefCount<AsyncMutex<RedisClient>>>,
+  counter: AtomicUsize,
+}
+
+/// A cheaply cloneable round-robin client pool that provides exclusive ownership over the inner clients.
+///
+/// This interface can be used when callers require exclusive ownership over the connection. For example,
+///
+/// ```no_run no_compile
+/// WATCH foo
+/// foo = GET foo
+/// if foo > 1:
+///   MULTI
+///     INCR foo
+///     INCR bar
+///     INCR baz
+///   EXEC
+/// ```
+///
+/// Unlike [RedisPool](crate::clients::RedisPool), this pooling interface does not directly implement
+/// [ClientLike](crate::interfaces::ClientLike). Callers acquire and release clients via the returned
+/// [MutexGuard](OwnedMutexGuard).
+///
+/// ```rust
+/// use fred::{
+///   clients::{ExclusivePool, RedisPool},
+///   prelude::*,
+/// };
+///
+/// async fn example() -> Result<(), RedisError> {
+///   let builder = Builder::default_centralized();
+///   let shared_pool = builder.build_pool(5)?;
+///   let exclusive_pool = builder.build_exclusive_pool(5)?;
+///   shared_pool.init().await?;
+///   exclusive_pool.init().await?;
+///
+///   // since `RedisPool` implements `ClientLike` we can use most command interfaces directly
+///   let foo: Option<String> = shared_pool.set("foo", 1, None, None, false).await?;
+///
+///   // with an `ExclusivePool` callers acquire and release clients with an async lock guard
+///   let results: Option<(i64, i64, i64)> = {
+///     let client = exclusive_pool.acquire().await;
+///
+///     client.watch("foo").await?;
+///     if let Some(1) = client.get::<Option<i64>, _>("foo").await? {
+///       let trx = client.multi();
+///       trx.incr("foo").await?;
+///       trx.incr("bar").await?;
+///       trx.incr("baz").await?;
+///       Some(trx.exec(true).await?)
+///     } else {
+///       None
+///     }
+///   };
+///   assert_eq!(results, Some((2, 1, 1)));
+///
+///   Ok(())
+/// }
+/// ```
+///
+/// Callers should avoid cloning the inner clients, if possible.
+#[cfg(not(feature = "glommio"))]
+#[derive(Clone)]
+pub struct ExclusivePool {
+  inner: RefCount<PoolInner>,
+}
+
+#[cfg(not(feature = "glommio"))]
+impl fmt::Debug for ExclusivePool {
+  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    f.debug_struct("ExclusivePool")
+      .field("size", &self.inner.clients.len())
+      .finish()
+  }
+}
+
+#[cfg(not(feature = "glommio"))]
+impl ExclusivePool {
+  /// Create a new pool without connecting to the server.
+  ///
+  /// See the [builder](crate::types::Builder) interface for more information.
+  pub fn new(
+    config: RedisConfig,
+    perf: Option<PerformanceConfig>,
+    connection: Option<ConnectionConfig>,
+    policy: Option<ReconnectPolicy>,
+    size: usize,
+  ) -> Result<Self, RedisError> {
+    if size == 0 {
+      Err(RedisError::new(RedisErrorKind::Config, "Pool cannot be empty."))
+    } else {
+      let mut clients = Vec::with_capacity(size);
+      for _ in 0 .. size {
+        clients.push(RefCount::new(AsyncMutex::new(RedisClient::new(
+          config.clone(),
+          perf.clone(),
+          connection.clone(),
+          policy.clone(),
+        ))));
+      }
+
+      Ok(ExclusivePool {
+        inner: RefCount::new(PoolInner {
+          clients,
+          counter: AtomicUsize::new(0),
+        }),
+      })
+    }
+  }
+
+  /// Read the clients in the pool.
+  pub fn clients(&self) -> &[RefCount<AsyncMutex<RedisClient>>] {
+    &self.inner.clients
+  }
+
+  /// Connect each client to the server, returning the task driving each connection.
+  ///
+  /// Use the base [connect](Self::connect) function to return one handle that drives all connections via [join](https://docs.rs/futures/latest/futures/macro.join.html).
+  pub async fn connect_pool(&self) -> Vec<ConnectHandle> {
+    let mut connect_tasks = Vec::with_capacity(self.inner.clients.len());
+    for locked_client in self.inner.clients.iter() {
+      connect_tasks.push(locked_client.lock().await.connect());
+    }
+    connect_tasks
+  }
+
+  /// Connect each client to the server.
+  ///
+  /// This function returns a `JoinHandle` to a task that drives **all** connections via [join](https://docs.rs/futures/latest/futures/macro.join.html).
+  ///
+  /// See [connect_pool](crate::clients::RedisPool::connect_pool) for a variation of this function that separates the
+  /// connection tasks.
+  ///
+  /// See [init](Self::init) for an alternative shorthand.
+  pub async fn connect(&self) -> ConnectHandle {
+    let tasks = self.connect_pool().await;
+    tokio::spawn(async move {
+      for result in join_all(tasks).await.into_iter() {
+        result??;
+      }
+
+      Ok(())
+    })
+  }
+
+  /// Force a reconnection to the server(s) for each client.
+  ///
+  /// When running against a cluster this function will also refresh the cached cluster routing table.
+  pub async fn force_reconnection(&self) -> RedisResult<()> {
+    let mut fts = Vec::with_capacity(self.inner.clients.len());
+    for locked_client in self.inner.clients.iter() {
+      let client = locked_client.clone();
+      fts.push(async move { client.lock_owned().await.force_reconnection().await });
+    }
+
+    try_join_all(fts).await?;
+    Ok(())
+  }
+
+  /// Wait for all the clients to connect to the server.
+  pub async fn wait_for_connect(&self) -> RedisResult<()> {
+    let mut fts = Vec::with_capacity(self.inner.clients.len());
+    for locked_client in self.inner.clients.iter() {
+      let client = locked_client.clone();
+      fts.push(async move { client.lock().await.wait_for_connect().await });
+    }
+
+    try_join_all(fts).await?;
+    Ok(())
+  }
+
+  /// Initialize a new routing and connection task for each client and wait for them to connect successfully.
+  ///
+  /// The returned [ConnectHandle](crate::types::ConnectHandle) refers to the task that drives the routing and
+  /// connection layer for each client. It will not finish until the max reconnection count is reached.
+  ///
+  /// Callers can also use [connect](Self::connect) and [wait_for_connect](Self::wait_for_connect) separately if
+  /// needed.
+  ///
+  /// ```rust
+  /// use fred::prelude::*;
+  ///
+  /// #[tokio::main]
+  /// async fn main() -> Result<(), RedisError> {
+  ///   let pool = Builder::default_centralized().build_exclusive_pool(5)?;
+  ///   let connection_task = pool.init().await?;
+  ///
+  ///   // ...
+  ///
+  ///   pool.quit().await?;
+  ///   connection_task.await?
+  /// }
+  /// ```
+  pub async fn init(&self) -> RedisResult<ConnectHandle> {
+    let mut rxs = Vec::with_capacity(self.inner.clients.len());
+    for locked_client in self.inner.clients.iter() {
+      let mut rx = {
+        locked_client
+          .lock()
+          .await
+          .inner
+          .notifications
+          .connect
+          .load()
+          .subscribe()
+      };
+
+      rxs.push(async move { rx.recv().await });
+    }
+
+    let connect_task = self.connect().await;
+    let init_err = join_all(rxs).await.into_iter().find_map(|r| match r {
+      Ok(Err(e)) => Some(e),
+      Err(e) => Some(e.into()),
+      _ => None,
+    });
+
+    if let Some(err) = init_err {
+      for client in self.inner.clients.iter() {
+        utils::reset_router_task(client.lock().await.inner());
+      }
+
+      Err(err)
+    } else {
+      Ok(connect_task)
+    }
+  }
+
+  /// Read the size of the pool.
+  pub fn size(&self) -> usize {
+    self.inner.clients.len()
+  }
+
+  /// Read the client that should run the next command.
+  pub async fn acquire(&self) -> OwnedMutexGuard<RedisClient> {
+    let mut idx = utils::incr_atomic(&self.inner.counter) % self.inner.clients.len();
+
+    for _ in 0 .. self.inner.clients.len() {
+      if let Ok(client) = self.inner.clients[idx].clone().try_lock_owned() {
+        return client;
+      }
+
+      idx = (idx + 1) % self.inner.clients.len();
+    }
+
+    self.inner.clients[idx].clone().lock_owned().await
+  }
+
+  /// Update the internal [PerformanceConfig](crate::types::PerformanceConfig) on each client in place with new
+  /// values.
+  pub async fn update_perf_config(&self, config: PerformanceConfig) {
+    for client in self.inner.clients.iter() {
+      client.lock().await.update_perf_config(config.clone());
+    }
+  }
+
+  /// Override the DNS resolution logic for all clients in the pool.
+  #[cfg(feature = "dns")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "dns")))]
+  #[allow(refining_impl_trait)]
+  pub async fn set_resolver(&self, resolver: RefCount<dyn Resolve>) {
+    for client in self.inner.clients.iter() {
+      client.lock().await.set_resolver(resolver.clone()).await;
+    }
+  }
+
+  /// Close the connection to the Redis server for each client. The returned future resolves when the command has been
+  /// written to the socket, not when the connection has been fully closed. Some time after this future resolves the
+  /// future returned by [connect](Self::connect) will resolve which indicates that the connection has been fully
+  /// closed.
+  ///
+  /// This function will also close all error, pubsub message, and reconnection event streams on all clients in the
+  /// pool.
+  pub async fn quit(&self) -> RedisResult<()> {
+    let mut fts = Vec::with_capacity(self.inner.clients.len());
+    for locked_client in self.inner.clients.iter() {
+      let client = locked_client.clone();
+      fts.push(async move { client.lock().await.quit().await });
+    }
+
+    join_all(fts).await;
+    Ok(())
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/clients/pubsub.rs.html b/doc/src/fred/clients/pubsub.rs.html new file mode 100644 index 00000000..ce1044b3 --- /dev/null +++ b/doc/src/fred/clients/pubsub.rs.html @@ -0,0 +1,851 @@ +pubsub.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::*,
+  modules::inner::RedisClientInner,
+  prelude::RedisClient,
+  runtime::{spawn, JoinHandle, RefCount, RwLock},
+  types::{ConnectionConfig, MultipleStrings, PerformanceConfig, ReconnectPolicy, RedisConfig, RedisKey},
+  util::group_by_hash_slot,
+};
+use bytes_utils::Str;
+use rm_send_macros::rm_send_if;
+use std::{collections::BTreeSet, fmt, fmt::Formatter, future::Future, mem};
+
+type ChannelSet = RefCount<RwLock<BTreeSet<Str>>>;
+
+/// A subscriber client that will manage subscription state to any [pubsub](https://redis.io/docs/manual/pubsub/) channels or patterns for the caller.
+///
+/// If the connection to the server closes for any reason this struct can automatically re-subscribe to channels,
+/// patterns, and sharded channels.
+///
+/// **Note: most non-pubsub commands are only supported when using RESP3.**
+///
+/// ```rust no_run
+/// use fred::clients::SubscriberClient;
+/// use fred::prelude::*;
+///
+/// async fn example() -> Result<(), RedisError> {
+///   let subscriber = Builder::default_centralized().build_subscriber_client()?;
+///   subscriber.init().await?;
+///
+///   // spawn a task that will re-subscribe to channels and patterns after reconnecting
+///   let _ = subscriber.manage_subscriptions();
+///
+///   let mut message_rx = subscriber.message_rx();
+///   let jh = tokio::spawn(async move {
+///     while let Ok(message) = message_rx.recv().await {
+///       println!("Recv message {:?} on channel {}", message.value, message.channel);
+///     }
+///   });
+///
+///   let _ = subscriber.subscribe("foo").await?;
+///   let _ = subscriber.psubscribe("bar*").await?;
+///   println!("Tracking channels: {:?}", subscriber.tracked_channels()); // foo
+///   println!("Tracking patterns: {:?}", subscriber.tracked_patterns()); // bar*
+///
+///   // force a re-subscription
+///   subscriber.resubscribe_all().await?;
+///   // clear all the local state and unsubscribe
+///   subscriber.unsubscribe_all().await?;
+///   subscriber.quit().await?;
+///   Ok(())
+/// }
+/// ```
+#[derive(Clone)]
+#[cfg_attr(docsrs, doc(cfg(feature = "subscriber-client")))]
+pub struct SubscriberClient {
+  channels:       ChannelSet,
+  patterns:       ChannelSet,
+  shard_channels: ChannelSet,
+  inner:          RefCount<RedisClientInner>,
+}
+
+impl fmt::Debug for SubscriberClient {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    f.debug_struct("SubscriberClient")
+      .field("id", &self.inner.id)
+      .field("channels", &self.tracked_channels())
+      .field("patterns", &self.tracked_patterns())
+      .field("shard_channels", &self.tracked_shard_channels())
+      .finish()
+  }
+}
+
+impl ClientLike for SubscriberClient {
+  #[doc(hidden)]
+  fn inner(&self) -> &RefCount<RedisClientInner> {
+    &self.inner
+  }
+}
+
+impl EventInterface for SubscriberClient {}
+#[cfg(feature = "i-acl")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))]
+impl AclInterface for SubscriberClient {}
+#[cfg(feature = "i-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))]
+impl ClientInterface for SubscriberClient {}
+#[cfg(feature = "i-cluster")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))]
+impl ClusterInterface for SubscriberClient {}
+#[cfg(feature = "i-config")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))]
+impl ConfigInterface for SubscriberClient {}
+#[cfg(feature = "i-geo")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))]
+impl GeoInterface for SubscriberClient {}
+#[cfg(feature = "i-hashes")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))]
+impl HashesInterface for SubscriberClient {}
+#[cfg(feature = "i-hyperloglog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))]
+impl HyperloglogInterface for SubscriberClient {}
+impl MetricsInterface for SubscriberClient {}
+#[cfg(feature = "transactions")]
+#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))]
+impl TransactionInterface for SubscriberClient {}
+#[cfg(feature = "i-keys")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))]
+impl KeysInterface for SubscriberClient {}
+#[cfg(feature = "i-scripts")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+impl LuaInterface for SubscriberClient {}
+#[cfg(feature = "i-lists")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))]
+impl ListInterface for SubscriberClient {}
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+impl MemoryInterface for SubscriberClient {}
+impl AuthInterface for SubscriberClient {}
+#[cfg(feature = "i-server")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+impl ServerInterface for SubscriberClient {}
+#[cfg(feature = "i-slowlog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))]
+impl SlowlogInterface for SubscriberClient {}
+#[cfg(feature = "i-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))]
+impl SetsInterface for SubscriberClient {}
+#[cfg(feature = "i-sorted-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))]
+impl SortedSetsInterface for SubscriberClient {}
+#[cfg(feature = "i-server")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+impl HeartbeatInterface for SubscriberClient {}
+#[cfg(feature = "i-streams")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))]
+impl StreamsInterface for SubscriberClient {}
+#[cfg(feature = "i-scripts")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+impl FunctionInterface for SubscriberClient {}
+#[cfg(feature = "i-redis-json")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))]
+impl RedisJsonInterface for SubscriberClient {}
+#[cfg(feature = "i-time-series")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+impl TimeSeriesInterface for SubscriberClient {}
+#[cfg(feature = "i-tracking")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+impl TrackingInterface for SubscriberClient {}
+#[cfg(feature = "i-redisearch")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))]
+impl RediSearchInterface for SubscriberClient {}
+
+#[cfg(feature = "i-pubsub")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))]
+#[rm_send_if(feature = "glommio")]
+impl PubsubInterface for SubscriberClient {
+  fn subscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<MultipleStrings> + Send,
+  {
+    into!(channels);
+
+    async move {
+      let result = commands::pubsub::subscribe(self, channels.clone()).await;
+      if result.is_ok() {
+        let mut guard = self.channels.write();
+
+        for channel in channels.inner().into_iter() {
+          if let Some(channel) = channel.as_bytes_str() {
+            guard.insert(channel);
+          }
+        }
+      }
+
+      result
+    }
+  }
+
+  fn unsubscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<MultipleStrings> + Send,
+  {
+    into!(channels);
+
+    async move {
+      let result = commands::pubsub::unsubscribe(self, channels.clone()).await;
+      if result.is_ok() {
+        let mut guard = self.channels.write();
+
+        if channels.len() == 0 {
+          guard.clear();
+        } else {
+          for channel in channels.inner().into_iter() {
+            if let Some(channel) = channel.as_bytes_str() {
+              let _ = guard.remove(&channel);
+            }
+          }
+        }
+      }
+      result
+    }
+  }
+
+  fn psubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<MultipleStrings> + Send,
+  {
+    into!(patterns);
+
+    async move {
+      let result = commands::pubsub::psubscribe(self, patterns.clone()).await;
+      if result.is_ok() {
+        let mut guard = self.patterns.write();
+
+        for pattern in patterns.inner().into_iter() {
+          if let Some(pattern) = pattern.as_bytes_str() {
+            guard.insert(pattern);
+          }
+        }
+      }
+      result
+    }
+  }
+
+  fn punsubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<MultipleStrings> + Send,
+  {
+    into!(patterns);
+
+    async move {
+      let result = commands::pubsub::punsubscribe(self, patterns.clone()).await;
+      if result.is_ok() {
+        let mut guard = self.patterns.write();
+
+        if patterns.len() == 0 {
+          guard.clear();
+        } else {
+          for pattern in patterns.inner().into_iter() {
+            if let Some(pattern) = pattern.as_bytes_str() {
+              let _ = guard.remove(&pattern);
+            }
+          }
+        }
+      }
+      result
+    }
+  }
+
+  fn ssubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    C: Into<MultipleStrings> + Send,
+  {
+    into!(channels);
+
+    async move {
+      let result = commands::pubsub::ssubscribe(self, channels.clone()).await;
+      if result.is_ok() {
+        let mut guard = self.shard_channels.write();
+
+        for channel in channels.inner().into_iter() {
+          if let Some(channel) = channel.as_bytes_str() {
+            guard.insert(channel);
+          }
+        }
+      }
+      result
+    }
+  }
+
+  fn sunsubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    C: Into<MultipleStrings> + Send,
+  {
+    into!(channels);
+
+    async move {
+      let result = commands::pubsub::sunsubscribe(self, channels.clone()).await;
+      if result.is_ok() {
+        let mut guard = self.shard_channels.write();
+
+        if channels.len() == 0 {
+          guard.clear();
+        } else {
+          for channel in channels.inner().into_iter() {
+            if let Some(channel) = channel.as_bytes_str() {
+              let _ = guard.remove(&channel);
+            }
+          }
+        }
+      }
+      result
+    }
+  }
+}
+
+impl SubscriberClient {
+  /// Create a new client instance without connecting to the server.
+  ///
+  /// See the [builder](crate::types::Builder) interface for more information.
+  pub fn new(
+    config: RedisConfig,
+    perf: Option<PerformanceConfig>,
+    connection: Option<ConnectionConfig>,
+    policy: Option<ReconnectPolicy>,
+  ) -> SubscriberClient {
+    SubscriberClient {
+      channels:       RefCount::new(RwLock::new(BTreeSet::new())),
+      patterns:       RefCount::new(RwLock::new(BTreeSet::new())),
+      shard_channels: RefCount::new(RwLock::new(BTreeSet::new())),
+      inner:          RedisClientInner::new(config, perf.unwrap_or_default(), connection.unwrap_or_default(), policy),
+    }
+  }
+
+  /// Create a new `SubscriberClient` from the config provided to this client.
+  ///
+  /// The returned client will not be connected to the server, and it will use new connections after connecting.
+  /// However, it will manage the same channel subscriptions as the original client.
+  pub fn clone_new(&self) -> Self {
+    let inner = RedisClientInner::new(
+      self.inner.config.as_ref().clone(),
+      self.inner.performance_config(),
+      self.inner.connection.as_ref().clone(),
+      self.inner.reconnect_policy(),
+    );
+
+    SubscriberClient {
+      inner,
+      channels: RefCount::new(RwLock::new(self.channels.read().clone())),
+      patterns: RefCount::new(RwLock::new(self.patterns.read().clone())),
+      shard_channels: RefCount::new(RwLock::new(self.shard_channels.read().clone())),
+    }
+  }
+
+  /// Spawn a task that will automatically re-subscribe to any channels or channel patterns used by the client.
+  pub fn manage_subscriptions(&self) -> JoinHandle<()> {
+    let _self = self.clone();
+    spawn(async move {
+      #[allow(unused_mut)]
+      let mut stream = _self.reconnect_rx();
+
+      while let Ok(_) = stream.recv().await {
+        if let Err(error) = _self.resubscribe_all().await {
+          error!(
+            "{}: Failed to resubscribe to channels or patterns: {:?}",
+            _self.id(),
+            error
+          );
+        }
+      }
+    })
+  }
+
+  /// Read the set of channels that this client will manage.
+  pub fn tracked_channels(&self) -> BTreeSet<Str> {
+    self.channels.read().clone()
+  }
+
+  /// Read the set of channel patterns that this client will manage.
+  pub fn tracked_patterns(&self) -> BTreeSet<Str> {
+    self.patterns.read().clone()
+  }
+
+  /// Read the set of shard channels that this client will manage.
+  pub fn tracked_shard_channels(&self) -> BTreeSet<Str> {
+    self.shard_channels.read().clone()
+  }
+
+  /// Re-subscribe to any tracked channels and patterns.
+  ///
+  /// This can be used to sync the client's subscriptions with the server after calling `QUIT`, then `connect`, etc.
+  pub async fn resubscribe_all(&self) -> Result<(), RedisError> {
+    let channels: Vec<RedisKey> = self.tracked_channels().into_iter().map(|s| s.into()).collect();
+    let patterns: Vec<RedisKey> = self.tracked_patterns().into_iter().map(|s| s.into()).collect();
+    let shard_channels: Vec<RedisKey> = self.tracked_shard_channels().into_iter().map(|s| s.into()).collect();
+
+    self.subscribe(channels).await?;
+    self.psubscribe(patterns).await?;
+
+    let shard_channel_groups = group_by_hash_slot(shard_channels)?;
+    for (_, keys) in shard_channel_groups.into_iter() {
+      // the client never pipelines this so no point in using join! or a pipeline here
+      self.ssubscribe(keys).await?;
+    }
+
+    Ok(())
+  }
+
+  /// Unsubscribe from all tracked channels and patterns, and remove them from the client cache.
+  pub async fn unsubscribe_all(&self) -> Result<(), RedisError> {
+    let channels: Vec<RedisKey> = mem::take(&mut *self.channels.write())
+      .into_iter()
+      .map(|s| s.into())
+      .collect();
+    let patterns: Vec<RedisKey> = mem::take(&mut *self.patterns.write())
+      .into_iter()
+      .map(|s| s.into())
+      .collect();
+    let shard_channels: Vec<RedisKey> = mem::take(&mut *self.shard_channels.write())
+      .into_iter()
+      .map(|s| s.into())
+      .collect();
+
+    self.unsubscribe(channels).await?;
+    self.punsubscribe(patterns).await?;
+
+    let shard_channel_groups = group_by_hash_slot(shard_channels)?;
+    let shard_subscriptions: Vec<_> = shard_channel_groups
+      .into_iter()
+      .map(|(_, keys)| async { self.sunsubscribe(keys).await })
+      .collect();
+
+    futures::future::try_join_all(shard_subscriptions).await?;
+    Ok(())
+  }
+
+  /// Create a new `RedisClient`, reusing the existing connection(s).
+  ///
+  /// Note: most non-pubsub commands are only supported when using RESP3.
+  pub fn to_client(&self) -> RedisClient {
+    RedisClient::from(&self.inner)
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/clients/redis.rs.html b/doc/src/fred/clients/redis.rs.html new file mode 100644 index 00000000..9c2f1826 --- /dev/null +++ b/doc/src/fred/clients/redis.rs.html @@ -0,0 +1,733 @@ +redis.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+
use crate::{
+  clients::{Pipeline, WithOptions},
+  commands,
+  error::{RedisError, RedisErrorKind},
+  interfaces::*,
+  modules::inner::RedisClientInner,
+  prelude::ClientLike,
+  runtime::RefCount,
+  types::*,
+};
+use bytes_utils::Str;
+use futures::Stream;
+use std::{fmt, fmt::Formatter};
+
+#[cfg(feature = "replicas")]
+use crate::clients::Replicas;
+#[cfg(feature = "i-tracking")]
+use crate::interfaces::TrackingInterface;
+
+/// A cheaply cloneable Redis client struct.
+#[derive(Clone)]
+pub struct RedisClient {
+  pub(crate) inner: RefCount<RedisClientInner>,
+}
+
+impl Default for RedisClient {
+  fn default() -> Self {
+    RedisClient::new(RedisConfig::default(), None, None, None)
+  }
+}
+
+impl fmt::Debug for RedisClient {
+  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    f.debug_struct("RedisClient")
+      .field("id", &self.inner.id)
+      .field("state", &self.state())
+      .finish()
+  }
+}
+
+impl fmt::Display for RedisClient {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    write!(f, "{}", self.inner.id)
+  }
+}
+
+#[doc(hidden)]
+impl<'a> From<&'a RefCount<RedisClientInner>> for RedisClient {
+  fn from(inner: &'a RefCount<RedisClientInner>) -> RedisClient {
+    RedisClient { inner: inner.clone() }
+  }
+}
+
+impl ClientLike for RedisClient {
+  #[doc(hidden)]
+  fn inner(&self) -> &RefCount<RedisClientInner> {
+    &self.inner
+  }
+}
+
+impl EventInterface for RedisClient {}
+#[cfg(feature = "i-redis-json")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))]
+impl RedisJsonInterface for RedisClient {}
+#[cfg(feature = "i-time-series")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+impl TimeSeriesInterface for RedisClient {}
+#[cfg(feature = "i-acl")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))]
+impl AclInterface for RedisClient {}
+#[cfg(feature = "i-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))]
+impl ClientInterface for RedisClient {}
+#[cfg(feature = "i-cluster")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))]
+impl ClusterInterface for RedisClient {}
+#[cfg(feature = "i-config")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))]
+impl ConfigInterface for RedisClient {}
+#[cfg(feature = "i-geo")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))]
+impl GeoInterface for RedisClient {}
+#[cfg(feature = "i-hashes")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))]
+impl HashesInterface for RedisClient {}
+#[cfg(feature = "i-hyperloglog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))]
+impl HyperloglogInterface for RedisClient {}
+impl MetricsInterface for RedisClient {}
+#[cfg(feature = "transactions")]
+#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))]
+impl TransactionInterface for RedisClient {}
+#[cfg(feature = "i-keys")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))]
+impl KeysInterface for RedisClient {}
+#[cfg(feature = "i-scripts")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+impl LuaInterface for RedisClient {}
+#[cfg(feature = "i-lists")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))]
+impl ListInterface for RedisClient {}
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+impl MemoryInterface for RedisClient {}
+impl AuthInterface for RedisClient {}
+#[cfg(feature = "i-server")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+impl ServerInterface for RedisClient {}
+#[cfg(feature = "i-slowlog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))]
+impl SlowlogInterface for RedisClient {}
+#[cfg(feature = "i-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))]
+impl SetsInterface for RedisClient {}
+#[cfg(feature = "i-sorted-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))]
+impl SortedSetsInterface for RedisClient {}
+#[cfg(feature = "i-server")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+impl HeartbeatInterface for RedisClient {}
+#[cfg(feature = "i-streams")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))]
+impl StreamsInterface for RedisClient {}
+#[cfg(feature = "i-scripts")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+impl FunctionInterface for RedisClient {}
+#[cfg(feature = "i-tracking")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+impl TrackingInterface for RedisClient {}
+#[cfg(feature = "i-pubsub")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))]
+impl PubsubInterface for RedisClient {}
+#[cfg(feature = "i-redisearch")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))]
+impl RediSearchInterface for RedisClient {}
+
+impl RedisClient {
+  /// Create a new client instance without connecting to the server.
+  ///
+  /// See the [builder](crate::types::Builder) interface for more information.
+  pub fn new(
+    config: RedisConfig,
+    perf: Option<PerformanceConfig>,
+    connection: Option<ConnectionConfig>,
+    policy: Option<ReconnectPolicy>,
+  ) -> RedisClient {
+    RedisClient {
+      inner: RedisClientInner::new(config, perf.unwrap_or_default(), connection.unwrap_or_default(), policy),
+    }
+  }
+
+  /// Create a new `RedisClient` from the config provided to this client.
+  ///
+  /// The returned client will **not** be connected to the server.
+  pub fn clone_new(&self) -> Self {
+    let mut policy = self.inner.policy.read().clone();
+    if let Some(policy) = policy.as_mut() {
+      policy.reset_attempts();
+    }
+
+    RedisClient::new(
+      self.inner.config.as_ref().clone(),
+      Some(self.inner.performance_config()),
+      Some(self.inner.connection_config()),
+      policy,
+    )
+  }
+
+  /// Split a clustered Redis client into a set of centralized clients - one for each primary node in the cluster.
+  ///
+  /// Alternatively, callers can use [with_cluster_node](crate::clients::RedisClient::with_cluster_node) to avoid
+  /// creating new connections.
+  ///
+  /// The clients returned by this function will not be connected to their associated servers. The caller needs to
+  /// call `connect` on each client before sending any commands.
+  pub fn split_cluster(&self) -> Result<Vec<RedisClient>, RedisError> {
+    if self.inner.config.server.is_clustered() {
+      commands::server::split(&self.inner)
+    } else {
+      Err(RedisError::new(
+        RedisErrorKind::Unknown,
+        "Client is not using a clustered deployment.",
+      ))
+    }
+  }
+
+  // --------------- SCANNING ---------------
+
+  /// Incrementally iterate over a set of keys matching the `pattern` argument, returning `count` results per page, if
+  /// specified.
+  ///
+  /// The scan operation can be canceled by dropping the returned stream.
+  ///
+  /// <https://redis.io/commands/scan>
+  pub fn scan<P>(
+    &self,
+    pattern: P,
+    count: Option<u32>,
+    r#type: Option<ScanType>,
+  ) -> impl Stream<Item = Result<ScanResult, RedisError>>
+  where
+    P: Into<Str>,
+  {
+    commands::scan::scan(&self.inner, pattern.into(), count, r#type, None)
+  }
+
+  /// Run the `SCAN` command on each primary/main node in a cluster concurrently.
+  ///
+  /// In order for this function to work reliably the cluster state must not change while scanning. If nodes are added
+  /// or removed, or hash slots are rebalanced, it may result in missing keys or duplicate keys in the result
+  /// stream. See [split_cluster](Self::split_cluster) for use cases that require scanning to work while the cluster
+  /// state changes.
+  ///
+  /// Unlike `SCAN`, `HSCAN`, etc, the returned stream may continue even if
+  /// [has_more](crate::types::ScanResult::has_more) returns false on a given page of keys.
+  pub fn scan_cluster<P>(
+    &self,
+    pattern: P,
+    count: Option<u32>,
+    r#type: Option<ScanType>,
+  ) -> impl Stream<Item = Result<ScanResult, RedisError>>
+  where
+    P: Into<Str>,
+  {
+    commands::scan::scan_cluster(&self.inner, pattern.into(), count, r#type)
+  }
+
+  /// Incrementally iterate over pages of the hash map stored at `key`, returning `count` results per page, if
+  /// specified.
+  ///
+  /// <https://redis.io/commands/hscan>
+  pub fn hscan<K, P>(
+    &self,
+    key: K,
+    pattern: P,
+    count: Option<u32>,
+  ) -> impl Stream<Item = Result<HScanResult, RedisError>>
+  where
+    K: Into<RedisKey>,
+    P: Into<Str>,
+  {
+    commands::scan::hscan(&self.inner, key.into(), pattern.into(), count)
+  }
+
+  /// Incrementally iterate over pages of the set stored at `key`, returning `count` results per page, if specified.
+  ///
+  /// <https://redis.io/commands/sscan>
+  pub fn sscan<K, P>(
+    &self,
+    key: K,
+    pattern: P,
+    count: Option<u32>,
+  ) -> impl Stream<Item = Result<SScanResult, RedisError>>
+  where
+    K: Into<RedisKey>,
+    P: Into<Str>,
+  {
+    commands::scan::sscan(&self.inner, key.into(), pattern.into(), count)
+  }
+
+  /// Incrementally iterate over pages of the sorted set stored at `key`, returning `count` results per page, if
+  /// specified.
+  ///
+  /// <https://redis.io/commands/zscan>
+  pub fn zscan<K, P>(
+    &self,
+    key: K,
+    pattern: P,
+    count: Option<u32>,
+  ) -> impl Stream<Item = Result<ZScanResult, RedisError>>
+  where
+    K: Into<RedisKey>,
+    P: Into<Str>,
+  {
+    commands::scan::zscan(&self.inner, key.into(), pattern.into(), count)
+  }
+
+  /// Send a series of commands in a [pipeline](https://redis.io/docs/manual/pipelining/).
+  pub fn pipeline(&self) -> Pipeline<RedisClient> {
+    Pipeline::from(self.clone())
+  }
+
+  /// Shorthand to route subsequent commands to the provided server.
+  ///
+  /// See [with_options](crate::interfaces::ClientLike::with_options) for more information.
+  ///
+  /// ```rust
+  /// # use fred::prelude::*;
+  /// async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  ///   // discover servers via the `RedisConfig` or active connections
+  ///   let connections = client.active_connections().await?;
+  ///
+  ///   // ping each node in the cluster individually
+  ///   for server in connections.into_iter() {
+  ///     let _: () = client.with_cluster_node(server).ping().await?;
+  ///   }
+  ///
+  ///   // or use the cached cluster routing table to discover servers
+  ///   let servers = client
+  ///     .cached_cluster_state()
+  ///     .expect("Failed to read cached cluster state")
+  ///     .unique_primary_nodes();
+  ///
+  ///   for server in servers.into_iter() {
+  ///     // verify the server address with `CLIENT INFO`
+  ///     let server_addr = client
+  ///       .with_cluster_node(&server)
+  ///       .client_info::<String>()
+  ///       .await?
+  ///       .split(" ")
+  ///       .find_map(|s| {
+  ///         let parts: Vec<&str> = s.split("=").collect();
+  ///         if parts[0] == "laddr" {
+  ///           Some(parts[1].to_owned())
+  ///         } else {
+  ///           None
+  ///         }
+  ///       })
+  ///       .expect("Failed to read or parse client info.");
+  ///
+  ///     assert_eq!(server_addr, server.to_string());
+  ///   }
+  ///
+  ///   Ok(())
+  /// }
+  /// ```
+  pub fn with_cluster_node<S>(&self, server: S) -> WithOptions<Self>
+  where
+    S: Into<Server>,
+  {
+    WithOptions {
+      client:  self.clone(),
+      options: Options {
+        cluster_node: Some(server.into()),
+        ..Default::default()
+      },
+    }
+  }
+
+  /// Create a client that interacts with replica nodes.
+  #[cfg(feature = "replicas")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+  pub fn replicas(&self) -> Replicas {
+    Replicas::from(&self.inner)
+  }
+}
+
+#[cfg(test)]
+mod tests {
+  #[cfg(feature = "sha-1")]
+  use crate::util;
+
+  #[test]
+  #[cfg(feature = "sha-1")]
+  fn should_correctly_sha1_hash() {
+    assert_eq!(
+      &util::sha1_hash("foobarbaz"),
+      "5f5513f8822fdbe5145af33b64d8d970dcf95c6e"
+    );
+    assert_eq!(&util::sha1_hash("abc123"), "6367c48dd193d56ea7b0baad25b19455e529f5ee");
+    assert_eq!(
+      &util::sha1_hash("jakdjfkldajfklej8a4tjkaldsnvkl43kjakljdvk42"),
+      "45c118f5de7c3fd3a4022135dc6acfb526f3c225"
+    );
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/clients/replica.rs.html b/doc/src/fred/clients/replica.rs.html new file mode 100644 index 00000000..8131802a --- /dev/null +++ b/doc/src/fred/clients/replica.rs.html @@ -0,0 +1,265 @@ +replica.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+
use crate::{
+  clients::{Pipeline, RedisClient},
+  error::RedisError,
+  interfaces::{self, *},
+  modules::inner::RedisClientInner,
+  protocol::command::{RedisCommand, RouterCommand},
+  runtime::{oneshot_channel, RefCount},
+  types::Server,
+};
+use std::{collections::HashMap, fmt, fmt::Formatter};
+
+/// A struct for interacting with cluster replica nodes.
+///
+/// All commands sent via this interface will use a replica node, if possible. The underlying connections are shared
+/// with the main client in order to maintain an up-to-date view of the system in the event that replicas change or
+/// are promoted. The cached replica routing table will be updated on the client when following cluster redirections
+/// or when any connection closes.
+///
+/// [Redis replication is asynchronous](https://redis.io/docs/management/replication/).
+#[derive(Clone)]
+#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+pub struct Replicas {
+  inner: RefCount<RedisClientInner>,
+}
+
+impl fmt::Debug for Replicas {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    f.debug_struct("Replicas").field("id", &self.inner.id).finish()
+  }
+}
+
+#[doc(hidden)]
+impl From<&RefCount<RedisClientInner>> for Replicas {
+  fn from(inner: &RefCount<RedisClientInner>) -> Self {
+    Replicas { inner: inner.clone() }
+  }
+}
+
+impl ClientLike for Replicas {
+  #[doc(hidden)]
+  fn inner(&self) -> &RefCount<RedisClientInner> {
+    &self.inner
+  }
+
+  #[doc(hidden)]
+  fn change_command(&self, command: &mut RedisCommand) {
+    command.use_replica = true;
+  }
+}
+
+#[cfg(feature = "i-redis-json")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))]
+impl RedisJsonInterface for Replicas {}
+#[cfg(feature = "i-time-series")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+impl TimeSeriesInterface for Replicas {}
+#[cfg(feature = "i-cluster")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))]
+impl ClusterInterface for Replicas {}
+#[cfg(feature = "i-config")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))]
+impl ConfigInterface for Replicas {}
+#[cfg(feature = "i-geo")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))]
+impl GeoInterface for Replicas {}
+#[cfg(feature = "i-hashes")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))]
+impl HashesInterface for Replicas {}
+#[cfg(feature = "i-hyperloglog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))]
+impl HyperloglogInterface for Replicas {}
+#[cfg(feature = "i-keys")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))]
+impl KeysInterface for Replicas {}
+#[cfg(feature = "i-scripts")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+impl LuaInterface for Replicas {}
+#[cfg(feature = "i-lists")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))]
+impl ListInterface for Replicas {}
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+impl MemoryInterface for Replicas {}
+#[cfg(feature = "i-server")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+impl ServerInterface for Replicas {}
+#[cfg(feature = "i-slowlog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))]
+impl SlowlogInterface for Replicas {}
+#[cfg(feature = "i-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))]
+impl SetsInterface for Replicas {}
+#[cfg(feature = "i-sorted-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))]
+impl SortedSetsInterface for Replicas {}
+#[cfg(feature = "i-streams")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))]
+impl StreamsInterface for Replicas {}
+#[cfg(feature = "i-scripts")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+impl FunctionInterface for Replicas {}
+#[cfg(feature = "i-redisearch")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))]
+impl RediSearchInterface for Replicas {}
+
+impl Replicas {
+  /// Read a mapping of replica server IDs to primary server IDs.
+  pub fn nodes(&self) -> HashMap<Server, Server> {
+    self.inner.server_state.read().replicas.clone()
+  }
+
+  /// Send a series of commands in a [pipeline](https://redis.io/docs/manual/pipelining/).
+  pub fn pipeline(&self) -> Pipeline<Replicas> {
+    Pipeline::from(self.clone())
+  }
+
+  /// Read the underlying [RedisClient](crate::clients::RedisClient) that interacts with primary nodes.
+  pub fn client(&self) -> RedisClient {
+    RedisClient::from(&self.inner)
+  }
+
+  /// Sync the cached replica routing table with the server(s).
+  ///
+  /// If `reset: true` the client will forcefully disconnect from replicas even if the connections could otherwise be
+  /// reused.
+  pub async fn sync(&self, reset: bool) -> Result<(), RedisError> {
+    let (tx, rx) = oneshot_channel();
+    let cmd = RouterCommand::SyncReplicas { tx, reset };
+    interfaces::send_to_router(&self.inner, cmd)?;
+    rx.await?
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/clients/sentinel.rs.html b/doc/src/fred/clients/sentinel.rs.html new file mode 100644 index 00000000..ebe32571 --- /dev/null +++ b/doc/src/fred/clients/sentinel.rs.html @@ -0,0 +1,165 @@ +sentinel.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+
use crate::{
+  interfaces::*,
+  modules::inner::RedisClientInner,
+  runtime::RefCount,
+  types::{ConnectionConfig, PerformanceConfig, ReconnectPolicy, SentinelConfig},
+};
+use std::fmt;
+
+/// A struct for interacting directly with Sentinel nodes.
+///
+/// This struct **will not** communicate with Redis servers behind the sentinel interface, but rather with the
+/// sentinel nodes themselves. Callers should use the [RedisClient](crate::clients::RedisClient) interface with a
+/// [ServerConfig::Sentinel](crate::types::ServerConfig::Sentinel) for interacting with Redis services behind a
+/// sentinel layer.
+///
+/// See the [sentinel API docs](https://redis.io/topics/sentinel#sentinel-api) for more information.
+#[derive(Clone)]
+#[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))]
+pub struct SentinelClient {
+  inner: RefCount<RedisClientInner>,
+}
+
+impl ClientLike for SentinelClient {
+  #[doc(hidden)]
+  fn inner(&self) -> &RefCount<RedisClientInner> {
+    &self.inner
+  }
+}
+
+impl fmt::Debug for SentinelClient {
+  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    f.debug_struct("SentinelClient")
+      .field("id", &self.inner.id)
+      .field("state", &self.state())
+      .finish()
+  }
+}
+
+#[doc(hidden)]
+impl<'a> From<&'a RefCount<RedisClientInner>> for SentinelClient {
+  fn from(inner: &'a RefCount<RedisClientInner>) -> Self {
+    SentinelClient { inner: inner.clone() }
+  }
+}
+
+impl EventInterface for SentinelClient {}
+impl SentinelInterface for SentinelClient {}
+impl MetricsInterface for SentinelClient {}
+#[cfg(feature = "i-acl")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))]
+impl AclInterface for SentinelClient {}
+#[cfg(feature = "i-pubsub")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))]
+impl PubsubInterface for SentinelClient {}
+#[cfg(feature = "i-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))]
+impl ClientInterface for SentinelClient {}
+impl AuthInterface for SentinelClient {}
+#[cfg(feature = "i-server")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+impl HeartbeatInterface for SentinelClient {}
+
+impl SentinelClient {
+  /// Create a new client instance without connecting to the sentinel node.
+  ///
+  /// See the [builder](crate::types::Builder) interface for more information.
+  pub fn new(
+    config: SentinelConfig,
+    perf: Option<PerformanceConfig>,
+    connection: Option<ConnectionConfig>,
+    policy: Option<ReconnectPolicy>,
+  ) -> SentinelClient {
+    SentinelClient {
+      inner: RedisClientInner::new(
+        config.into(),
+        perf.unwrap_or_default(),
+        connection.unwrap_or_default(),
+        policy,
+      ),
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/clients/transaction.rs.html b/doc/src/fred/clients/transaction.rs.html new file mode 100644 index 00000000..afa66ed4 --- /dev/null +++ b/doc/src/fred/clients/transaction.rs.html @@ -0,0 +1,695 @@ +transaction.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  interfaces,
+  interfaces::*,
+  modules::inner::RedisClientInner,
+  prelude::RedisValue,
+  protocol::{
+    command::{RedisCommand, RedisCommandKind, RouterCommand},
+    hashers::ClusterHash,
+    responders::ResponseKind,
+    utils as protocol_utils,
+  },
+  runtime::{oneshot_channel, Mutex, RefCount},
+  types::{FromRedis, MultipleKeys, Options, RedisKey, Server},
+  utils,
+};
+use std::{collections::VecDeque, fmt};
+
+/// A cheaply cloneable transaction block.
+#[derive(Clone)]
+#[cfg(feature = "transactions")]
+#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))]
+pub struct Transaction {
+  id:        u64,
+  inner:     RefCount<RedisClientInner>,
+  commands:  RefCount<Mutex<VecDeque<RedisCommand>>>,
+  watched:   RefCount<Mutex<VecDeque<RedisKey>>>,
+  hash_slot: RefCount<Mutex<Option<u16>>>,
+}
+
+impl fmt::Debug for Transaction {
+  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    f.debug_struct("Transaction")
+      .field("client", &self.inner.id)
+      .field("id", &self.id)
+      .field("length", &self.commands.lock().len())
+      .field("hash_slot", &self.hash_slot.lock())
+      .finish()
+  }
+}
+
+impl PartialEq for Transaction {
+  fn eq(&self, other: &Self) -> bool {
+    self.id == other.id
+  }
+}
+
+impl Eq for Transaction {}
+
+impl ClientLike for Transaction {
+  #[doc(hidden)]
+  fn inner(&self) -> &RefCount<RedisClientInner> {
+    &self.inner
+  }
+
+  #[doc(hidden)]
+  fn send_command<C>(&self, command: C) -> Result<(), RedisError>
+  where
+    C: Into<RedisCommand>,
+  {
+    let mut command: RedisCommand = command.into();
+    self.disallow_all_cluster_commands(&command)?;
+    // check cluster slot mappings as commands are added
+    self.update_hash_slot(&command)?;
+
+    #[allow(unused_mut)]
+    if let Some(mut tx) = command.take_responder() {
+      trace!(
+        "{}: Respond early to {} command in transaction.",
+        &self.inner.id,
+        command.kind.to_str_debug()
+      );
+      let _ = tx.send(Ok(protocol_utils::queued_frame()));
+    }
+
+    self.commands.lock().push_back(command);
+    Ok(())
+  }
+}
+
+#[cfg(feature = "i-acl")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))]
+impl AclInterface for Transaction {}
+#[cfg(feature = "i-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))]
+impl ClientInterface for Transaction {}
+#[cfg(feature = "i-pubsub")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))]
+impl PubsubInterface for Transaction {}
+#[cfg(feature = "i-config")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))]
+impl ConfigInterface for Transaction {}
+#[cfg(feature = "i-geo")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))]
+impl GeoInterface for Transaction {}
+#[cfg(feature = "i-hashes")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))]
+impl HashesInterface for Transaction {}
+#[cfg(feature = "i-hyperloglog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))]
+impl HyperloglogInterface for Transaction {}
+#[cfg(feature = "i-keys")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))]
+impl KeysInterface for Transaction {}
+#[cfg(feature = "i-lists")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))]
+impl ListInterface for Transaction {}
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+impl MemoryInterface for Transaction {}
+impl AuthInterface for Transaction {}
+#[cfg(feature = "i-server")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+impl ServerInterface for Transaction {}
+#[cfg(feature = "i-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))]
+impl SetsInterface for Transaction {}
+#[cfg(feature = "i-sorted-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))]
+impl SortedSetsInterface for Transaction {}
+#[cfg(feature = "i-streams")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))]
+impl StreamsInterface for Transaction {}
+#[cfg(feature = "i-scripts")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+impl FunctionInterface for Transaction {}
+#[cfg(feature = "i-redis-json")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))]
+impl RedisJsonInterface for Transaction {}
+#[cfg(feature = "i-time-series")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+impl TimeSeriesInterface for Transaction {}
+#[cfg(feature = "i-redisearch")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))]
+impl RediSearchInterface for Transaction {}
+
+impl Transaction {
+  /// Create a new transaction.
+  pub(crate) fn from_inner(inner: &RefCount<RedisClientInner>) -> Self {
+    Transaction {
+      inner:     inner.clone(),
+      commands:  RefCount::new(Mutex::new(VecDeque::new())),
+      watched:   RefCount::new(Mutex::new(VecDeque::new())),
+      hash_slot: RefCount::new(Mutex::new(None)),
+      id:        utils::random_u64(u64::MAX),
+    }
+  }
+
+  /// Check and update the hash slot for the transaction.
+  pub(crate) fn update_hash_slot(&self, command: &RedisCommand) -> Result<(), RedisError> {
+    if !self.inner.config.server.is_clustered() {
+      return Ok(());
+    }
+
+    if let Some(slot) = command.cluster_hash() {
+      if let Some(old_slot) = utils::read_mutex(&self.hash_slot) {
+        let (old_server, server) = self.inner.with_cluster_state(|state| {
+          debug!(
+            "{}: Checking transaction hash slots: {}, {}",
+            &self.inner.id, old_slot, slot
+          );
+
+          Ok((state.get_server(old_slot).cloned(), state.get_server(slot).cloned()))
+        })?;
+
+        if old_server != server {
+          return Err(RedisError::new(
+            RedisErrorKind::Cluster,
+            "All transaction commands must use the same cluster node.",
+          ));
+        }
+      } else {
+        utils::set_mutex(&self.hash_slot, Some(slot));
+      }
+    }
+
+    Ok(())
+  }
+
+  pub(crate) fn disallow_all_cluster_commands(&self, command: &RedisCommand) -> Result<(), RedisError> {
+    if command.is_all_cluster_nodes() {
+      Err(RedisError::new(
+        RedisErrorKind::Cluster,
+        "Cannot use concurrent cluster commands inside a transaction.",
+      ))
+    } else {
+      Ok(())
+    }
+  }
+
+  /// An ID identifying the underlying transaction state.
+  pub fn id(&self) -> u64 {
+    self.id
+  }
+
+  /// Clear the internal command buffer and watched keys.
+  pub fn reset(&self) {
+    self.commands.lock().clear();
+    self.watched.lock().clear();
+    self.hash_slot.lock().take();
+  }
+
+  /// Read the number of commands queued to run.
+  pub fn len(&self) -> usize {
+    self.commands.lock().len()
+  }
+
+  /// Read the number of keys to `WATCH` before the starting the transaction.
+  pub fn watched_len(&self) -> usize {
+    self.watched.lock().len()
+  }
+
+  /// Executes all previously queued commands in a transaction.
+  ///
+  /// If `abort_on_error` is `true` the client will automatically send `DISCARD` if an error is received from
+  /// any of the commands prior to `EXEC`. This does **not** apply to `MOVED` or `ASK` errors, which wll be followed
+  /// automatically.
+  ///
+  /// <https://redis.io/commands/exec>
+  ///
+  /// ```rust no_run
+  /// # use fred::prelude::*;
+  ///
+  /// async fn example(client: &RedisClient) -> Result<(), RedisError> {
+  ///   let _ = client.mset(vec![("foo", 1), ("bar", 2)]).await?;
+  ///
+  ///   let trx = client.multi();
+  ///   let _: () = trx.get("foo").await?; // returns QUEUED
+  ///   let _: () = trx.get("bar").await?; // returns QUEUED
+  ///
+  ///   let (foo, bar): (i64, i64) = trx.exec(false).await?;
+  ///   assert_eq!((foo, bar), (1, 2));
+  ///   Ok(())
+  /// }
+  /// ```
+  pub async fn exec<R>(&self, abort_on_error: bool) -> Result<R, RedisError>
+  where
+    R: FromRedis,
+  {
+    let commands = {
+      self
+        .commands
+        .lock()
+        .iter()
+        .map(|cmd| cmd.duplicate(ResponseKind::Skip))
+        .collect()
+    };
+    let watched = { self.watched.lock().iter().cloned().collect() };
+    let hash_slot = utils::read_mutex(&self.hash_slot);
+    exec(&self.inner, commands, watched, hash_slot, abort_on_error, self.id)
+      .await?
+      .convert()
+  }
+
+  /// Send the `WATCH` command with the provided keys before starting the transaction.
+  pub fn watch_before<K>(&self, keys: K)
+  where
+    K: Into<MultipleKeys>,
+  {
+    self.watched.lock().extend(keys.into().inner());
+  }
+
+  /// Read the hash slot against which this transaction will run, if known.  
+  pub fn hash_slot(&self) -> Option<u16> {
+    utils::read_mutex(&self.hash_slot)
+  }
+
+  /// Read the server ID against which this transaction will run, if known.
+  pub fn cluster_node(&self) -> Option<Server> {
+    utils::read_mutex(&self.hash_slot).and_then(|slot| {
+      self
+        .inner
+        .with_cluster_state(|state| Ok(state.get_server(slot).cloned()))
+        .ok()
+        .and_then(|server| server)
+    })
+  }
+}
+
+async fn exec(
+  inner: &RefCount<RedisClientInner>,
+  commands: VecDeque<RedisCommand>,
+  watched: VecDeque<RedisKey>,
+  hash_slot: Option<u16>,
+  abort_on_error: bool,
+  id: u64,
+) -> Result<RedisValue, RedisError> {
+  if commands.is_empty() {
+    return Ok(RedisValue::Null);
+  }
+  let (tx, rx) = oneshot_channel();
+  let trx_options = Options::from_command(&commands[0]);
+
+  let mut multi = RedisCommand::new(RedisCommandKind::Multi, vec![]);
+  trx_options.apply(&mut multi);
+
+  let commands: Vec<RedisCommand> = [multi]
+    .into_iter()
+    .chain(commands.into_iter())
+    .map(|mut command| {
+      command.inherit_options(inner);
+      command.response = ResponseKind::Skip;
+      command.can_pipeline = false;
+      command.skip_backpressure = true;
+      command.transaction_id = Some(id);
+      command.use_replica = false;
+      if let Some(hash_slot) = hash_slot.as_ref() {
+        command.hasher = ClusterHash::Custom(*hash_slot);
+      }
+      command
+    })
+    .collect();
+  // collapse the watched keys into one command
+  let watched = if watched.is_empty() {
+    None
+  } else {
+    let args: Vec<RedisValue> = watched.into_iter().map(|k| k.into()).collect();
+    let mut watch_cmd = RedisCommand::new(RedisCommandKind::Watch, args);
+    watch_cmd.can_pipeline = false;
+    watch_cmd.skip_backpressure = true;
+    watch_cmd.transaction_id = Some(id);
+    if let Some(hash_slot) = hash_slot.as_ref() {
+      watch_cmd.hasher = ClusterHash::Custom(*hash_slot);
+    }
+    Some(watch_cmd)
+  };
+
+  _trace!(
+    inner,
+    "Sending transaction {} with {} commands ({} watched) to router.",
+    id,
+    commands.len(),
+    watched.as_ref().map(|c| c.args().len()).unwrap_or(0)
+  );
+  let command = RouterCommand::Transaction {
+    id,
+    tx,
+    commands,
+    watched,
+    abort_on_error,
+  };
+  let timeout_dur = trx_options.timeout.unwrap_or_else(|| inner.default_command_timeout());
+
+  interfaces::send_to_router(inner, command)?;
+  let frame = utils::timeout(rx, timeout_dur).await??;
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/acl.rs.html b/doc/src/fred/commands/impls/acl.rs.html new file mode 100644 index 00000000..c2507389 --- /dev/null +++ b/doc/src/fred/commands/impls/acl.rs.html @@ -0,0 +1,167 @@ +acl.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+
use super::*;
+use crate::{
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  types::*,
+  utils,
+};
+use bytes_utils::Str;
+
+ok_cmd!(acl_load, AclLoad);
+ok_cmd!(acl_save, AclSave);
+values_cmd!(acl_list, AclList);
+values_cmd!(acl_users, AclUsers);
+value_cmd!(acl_whoami, AclWhoAmI);
+
+pub async fn acl_setuser<C: ClientLike>(client: &C, username: Str, rules: MultipleValues) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, move || {
+    let rules = rules.into_multiple_values();
+    let mut args = Vec::with_capacity(rules.len() + 1);
+    args.push(username.into());
+
+    for rule in rules.into_iter() {
+      args.push(rule);
+    }
+    Ok((RedisCommandKind::AclSetUser, args))
+  })
+  .await?;
+
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+pub async fn acl_getuser<C: ClientLike>(client: &C, username: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::AclGetUser, vec![username.into()]))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn acl_deluser<C: ClientLike>(client: &C, usernames: MultipleKeys) -> Result<RedisValue, RedisError> {
+  let args: Vec<RedisValue> = usernames.inner().into_iter().map(|k| k.into()).collect();
+  let frame = utils::request_response(client, move || Ok((RedisCommandKind::AclDelUser, args))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn acl_cat<C: ClientLike>(client: &C, category: Option<Str>) -> Result<RedisValue, RedisError> {
+  let args: Vec<RedisValue> = if let Some(cat) = category {
+    vec![cat.into()]
+  } else {
+    Vec::new()
+  };
+
+  let frame = utils::request_response(client, move || Ok((RedisCommandKind::AclCat, args))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn acl_genpass<C: ClientLike>(client: &C, bits: Option<u16>) -> Result<RedisValue, RedisError> {
+  let args: Vec<RedisValue> = if let Some(bits) = bits {
+    vec![bits.into()]
+  } else {
+    Vec::new()
+  };
+
+  let frame = utils::request_response(client, move || Ok((RedisCommandKind::AclGenPass, args))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn acl_log_reset<C: ClientLike>(client: &C) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, || Ok((RedisCommandKind::AclLog, vec![static_val!(RESET)]))).await?;
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+pub async fn acl_log_count<C: ClientLike>(client: &C, count: Option<u32>) -> Result<RedisValue, RedisError> {
+  let args: Vec<RedisValue> = if let Some(count) = count {
+    vec![count.into()]
+  } else {
+    Vec::new()
+  };
+
+  let frame = utils::request_response(client, move || Ok((RedisCommandKind::AclLog, args))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/client.rs.html b/doc/src/fred/commands/impls/client.rs.html new file mode 100644 index 00000000..06545ec4 --- /dev/null +++ b/doc/src/fred/commands/impls/client.rs.html @@ -0,0 +1,265 @@ +client.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+
use super::*;
+use crate::{
+  protocol::{
+    command::{RedisCommand, RedisCommandKind},
+    utils as protocol_utils,
+  },
+  types::*,
+  utils,
+};
+use bytes_utils::Str;
+
+value_cmd!(client_id, ClientID);
+value_cmd!(client_info, ClientInfo);
+
+pub async fn client_kill<C: ClientLike>(
+  client: &C,
+  filters: Vec<ClientKillFilter>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(filters.len() * 2);
+
+    for filter in filters.into_iter() {
+      let (field, value) = filter.to_str();
+      args.push(field.into());
+      args.push(value.into());
+    }
+
+    Ok((RedisCommandKind::ClientKill, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn client_list<C: ClientLike>(
+  client: &C,
+  r#type: Option<ClientKillType>,
+  ids: Option<Vec<String>>,
+) -> Result<RedisValue, RedisError> {
+  let ids: Option<Vec<RedisKey>> = ids.map(|ids| ids.into_iter().map(|id| id.into()).collect());
+  let frame = utils::request_response(client, move || {
+    let max_args = 2 + ids.as_ref().map(|i| i.len()).unwrap_or(0);
+    let mut args = Vec::with_capacity(max_args);
+
+    if let Some(kind) = r#type {
+      args.push(static_val!(TYPE));
+      args.push(kind.to_str().into());
+    }
+    if let Some(ids) = ids {
+      if !ids.is_empty() {
+        args.push(static_val!(ID));
+
+        for id in ids.into_iter() {
+          args.push(id.into());
+        }
+      }
+    }
+
+    Ok((RedisCommandKind::ClientList, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn client_pause<C: ClientLike>(
+  client: &C,
+  timeout: i64,
+  mode: Option<ClientPauseKind>,
+) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2);
+    args.push(timeout.into());
+
+    if let Some(mode) = mode {
+      args.push(mode.to_str().into());
+    }
+
+    Ok((RedisCommandKind::ClientPause, args))
+  })
+  .await?;
+
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+value_cmd!(client_getname, ClientGetName);
+
+pub async fn client_setname<C: ClientLike>(client: &C, name: Str) -> Result<(), RedisError> {
+  let frame =
+    utils::request_response(client, move || Ok((RedisCommandKind::ClientSetname, vec![name.into()]))).await?;
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+ok_cmd!(client_unpause, ClientUnpause);
+
+pub async fn client_reply<C: ClientLike>(client: &C, flag: ClientReplyFlag) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::ClientReply, vec![flag.to_str().into()]))
+  })
+  .await?;
+
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+pub async fn client_unblock<C: ClientLike>(
+  client: &C,
+  id: RedisValue,
+  flag: Option<ClientUnblockFlag>,
+) -> Result<RedisValue, RedisError> {
+  let inner = client.inner();
+
+  let mut args = Vec::with_capacity(2);
+  args.push(id);
+  if let Some(flag) = flag {
+    args.push(flag.to_str().into());
+  }
+  let command = RedisCommand::new(RedisCommandKind::ClientUnblock, args);
+
+  let frame = utils::backchannel_request_response(inner, command, false).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn unblock_self<C: ClientLike>(client: &C, flag: Option<ClientUnblockFlag>) -> Result<(), RedisError> {
+  let inner = client.inner();
+  let flag = flag.unwrap_or(ClientUnblockFlag::Error);
+  let result = utils::interrupt_blocked_connection(inner, flag).await;
+  inner.backchannel.write().await.set_unblocked();
+  result
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/cluster.rs.html b/doc/src/fred/commands/impls/cluster.rs.html new file mode 100644 index 00000000..13617356 --- /dev/null +++ b/doc/src/fred/commands/impls/cluster.rs.html @@ -0,0 +1,367 @@ +cluster.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+
use super::*;
+use crate::{
+  interfaces,
+  protocol::{
+    command::{RedisCommandKind, RouterCommand},
+    utils as protocol_utils,
+  },
+  runtime::oneshot_channel,
+  types::*,
+  utils,
+};
+use bytes_utils::Str;
+use std::convert::TryInto;
+
+value_cmd!(cluster_bumpepoch, ClusterBumpEpoch);
+ok_cmd!(cluster_flushslots, ClusterFlushSlots);
+value_cmd!(cluster_myid, ClusterMyID);
+value_cmd!(cluster_nodes, ClusterNodes);
+ok_cmd!(cluster_saveconfig, ClusterSaveConfig);
+values_cmd!(cluster_slots, ClusterSlots);
+
+pub async fn cluster_info<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, || Ok((RedisCommandKind::ClusterInfo, vec![]))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn cluster_add_slots<C: ClientLike>(client: &C, slots: MultipleHashSlots) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(slots.len());
+
+    for slot in slots.inner().into_iter() {
+      args.push(slot.into());
+    }
+
+    Ok((RedisCommandKind::ClusterAddSlots, args))
+  })
+  .await?;
+
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+pub async fn cluster_count_failure_reports<C: ClientLike>(
+  client: &C,
+  node_id: Str,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::ClusterCountFailureReports, vec![node_id.into()]))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn cluster_count_keys_in_slot<C: ClientLike>(client: &C, slot: u16) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::ClusterCountKeysInSlot, vec![slot.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn cluster_del_slots<C: ClientLike>(client: &C, slots: MultipleHashSlots) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(slots.len());
+
+    for slot in slots.inner().into_iter() {
+      args.push(slot.into());
+    }
+
+    Ok((RedisCommandKind::ClusterDelSlots, args))
+  })
+  .await?;
+
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+pub async fn cluster_failover<C: ClientLike>(
+  client: &C,
+  flag: Option<ClusterFailoverFlag>,
+) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args = if let Some(flag) = flag {
+      vec![flag.to_str().into()]
+    } else {
+      Vec::new()
+    };
+
+    Ok((RedisCommandKind::ClusterFailOver, args))
+  })
+  .await?;
+
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+pub async fn cluster_forget<C: ClientLike>(client: &C, node_id: Str) -> Result<(), RedisError> {
+  one_arg_ok_cmd(client, RedisCommandKind::ClusterForget, node_id.into()).await
+}
+
+pub async fn cluster_get_keys_in_slot<C: ClientLike>(
+  client: &C,
+  slot: u16,
+  count: u64,
+) -> Result<RedisValue, RedisError> {
+  let count: RedisValue = count.try_into()?;
+
+  let frame = utils::request_response(client, move || {
+    let args = vec![slot.into(), count];
+    Ok((RedisCommandKind::ClusterGetKeysInSlot, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn cluster_keyslot<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::ClusterKeySlot, key.into()).await
+}
+
+pub async fn cluster_meet<C: ClientLike>(client: &C, ip: Str, port: u16) -> Result<(), RedisError> {
+  args_ok_cmd(client, RedisCommandKind::ClusterMeet, vec![ip.into(), port.into()]).await
+}
+
+pub async fn cluster_replicate<C: ClientLike>(client: &C, node_id: Str) -> Result<(), RedisError> {
+  one_arg_ok_cmd(client, RedisCommandKind::ClusterReplicate, node_id.into()).await
+}
+
+pub async fn cluster_replicas<C: ClientLike>(client: &C, node_id: Str) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::ClusterReplicas, node_id.into()).await
+}
+
+pub async fn cluster_reset<C: ClientLike>(client: &C, mode: Option<ClusterResetFlag>) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1);
+    if let Some(flag) = mode {
+      args.push(flag.to_str().into());
+    }
+
+    Ok((RedisCommandKind::ClusterReset, args))
+  })
+  .await?;
+
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+pub async fn cluster_set_config_epoch<C: ClientLike>(client: &C, epoch: u64) -> Result<(), RedisError> {
+  let epoch: RedisValue = epoch.try_into()?;
+  one_arg_ok_cmd(client, RedisCommandKind::ClusterSetConfigEpoch, epoch).await
+}
+
+pub async fn cluster_setslot<C: ClientLike>(
+  client: &C,
+  slot: u16,
+  state: ClusterSetSlotState,
+) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(3);
+    args.push(slot.into());
+
+    let (state, arg) = state.to_str();
+    args.push(state.into());
+    if let Some(arg) = arg {
+      args.push(arg.into());
+    }
+
+    Ok((RedisCommandKind::ClusterSetSlot, args))
+  })
+  .await?;
+
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+pub async fn sync_cluster<C: ClientLike>(client: &C) -> Result<(), RedisError> {
+  let (tx, rx) = oneshot_channel();
+  let command = RouterCommand::SyncCluster { tx };
+  interfaces::send_to_router(client.inner(), command)?;
+
+  rx.await?
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/config.rs.html b/doc/src/fred/commands/impls/config.rs.html new file mode 100644 index 00000000..4a461cde --- /dev/null +++ b/doc/src/fred/commands/impls/config.rs.html @@ -0,0 +1,29 @@ +config.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
use super::*;
+use crate::{protocol::command::RedisCommandKind, types::*};
+use bytes_utils::Str;
+
+ok_cmd!(config_resetstat, ConfigResetStat);
+ok_cmd!(config_rewrite, ConfigRewrite);
+
+pub async fn config_get<C: ClientLike>(client: &C, parameter: Str) -> Result<RedisValue, RedisError> {
+  one_arg_values_cmd(client, RedisCommandKind::ConfigGet, parameter.into()).await
+}
+
+pub async fn config_set<C: ClientLike>(client: &C, parameter: Str, value: RedisValue) -> Result<(), RedisError> {
+  args_ok_cmd(client, RedisCommandKind::ConfigSet, vec![parameter.into(), value]).await
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/geo.rs.html b/doc/src/fred/commands/impls/geo.rs.html new file mode 100644 index 00000000..5de6f885 --- /dev/null +++ b/doc/src/fred/commands/impls/geo.rs.html @@ -0,0 +1,701 @@ +geo.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+
use super::*;
+use crate::{
+  error::RedisError,
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  types::*,
+  utils,
+};
+use std::convert::TryInto;
+
+static WITH_COORD: &str = "WITHCOORD";
+static WITH_DIST: &str = "WITHDIST";
+static WITH_HASH: &str = "WITHHASH";
+static STORE_DIST: &str = "STOREDIST";
+static FROM_MEMBER: &str = "FROMMEMBER";
+static FROM_LONLAT: &str = "FROMLONLAT";
+static BY_RADIUS: &str = "BYRADIUS";
+static BY_BOX: &str = "BYBOX";
+
+pub async fn geoadd<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  options: Option<SetOptions>,
+  changed: bool,
+  values: MultipleGeoValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(3 + (values.len() * 3));
+    args.push(key.into());
+
+    if let Some(options) = options {
+      args.push(options.to_str().into());
+    }
+    if changed {
+      args.push(static_val!(CHANGED));
+    }
+
+    for value in values.inner().into_iter() {
+      args.push(value.coordinates.longitude.try_into()?);
+      args.push(value.coordinates.latitude.try_into()?);
+      args.push(value.member)
+    }
+
+    Ok((RedisCommandKind::GeoAdd, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn geohash<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  members: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let members = members.into_multiple_values();
+    let mut args = Vec::with_capacity(1 + members.len());
+    args.push(key.into());
+
+    for member in members.into_iter() {
+      args.push(member);
+    }
+
+    Ok((RedisCommandKind::GeoHash, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn geopos<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  members: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let members = members.into_multiple_values();
+    let mut args = Vec::with_capacity(1 + members.len());
+    args.push(key.into());
+
+    for member in members.into_iter() {
+      args.push(member);
+    }
+
+    Ok((RedisCommandKind::GeoPos, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn geodist<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  src: RedisValue,
+  dest: RedisValue,
+  unit: Option<GeoUnit>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(4);
+    args.push(key.into());
+    args.push(src);
+    args.push(dest);
+
+    if let Some(unit) = unit {
+      args.push(unit.to_str().into());
+    }
+
+    Ok((RedisCommandKind::GeoDist, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn georadius<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  position: GeoPosition,
+  radius: f64,
+  unit: GeoUnit,
+  withcoord: bool,
+  withdist: bool,
+  withhash: bool,
+  count: Option<(u64, Any)>,
+  ord: Option<SortOrder>,
+  store: Option<RedisKey>,
+  storedist: Option<RedisKey>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(16);
+    args.push(key.into());
+    args.push(position.longitude.try_into()?);
+    args.push(position.latitude.try_into()?);
+    args.push(radius.try_into()?);
+    args.push(unit.to_str().into());
+
+    if withcoord {
+      args.push(static_val!(WITH_COORD));
+    }
+    if withdist {
+      args.push(static_val!(WITH_DIST));
+    }
+    if withhash {
+      args.push(static_val!(WITH_HASH));
+    }
+    if let Some((count, any)) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.try_into()?);
+      if any {
+        args.push(static_val!(ANY));
+      }
+    }
+    if let Some(ord) = ord {
+      args.push(ord.to_str().into());
+    }
+    if let Some(store) = store {
+      args.push(static_val!(STORE));
+      args.push(store.into());
+    }
+    if let Some(store_dist) = storedist {
+      args.push(static_val!(STORE_DIST));
+      args.push(store_dist.into());
+    }
+
+    Ok((RedisCommandKind::GeoRadius, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn georadiusbymember<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  member: RedisValue,
+  radius: f64,
+  unit: GeoUnit,
+  withcoord: bool,
+  withdist: bool,
+  withhash: bool,
+  count: Option<(u64, Any)>,
+  ord: Option<SortOrder>,
+  store: Option<RedisKey>,
+  storedist: Option<RedisKey>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(15);
+    args.push(key.into());
+    args.push(member);
+    args.push(radius.try_into()?);
+    args.push(unit.to_str().into());
+
+    if withcoord {
+      args.push(static_val!(WITH_COORD));
+    }
+    if withdist {
+      args.push(static_val!(WITH_DIST));
+    }
+    if withhash {
+      args.push(static_val!(WITH_HASH));
+    }
+    if let Some((count, any)) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.try_into()?);
+      if any {
+        args.push(static_val!(ANY));
+      }
+    }
+    if let Some(ord) = ord {
+      args.push(ord.to_str().into());
+    }
+    if let Some(store) = store {
+      args.push(static_val!(STORE));
+      args.push(store.into());
+    }
+    if let Some(store_dist) = storedist {
+      args.push(static_val!(STORE_DIST));
+      args.push(store_dist.into());
+    }
+
+    Ok((RedisCommandKind::GeoRadiusByMember, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn geosearch<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  from_member: Option<RedisValue>,
+  from_lonlat: Option<GeoPosition>,
+  by_radius: Option<(f64, GeoUnit)>,
+  by_box: Option<(f64, f64, GeoUnit)>,
+  ord: Option<SortOrder>,
+  count: Option<(u64, Any)>,
+  withcoord: bool,
+  withdist: bool,
+  withhash: bool,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(15);
+    args.push(key.into());
+
+    if let Some(member) = from_member {
+      args.push(static_val!(FROM_MEMBER));
+      args.push(member);
+    }
+    if let Some(position) = from_lonlat {
+      args.push(static_val!(FROM_LONLAT));
+      args.push(position.longitude.try_into()?);
+      args.push(position.latitude.try_into()?);
+    }
+
+    if let Some((radius, unit)) = by_radius {
+      args.push(static_val!(BY_RADIUS));
+      args.push(radius.try_into()?);
+      args.push(unit.to_str().into());
+    }
+    if let Some((width, height, unit)) = by_box {
+      args.push(static_val!(BY_BOX));
+      args.push(width.try_into()?);
+      args.push(height.try_into()?);
+      args.push(unit.to_str().into());
+    }
+    if let Some(ord) = ord {
+      args.push(ord.to_str().into());
+    }
+    if let Some((count, any)) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.try_into()?);
+      if any {
+        args.push(static_val!(ANY));
+      }
+    }
+    if withcoord {
+      args.push(static_val!(WITH_COORD));
+    }
+    if withdist {
+      args.push(static_val!(WITH_DIST));
+    }
+    if withhash {
+      args.push(static_val!(WITH_HASH));
+    }
+
+    Ok((RedisCommandKind::GeoSearch, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn geosearchstore<C: ClientLike>(
+  client: &C,
+  dest: RedisKey,
+  source: RedisKey,
+  from_member: Option<RedisValue>,
+  from_lonlat: Option<GeoPosition>,
+  by_radius: Option<(f64, GeoUnit)>,
+  by_box: Option<(f64, f64, GeoUnit)>,
+  ord: Option<SortOrder>,
+  count: Option<(u64, Any)>,
+  storedist: bool,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(14);
+    args.push(dest.into());
+    args.push(source.into());
+
+    if let Some(member) = from_member {
+      args.push(static_val!(FROM_MEMBER));
+      args.push(member);
+    }
+    if let Some(position) = from_lonlat {
+      args.push(static_val!(FROM_LONLAT));
+      args.push(position.longitude.try_into()?);
+      args.push(position.latitude.try_into()?);
+    }
+    if let Some((radius, unit)) = by_radius {
+      args.push(static_val!(BY_RADIUS));
+      args.push(radius.try_into()?);
+      args.push(unit.to_str().into());
+    }
+    if let Some((width, height, unit)) = by_box {
+      args.push(static_val!(BY_BOX));
+      args.push(width.try_into()?);
+      args.push(height.try_into()?);
+      args.push(unit.to_str().into());
+    }
+    if let Some(ord) = ord {
+      args.push(ord.to_str().into());
+    }
+    if let Some((count, any)) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.try_into()?);
+      if any {
+        args.push(static_val!(ANY));
+      }
+    }
+    if storedist {
+      args.push(static_val!(STORE_DIST));
+    }
+
+    Ok((RedisCommandKind::GeoSearchStore, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/hashes.rs.html b/doc/src/fred/commands/impls/hashes.rs.html new file mode 100644 index 00000000..7e2575a2 --- /dev/null +++ b/doc/src/fred/commands/impls/hashes.rs.html @@ -0,0 +1,383 @@ +hashes.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+
use super::*;
+use crate::{
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  types::*,
+  utils,
+};
+use redis_protocol::resp3::types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame};
+use std::{convert::TryInto, str};
+
+fn frame_is_queued(frame: &Resp3Frame) -> bool {
+  match frame {
+    Resp3Frame::SimpleString { ref data, .. } | Resp3Frame::BlobString { ref data, .. } => {
+      str::from_utf8(data).ok().map(|s| s == QUEUED).unwrap_or(false)
+    },
+    _ => false,
+  }
+}
+
+pub async fn hdel<C: ClientLike>(client: &C, key: RedisKey, fields: MultipleKeys) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1 + fields.len());
+    args.push(key.into());
+
+    for field in fields.inner().into_iter() {
+      args.push(field.into());
+    }
+
+    Ok((RedisCommandKind::HDel, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn hexists<C: ClientLike>(client: &C, key: RedisKey, field: RedisKey) -> Result<RedisValue, RedisError> {
+  let args: Vec<RedisValue> = vec![key.into(), field.into()];
+  args_value_cmd(client, RedisCommandKind::HExists, args).await
+}
+
+pub async fn hget<C: ClientLike>(client: &C, key: RedisKey, field: RedisKey) -> Result<RedisValue, RedisError> {
+  let args: Vec<RedisValue> = vec![key.into(), field.into()];
+  args_value_cmd(client, RedisCommandKind::HGet, args).await
+}
+
+pub async fn hgetall<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || Ok((RedisCommandKind::HGetAll, vec![key.into()]))).await?;
+
+  if frame.as_str().map(|s| s == QUEUED).unwrap_or(false) {
+    protocol_utils::frame_to_results(frame)
+  } else {
+    Ok(RedisValue::Map(protocol_utils::frame_to_map(frame)?))
+  }
+}
+
+pub async fn hincrby<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  field: RedisKey,
+  increment: i64,
+) -> Result<RedisValue, RedisError> {
+  let args: Vec<RedisValue> = vec![key.into(), field.into(), increment.into()];
+  args_value_cmd(client, RedisCommandKind::HIncrBy, args).await
+}
+
+pub async fn hincrbyfloat<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  field: RedisKey,
+  increment: f64,
+) -> Result<RedisValue, RedisError> {
+  let args: Vec<RedisValue> = vec![key.into(), field.into(), increment.try_into()?];
+  args_value_cmd(client, RedisCommandKind::HIncrByFloat, args).await
+}
+
+pub async fn hkeys<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || Ok((RedisCommandKind::HKeys, vec![key.into()]))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn hlen<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::HLen, key.into()).await
+}
+
+pub async fn hmget<C: ClientLike>(client: &C, key: RedisKey, fields: MultipleKeys) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1 + fields.len());
+    args.push(key.into());
+
+    for field in fields.inner().into_iter() {
+      args.push(field.into());
+    }
+    Ok((RedisCommandKind::HMGet, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn hmset<C: ClientLike>(client: &C, key: RedisKey, values: RedisMap) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1 + (values.len() * 2));
+    args.push(key.into());
+
+    for (key, value) in values.inner().into_iter().filter(|x| !x.1.is_null()) {
+      args.push(key.into());
+      args.push(value);
+    }
+    Ok((RedisCommandKind::HMSet, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn hset<C: ClientLike>(client: &C, key: RedisKey, values: RedisMap) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1 + (values.len() * 2));
+    args.push(key.into());
+
+    for (key, value) in values.inner().into_iter().filter(|x| !x.1.is_null()) {
+      args.push(key.into());
+      args.push(value);
+    }
+
+    Ok((RedisCommandKind::HSet, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn hsetnx<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  field: RedisKey,
+  value: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::HSetNx, vec![key.into(), field.into(), value]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn hrandfield<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  count: Option<(i64, bool)>,
+) -> Result<RedisValue, RedisError> {
+  let (has_count, has_values) = count.as_ref().map(|(_c, b)| (true, *b)).unwrap_or((false, false));
+
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(3);
+    args.push(key.into());
+
+    if let Some((count, with_values)) = count {
+      args.push(count.into());
+      if with_values {
+        args.push(static_val!(WITH_VALUES));
+      }
+    }
+
+    Ok((RedisCommandKind::HRandField, args))
+  })
+  .await?;
+
+  if has_count {
+    if has_values && frame.as_str().map(|s| s != QUEUED).unwrap_or(true) {
+      let frame = protocol_utils::flatten_frame(frame);
+      protocol_utils::frame_to_map(frame).map(RedisValue::Map)
+    } else {
+      protocol_utils::frame_to_results(frame)
+    }
+  } else {
+    protocol_utils::frame_to_results(frame)
+  }
+}
+
+pub async fn hstrlen<C: ClientLike>(client: &C, key: RedisKey, field: RedisKey) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::HStrLen, vec![key.into(), field.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn hvals<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_values_cmd(client, RedisCommandKind::HVals, key.into()).await
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/hyperloglog.rs.html b/doc/src/fred/commands/impls/hyperloglog.rs.html new file mode 100644 index 00000000..af0a89b6 --- /dev/null +++ b/doc/src/fred/commands/impls/hyperloglog.rs.html @@ -0,0 +1,101 @@ +hyperloglog.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+
use super::*;
+use crate::{
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  types::*,
+  utils,
+};
+
+pub async fn pfadd<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  elements: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let elements = elements.into_multiple_values();
+    let mut args = Vec::with_capacity(1 + elements.len());
+    args.push(key.into());
+
+    for element in elements.into_iter() {
+      args.push(element);
+    }
+    Ok((RedisCommandKind::Pfadd, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn pfcount<C: ClientLike>(client: &C, keys: MultipleKeys) -> Result<RedisValue, RedisError> {
+  let args: Vec<RedisValue> = keys.inner().into_iter().map(|k| k.into()).collect();
+  args_value_cmd(client, RedisCommandKind::Pfcount, args).await
+}
+
+pub async fn pfmerge<C: ClientLike>(
+  client: &C,
+  dest: RedisKey,
+  sources: MultipleKeys,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1 + sources.len());
+    args.push(dest.into());
+
+    for source in sources.inner().into_iter() {
+      args.push(source.into());
+    }
+    Ok((RedisCommandKind::Pfmerge, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/keys.rs.html b/doc/src/fred/commands/impls/keys.rs.html new file mode 100644 index 00000000..092f8be5 --- /dev/null +++ b/doc/src/fred/commands/impls/keys.rs.html @@ -0,0 +1,889 @@ +keys.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+
use super::*;
+use crate::{
+  error::*,
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  types::*,
+  utils,
+};
+use std::convert::TryInto;
+
+fn check_empty_keys(keys: &MultipleKeys) -> Result<(), RedisError> {
+  if keys.len() == 0 {
+    Err(RedisError::new(
+      RedisErrorKind::InvalidArgument,
+      "At least one key is required.",
+    ))
+  } else {
+    Ok(())
+  }
+}
+
+value_cmd!(randomkey, Randomkey);
+
+pub async fn get<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_values_cmd(client, RedisCommandKind::Get, key.into()).await
+}
+
+pub async fn set<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  value: RedisValue,
+  expire: Option<Expiration>,
+  options: Option<SetOptions>,
+  get: bool,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(6);
+    args.push(key.into());
+    args.push(value);
+
+    if let Some(expire) = expire {
+      let (k, v) = expire.into_args();
+      args.push(k.into());
+      if let Some(v) = v {
+        args.push(v.into());
+      }
+    }
+    if let Some(options) = options {
+      args.push(options.to_str().into());
+    }
+    if get {
+      args.push(static_val!(GET));
+    }
+
+    Ok((RedisCommandKind::Set, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn del<C: ClientLike>(client: &C, keys: MultipleKeys) -> Result<RedisValue, RedisError> {
+  check_empty_keys(&keys)?;
+
+  let args: Vec<RedisValue> = keys.inner().drain(..).map(|k| k.into()).collect();
+  let frame = utils::request_response(client, move || Ok((RedisCommandKind::Del, args))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn unlink<C: ClientLike>(client: &C, keys: MultipleKeys) -> Result<RedisValue, RedisError> {
+  check_empty_keys(&keys)?;
+
+  let args: Vec<RedisValue> = keys.inner().drain(..).map(|k| k.into()).collect();
+  let frame = utils::request_response(client, move || Ok((RedisCommandKind::Unlink, args))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn append<C: ClientLike>(client: &C, key: RedisKey, value: RedisValue) -> Result<RedisValue, RedisError> {
+  args_value_cmd(client, RedisCommandKind::Append, vec![key.into(), value]).await
+}
+
+pub async fn incr<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::Incr, key.into()).await
+}
+
+pub async fn decr<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::Decr, key.into()).await
+}
+
+pub async fn incr_by<C: ClientLike>(client: &C, key: RedisKey, val: i64) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::IncrBy, vec![key.into(), val.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn decr_by<C: ClientLike>(client: &C, key: RedisKey, val: i64) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::DecrBy, vec![key.into(), val.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn incr_by_float<C: ClientLike>(client: &C, key: RedisKey, val: f64) -> Result<RedisValue, RedisError> {
+  let val: RedisValue = val.try_into()?;
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::IncrByFloat, vec![key.into(), val]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ttl<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::Ttl, key.into()).await
+}
+
+pub async fn pttl<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::Pttl, key.into()).await
+}
+
+pub async fn persist<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::Persist, key.into()).await
+}
+
+pub async fn expire<C: ClientLike>(client: &C, key: RedisKey, seconds: i64) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Expire, vec![key.into(), seconds.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn expire_at<C: ClientLike>(client: &C, key: RedisKey, timestamp: i64) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::ExpireAt, vec![key.into(), timestamp.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn pexpire<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  milliseconds: i64,
+  options: Option<ExpireOptions>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args = if let Some(options) = options {
+      vec![key.into(), milliseconds.into(), options.to_str().into()]
+    } else {
+      vec![key.into(), milliseconds.into()]
+    };
+
+    Ok((RedisCommandKind::Pexpire, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn pexpire_at<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  timestamp: i64,
+  options: Option<ExpireOptions>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args = if let Some(options) = options {
+      vec![key.into(), timestamp.into(), options.to_str().into()]
+    } else {
+      vec![key.into(), timestamp.into()]
+    };
+
+    Ok((RedisCommandKind::Pexpireat, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn exists<C: ClientLike>(client: &C, keys: MultipleKeys) -> Result<RedisValue, RedisError> {
+  check_empty_keys(&keys)?;
+
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(keys.len());
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+
+    Ok((RedisCommandKind::Exists, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn dump<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_values_cmd(client, RedisCommandKind::Dump, key.into()).await
+}
+
+pub async fn restore<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  ttl: i64,
+  serialized: RedisValue,
+  replace: bool,
+  absttl: bool,
+  idletime: Option<i64>,
+  frequency: Option<i64>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(9);
+    args.push(key.into());
+    args.push(ttl.into());
+    args.push(serialized);
+
+    if replace {
+      args.push(static_val!(REPLACE));
+    }
+    if absttl {
+      args.push(static_val!(ABSTTL));
+    }
+    if let Some(idletime) = idletime {
+      args.push(static_val!(IDLE_TIME));
+      args.push(idletime.into());
+    }
+    if let Some(frequency) = frequency {
+      args.push(static_val!(FREQ));
+      args.push(frequency.into());
+    }
+
+    Ok((RedisCommandKind::Restore, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn getrange<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  start: usize,
+  end: usize,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::GetRange, vec![
+      key.into(),
+      start.try_into()?,
+      end.try_into()?,
+    ]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn setrange<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  offset: u32,
+  value: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Setrange, vec![key.into(), offset.into(), value]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn getset<C: ClientLike>(client: &C, key: RedisKey, value: RedisValue) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::GetSet, vec![key.into(), value]).await
+}
+
+pub async fn rename<C: ClientLike>(
+  client: &C,
+  source: RedisKey,
+  destination: RedisKey,
+) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::Rename, vec![
+    source.into(),
+    destination.into(),
+  ])
+  .await
+}
+
+pub async fn renamenx<C: ClientLike>(
+  client: &C,
+  source: RedisKey,
+  destination: RedisKey,
+) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::Renamenx, vec![
+    source.into(),
+    destination.into(),
+  ])
+  .await
+}
+
+pub async fn getdel<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_values_cmd(client, RedisCommandKind::GetDel, key.into()).await
+}
+
+pub async fn strlen<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::Strlen, key.into()).await
+}
+
+pub async fn mget<C: ClientLike>(client: &C, keys: MultipleKeys) -> Result<RedisValue, RedisError> {
+  check_empty_keys(&keys)?;
+
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(keys.len());
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+
+    Ok((RedisCommandKind::Mget, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn mset<C: ClientLike>(client: &C, values: RedisMap) -> Result<RedisValue, RedisError> {
+  if values.len() == 0 {
+    return Err(RedisError::new(
+      RedisErrorKind::InvalidArgument,
+      "Values cannot be empty.",
+    ));
+  }
+
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(values.len() * 2);
+
+    for (key, value) in values.inner().into_iter() {
+      args.push(key.into());
+      args.push(value);
+    }
+
+    Ok((RedisCommandKind::Mset, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn msetnx<C: ClientLike>(client: &C, values: RedisMap) -> Result<RedisValue, RedisError> {
+  if values.len() == 0 {
+    return Err(RedisError::new(
+      RedisErrorKind::InvalidArgument,
+      "Values cannot be empty.",
+    ));
+  }
+
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(values.len() * 2);
+
+    for (key, value) in values.inner().into_iter() {
+      args.push(key.into());
+      args.push(value);
+    }
+
+    Ok((RedisCommandKind::Msetnx, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn copy<C: ClientLike>(
+  client: &C,
+  source: RedisKey,
+  destination: RedisKey,
+  db: Option<u8>,
+  replace: bool,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(5);
+    args.push(source.into());
+    args.push(destination.into());
+
+    if let Some(db) = db {
+      args.push(static_val!(DB));
+      args.push(db.into());
+    }
+    if replace {
+      args.push(static_val!(REPLACE));
+    }
+
+    Ok((RedisCommandKind::Copy, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn watch<C: ClientLike>(client: &C, keys: MultipleKeys) -> Result<(), RedisError> {
+  let args = keys.inner().into_iter().map(|k| k.into()).collect();
+  args_ok_cmd(client, RedisCommandKind::Watch, args).await
+}
+
+ok_cmd!(unwatch, Unwatch);
+
+pub async fn lcs<C: ClientLike>(
+  client: &C,
+  key1: RedisKey,
+  key2: RedisKey,
+  len: bool,
+  idx: bool,
+  minmatchlen: Option<i64>,
+  withmatchlen: bool,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(7);
+    args.push(key1.into());
+    args.push(key2.into());
+
+    if len {
+      args.push(static_val!(LEN));
+    }
+    if idx {
+      args.push(static_val!(IDX));
+    }
+    if let Some(minmatchlen) = minmatchlen {
+      args.push(static_val!(MINMATCHLEN));
+      args.push(minmatchlen.into());
+    }
+    if withmatchlen {
+      args.push(static_val!(WITHMATCHLEN));
+    }
+
+    Ok((RedisCommandKind::Lcs, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/lists.rs.html b/doc/src/fred/commands/impls/lists.rs.html new file mode 100644 index 00000000..5ef5c155 --- /dev/null +++ b/doc/src/fred/commands/impls/lists.rs.html @@ -0,0 +1,963 @@ +lists.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+
use super::*;
+use crate::{
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  types::*,
+  utils,
+};
+use bytes_utils::Str;
+use std::convert::TryInto;
+
+pub async fn sort_ro<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  by: Option<Str>,
+  limit: Option<Limit>,
+  get: MultipleStrings,
+  order: Option<SortOrder>,
+  alpha: bool,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(8 + get.len() * 2);
+    args.push(key.into());
+
+    if let Some(pattern) = by {
+      args.push(static_val!("BY"));
+      args.push(pattern.into());
+    }
+    if let Some((offset, count)) = limit {
+      args.push(static_val!(LIMIT));
+      args.push(offset.into());
+      args.push(count.into());
+    }
+    for pattern in get.inner().into_iter() {
+      args.push(static_val!(GET));
+      args.push(pattern.into());
+    }
+    if let Some(order) = order {
+      args.push(order.to_str().into());
+    }
+    if alpha {
+      args.push(static_val!("ALPHA"));
+    }
+
+    Ok((RedisCommandKind::SortRo, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn sort<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  by: Option<Str>,
+  limit: Option<Limit>,
+  get: MultipleStrings,
+  order: Option<SortOrder>,
+  alpha: bool,
+  store: Option<RedisKey>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(10 + get.len() * 2);
+    args.push(key.into());
+
+    if let Some(pattern) = by {
+      args.push(static_val!("BY"));
+      args.push(pattern.into());
+    }
+    if let Some((offset, count)) = limit {
+      args.push(static_val!(LIMIT));
+      args.push(offset.into());
+      args.push(count.into());
+    }
+    for pattern in get.inner().into_iter() {
+      args.push(static_val!(GET));
+      args.push(pattern.into());
+    }
+    if let Some(order) = order {
+      args.push(order.to_str().into());
+    }
+    if alpha {
+      args.push(static_val!("ALPHA"));
+    }
+    if let Some(dest) = store {
+      args.push(static_val!(STORE));
+      args.push(dest.into());
+    }
+
+    Ok((RedisCommandKind::Sort, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn blmpop<C: ClientLike>(
+  client: &C,
+  timeout: f64,
+  keys: MultipleKeys,
+  direction: LMoveDirection,
+  count: Option<i64>,
+) -> Result<RedisValue, RedisError> {
+  let timeout: RedisValue = timeout.try_into()?;
+
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(keys.len() + 4);
+    args.push(timeout);
+    args.push(keys.len().try_into()?);
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    args.push(direction.to_str().into());
+    if let Some(count) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.into());
+    }
+
+    Ok((RedisCommandKind::BlmPop, args))
+  })
+  .await?;
+
+  protocol_utils::check_null_timeout(&frame)?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn blpop<C: ClientLike>(client: &C, keys: MultipleKeys, timeout: f64) -> Result<RedisValue, RedisError> {
+  let timeout: RedisValue = timeout.try_into()?;
+
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(keys.len() + 1);
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    args.push(timeout);
+
+    Ok((RedisCommandKind::BlPop, args))
+  })
+  .await?;
+
+  protocol_utils::check_null_timeout(&frame)?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn brpop<C: ClientLike>(client: &C, keys: MultipleKeys, timeout: f64) -> Result<RedisValue, RedisError> {
+  let timeout: RedisValue = timeout.try_into()?;
+
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(keys.len() + 1);
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    args.push(timeout);
+
+    Ok((RedisCommandKind::BrPop, args))
+  })
+  .await?;
+
+  protocol_utils::check_null_timeout(&frame)?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn brpoplpush<C: ClientLike>(
+  client: &C,
+  source: RedisKey,
+  destination: RedisKey,
+  timeout: f64,
+) -> Result<RedisValue, RedisError> {
+  let timeout: RedisValue = timeout.try_into()?;
+
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::BrPopLPush, vec![
+      source.into(),
+      destination.into(),
+      timeout,
+    ]))
+  })
+  .await?;
+
+  protocol_utils::check_null_timeout(&frame)?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn blmove<C: ClientLike>(
+  client: &C,
+  source: RedisKey,
+  destination: RedisKey,
+  source_direction: LMoveDirection,
+  destination_direction: LMoveDirection,
+  timeout: f64,
+) -> Result<RedisValue, RedisError> {
+  let timeout: RedisValue = timeout.try_into()?;
+
+  let frame = utils::request_response(client, move || {
+    let args = vec![
+      source.into(),
+      destination.into(),
+      source_direction.to_str().into(),
+      destination_direction.to_str().into(),
+      timeout,
+    ];
+
+    Ok((RedisCommandKind::BlMove, args))
+  })
+  .await?;
+
+  protocol_utils::check_null_timeout(&frame)?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn lmpop<C: ClientLike>(
+  client: &C,
+  keys: MultipleKeys,
+  direction: LMoveDirection,
+  count: Option<i64>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(keys.len() + 3);
+    args.push(keys.len().try_into()?);
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    args.push(direction.to_str().into());
+    if let Some(count) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.into());
+    }
+
+    Ok((RedisCommandKind::LMPop, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn lindex<C: ClientLike>(client: &C, key: RedisKey, index: i64) -> Result<RedisValue, RedisError> {
+  let args: Vec<RedisValue> = vec![key.into(), index.into()];
+  args_value_cmd(client, RedisCommandKind::LIndex, args).await
+}
+
+pub async fn linsert<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  location: ListLocation,
+  pivot: RedisValue,
+  element: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::LInsert, vec![
+      key.into(),
+      location.to_str().into(),
+      pivot,
+      element,
+    ]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn llen<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::LLen, key.into()).await
+}
+
+pub async fn lpop<C: ClientLike>(client: &C, key: RedisKey, count: Option<usize>) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2);
+    args.push(key.into());
+
+    if let Some(count) = count {
+      args.push(count.try_into()?);
+    }
+
+    Ok((RedisCommandKind::LPop, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn lpos<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  element: RedisValue,
+  rank: Option<i64>,
+  count: Option<i64>,
+  maxlen: Option<i64>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(8);
+    args.push(key.into());
+    args.push(element);
+
+    if let Some(rank) = rank {
+      args.push(static_val!(RANK));
+      args.push(rank.into());
+    }
+    if let Some(count) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.into());
+    }
+    if let Some(maxlen) = maxlen {
+      args.push(static_val!(MAXLEN));
+      args.push(maxlen.into());
+    }
+
+    Ok((RedisCommandKind::LPos, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn lpush<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  elements: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let elements = elements.into_multiple_values();
+    let mut args = Vec::with_capacity(1 + elements.len());
+    args.push(key.into());
+
+    for element in elements.into_iter() {
+      args.push(element);
+    }
+
+    Ok((RedisCommandKind::LPush, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn lpushx<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  elements: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let elements = elements.into_multiple_values();
+    let mut args = Vec::with_capacity(1 + elements.len());
+    args.push(key.into());
+
+    for element in elements.into_iter() {
+      args.push(element);
+    }
+
+    Ok((RedisCommandKind::LPushX, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn lrange<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  start: i64,
+  stop: i64,
+) -> Result<RedisValue, RedisError> {
+  let (key, start, stop) = (key.into(), start.into(), stop.into());
+  args_values_cmd(client, RedisCommandKind::LRange, vec![key, start, stop]).await
+}
+
+pub async fn lrem<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  count: i64,
+  element: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  let (key, count) = (key.into(), count.into());
+  args_value_cmd(client, RedisCommandKind::LRem, vec![key, count, element]).await
+}
+
+pub async fn lset<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  index: i64,
+  element: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  let args = vec![key.into(), index.into(), element];
+  args_value_cmd(client, RedisCommandKind::LSet, args).await
+}
+
+pub async fn ltrim<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  start: i64,
+  stop: i64,
+) -> Result<RedisValue, RedisError> {
+  let args = vec![key.into(), start.into(), stop.into()];
+  args_value_cmd(client, RedisCommandKind::LTrim, args).await
+}
+
+pub async fn rpop<C: ClientLike>(client: &C, key: RedisKey, count: Option<usize>) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2);
+    args.push(key.into());
+
+    if let Some(count) = count {
+      args.push(count.try_into()?);
+    }
+
+    Ok((RedisCommandKind::Rpop, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn rpoplpush<C: ClientLike>(
+  client: &C,
+  source: RedisKey,
+  dest: RedisKey,
+) -> Result<RedisValue, RedisError> {
+  let args = vec![source.into(), dest.into()];
+  args_value_cmd(client, RedisCommandKind::Rpoplpush, args).await
+}
+
+pub async fn lmove<C: ClientLike>(
+  client: &C,
+  source: RedisKey,
+  dest: RedisKey,
+  source_direction: LMoveDirection,
+  dest_direction: LMoveDirection,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args = vec![
+      source.into(),
+      dest.into(),
+      source_direction.to_str().into(),
+      dest_direction.to_str().into(),
+    ];
+
+    Ok((RedisCommandKind::LMove, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn rpush<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  elements: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let elements = elements.into_multiple_values();
+    let mut args = Vec::with_capacity(1 + elements.len());
+    args.push(key.into());
+
+    for element in elements.into_iter() {
+      args.push(element);
+    }
+
+    Ok((RedisCommandKind::Rpush, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn rpushx<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  elements: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let elements = elements.into_multiple_values();
+    let mut args = Vec::with_capacity(1 + elements.len());
+    args.push(key.into());
+
+    for element in elements.into_iter() {
+      args.push(element);
+    }
+
+    Ok((RedisCommandKind::Rpushx, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/lua.rs.html b/doc/src/fred/commands/impls/lua.rs.html new file mode 100644 index 00000000..030f40a7 --- /dev/null +++ b/doc/src/fred/commands/impls/lua.rs.html @@ -0,0 +1,939 @@ +lua.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+
use super::*;
+#[cfg(feature = "sha-1")]
+use crate::util::sha1_hash;
+use crate::{
+  error::*,
+  modules::inner::RedisClientInner,
+  protocol::{
+    command::{RedisCommand, RedisCommandKind},
+    hashers::ClusterHash,
+    responders::ResponseKind,
+    utils as protocol_utils,
+  },
+  runtime::{oneshot_channel, RefCount},
+  types::*,
+  utils,
+};
+use bytes::Bytes;
+use bytes_utils::Str;
+use redis_protocol::resp3::types::BytesFrame as Resp3Frame;
+use std::{convert::TryInto, str};
+
+/// Check that all the keys in an EVAL* command belong to the same server, returning a key slot that maps to that
+/// server.
+pub fn check_key_slot(inner: &RefCount<RedisClientInner>, keys: &[RedisKey]) -> Result<Option<u16>, RedisError> {
+  if inner.config.server.is_clustered() {
+    inner.with_cluster_state(|state| {
+      let (mut cmd_server, mut cmd_slot) = (None, None);
+      for key in keys.iter() {
+        let key_slot = redis_protocol::redis_keyslot(key.as_bytes());
+
+        if let Some(server) = state.get_server(key_slot) {
+          if let Some(ref cmd_server) = cmd_server {
+            if cmd_server != server {
+              return Err(RedisError::new(
+                RedisErrorKind::Cluster,
+                "All keys must belong to the same cluster node.",
+              ));
+            }
+          } else {
+            cmd_server = Some(server.clone());
+            cmd_slot = Some(key_slot);
+          }
+        } else {
+          return Err(RedisError::new(
+            RedisErrorKind::Cluster,
+            format!("Missing server for hash slot {}", key_slot),
+          ));
+        }
+      }
+
+      Ok(cmd_slot)
+    })
+  } else {
+    Ok(None)
+  }
+}
+
+pub async fn script_load<C: ClientLike>(client: &C, script: Str) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::ScriptLoad, script.into()).await
+}
+
+#[cfg(feature = "sha-1")]
+pub async fn script_load_cluster<C: ClientLike>(client: &C, script: Str) -> Result<RedisValue, RedisError> {
+  if !client.inner().config.server.is_clustered() {
+    return script_load(client, script).await;
+  }
+  let hash = sha1_hash(&script);
+
+  let (tx, rx) = oneshot_channel();
+  let response = ResponseKind::new_buffer(tx);
+  let mut command: RedisCommand = (RedisCommandKind::_ScriptLoadCluster, vec![script.into()], response).into();
+
+  let timeout_dur = utils::prepare_command(client, &mut command);
+  client.send_command(command)?;
+  let _ = utils::timeout(rx, timeout_dur).await??;
+  Ok(hash.into())
+}
+
+ok_cmd!(script_kill, ScriptKill);
+
+pub async fn script_kill_cluster<C: ClientLike>(client: &C) -> Result<(), RedisError> {
+  if !client.inner().config.server.is_clustered() {
+    return script_kill(client).await;
+  }
+
+  let (tx, rx) = oneshot_channel();
+  let response = ResponseKind::new_buffer(tx);
+  let mut command: RedisCommand = (RedisCommandKind::_ScriptKillCluster, vec![], response).into();
+
+  let timeout_dur = utils::prepare_command(client, &mut command);
+  client.send_command(command)?;
+  let _ = utils::timeout(rx, timeout_dur).await??;
+  Ok(())
+}
+
+pub async fn script_flush<C: ClientLike>(client: &C, r#async: bool) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, move || {
+    let arg = static_val!(if r#async { ASYNC } else { SYNC });
+    Ok((RedisCommandKind::ScriptFlush, vec![arg]))
+  })
+  .await?;
+
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+pub async fn script_flush_cluster<C: ClientLike>(client: &C, r#async: bool) -> Result<(), RedisError> {
+  if !client.inner().config.server.is_clustered() {
+    return script_flush(client, r#async).await;
+  }
+
+  let (tx, rx) = oneshot_channel();
+  let arg = static_val!(if r#async { ASYNC } else { SYNC });
+  let response = ResponseKind::new_buffer(tx);
+  let mut command: RedisCommand = (RedisCommandKind::_ScriptFlushCluster, vec![arg], response).into();
+
+  let timeout_dur = utils::prepare_command(client, &mut command);
+  client.send_command(command)?;
+
+  let _ = utils::timeout(rx, timeout_dur).await??;
+  Ok(())
+}
+
+pub async fn script_exists<C: ClientLike>(client: &C, hashes: MultipleStrings) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args = hashes.inner().into_iter().map(|s| s.into()).collect();
+    Ok((RedisCommandKind::ScriptExists, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn script_debug<C: ClientLike>(client: &C, flag: ScriptDebugFlag) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::ScriptDebug, vec![flag.to_str().into()]))
+  })
+  .await?;
+
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+pub async fn evalsha<C: ClientLike>(
+  client: &C,
+  hash: Str,
+  keys: MultipleKeys,
+  cmd_args: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let keys = keys.inner();
+  let custom_key_slot = check_key_slot(client.inner(), &keys)?;
+
+  let frame = utils::request_response(client, move || {
+    let cmd_args = cmd_args.into_multiple_values();
+    let mut args = Vec::with_capacity(2 + keys.len() + cmd_args.len());
+    args.push(hash.into());
+    args.push(keys.len().try_into()?);
+
+    for key in keys.into_iter() {
+      args.push(key.into());
+    }
+    for arg in cmd_args.into_iter() {
+      args.push(arg);
+    }
+
+    let mut command: RedisCommand = (RedisCommandKind::EvalSha, args).into();
+    command.hasher = custom_key_slot.map(ClusterHash::Custom).unwrap_or(ClusterHash::Random);
+    command.can_pipeline = false;
+    Ok(command)
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn eval<C: ClientLike>(
+  client: &C,
+  script: Str,
+  keys: MultipleKeys,
+  cmd_args: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let keys = keys.inner();
+  let custom_key_slot = check_key_slot(client.inner(), &keys)?;
+
+  let frame = utils::request_response(client, move || {
+    let cmd_args = cmd_args.into_multiple_values();
+    let mut args = Vec::with_capacity(2 + keys.len() + cmd_args.len());
+    args.push(script.into());
+    args.push(keys.len().try_into()?);
+
+    for key in keys.into_iter() {
+      args.push(key.into());
+    }
+    for arg in cmd_args.into_iter() {
+      args.push(arg);
+    }
+
+    let mut command: RedisCommand = (RedisCommandKind::Eval, args).into();
+    command.hasher = custom_key_slot.map(ClusterHash::Custom).unwrap_or(ClusterHash::Random);
+    command.can_pipeline = false;
+    Ok(command)
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn fcall<C: ClientLike>(
+  client: &C,
+  func: Str,
+  keys: MultipleKeys,
+  args: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args = args.into_multiple_values();
+    let mut arguments = Vec::with_capacity(keys.len() + args.len() + 2);
+    let mut custom_key_slot = None;
+
+    arguments.push(func.into());
+    arguments.push(keys.len().try_into()?);
+
+    for key in keys.inner().into_iter() {
+      custom_key_slot = Some(key.cluster_hash());
+      arguments.push(key.into());
+    }
+    for arg in args.into_iter() {
+      arguments.push(arg);
+    }
+
+    let mut command: RedisCommand = (RedisCommandKind::Fcall, arguments).into();
+    command.hasher = custom_key_slot.map(ClusterHash::Custom).unwrap_or(ClusterHash::Random);
+    command.can_pipeline = false;
+    Ok(command)
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn fcall_ro<C: ClientLike>(
+  client: &C,
+  func: Str,
+  keys: MultipleKeys,
+  args: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args = args.into_multiple_values();
+    let mut arguments = Vec::with_capacity(keys.len() + args.len() + 2);
+    let mut custom_key_slot = None;
+
+    arguments.push(func.into());
+    arguments.push(keys.len().try_into()?);
+
+    for key in keys.inner().into_iter() {
+      custom_key_slot = Some(key.cluster_hash());
+      arguments.push(key.into());
+    }
+    for arg in args.into_iter() {
+      arguments.push(arg);
+    }
+
+    let mut command: RedisCommand = (RedisCommandKind::FcallRO, arguments).into();
+    command.hasher = custom_key_slot.map(ClusterHash::Custom).unwrap_or(ClusterHash::Random);
+    command.can_pipeline = false;
+    Ok(command)
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn function_delete<C: ClientLike>(client: &C, library_name: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::FunctionDelete, vec![library_name.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn function_delete_cluster<C: ClientLike>(client: &C, library_name: Str) -> Result<(), RedisError> {
+  if !client.inner().config.server.is_clustered() {
+    return function_delete(client, library_name).await.map(|_| ());
+  }
+
+  let (tx, rx) = oneshot_channel();
+  let args: Vec<RedisValue> = vec![library_name.into()];
+
+  let response = ResponseKind::new_buffer(tx);
+  let mut command: RedisCommand = (RedisCommandKind::_FunctionDeleteCluster, args, response).into();
+
+  let timeout_dur = utils::prepare_command(client, &mut command);
+  client.send_command(command)?;
+
+  let _ = utils::timeout(rx, timeout_dur).await??;
+  Ok(())
+}
+
+pub async fn function_flush<C: ClientLike>(client: &C, r#async: bool) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args = if r#async {
+      vec![static_val!(ASYNC)]
+    } else {
+      vec![static_val!(SYNC)]
+    };
+
+    Ok((RedisCommandKind::FunctionFlush, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn function_flush_cluster<C: ClientLike>(client: &C, r#async: bool) -> Result<(), RedisError> {
+  if !client.inner().config.server.is_clustered() {
+    return function_flush(client, r#async).await.map(|_| ());
+  }
+
+  let (tx, rx) = oneshot_channel();
+  let args = if r#async {
+    vec![static_val!(ASYNC)]
+  } else {
+    vec![static_val!(SYNC)]
+  };
+
+  let response = ResponseKind::new_buffer(tx);
+  let command: RedisCommand = (RedisCommandKind::_FunctionFlushCluster, args, response).into();
+  client.send_command(command)?;
+
+  let _ = rx.await??;
+  Ok(())
+}
+
+pub async fn function_kill<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  let inner = client.inner();
+  let command = RedisCommand::new(RedisCommandKind::FunctionKill, vec![]);
+
+  let frame = utils::backchannel_request_response(inner, command, true).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn function_list<C: ClientLike>(
+  client: &C,
+  library_name: Option<Str>,
+  withcode: bool,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(3);
+
+    if let Some(library_name) = library_name {
+      args.push(static_val!(LIBRARYNAME));
+      args.push(library_name.into());
+    }
+    if withcode {
+      args.push(static_val!(WITHCODE));
+    }
+
+    Ok((RedisCommandKind::FunctionList, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn function_load<C: ClientLike>(client: &C, replace: bool, code: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2);
+    if replace {
+      args.push(static_val!(REPLACE));
+    }
+    args.push(code.into());
+
+    Ok((RedisCommandKind::FunctionLoad, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn function_load_cluster<C: ClientLike>(
+  client: &C,
+  replace: bool,
+  code: Str,
+) -> Result<RedisValue, RedisError> {
+  if !client.inner().config.server.is_clustered() {
+    return function_load(client, replace, code).await;
+  }
+
+  let (tx, rx) = oneshot_channel();
+  let mut args: Vec<RedisValue> = Vec::with_capacity(2);
+  if replace {
+    args.push(static_val!(REPLACE));
+  }
+  args.push(code.into());
+
+  let response = ResponseKind::new_buffer(tx);
+  let mut command: RedisCommand = (RedisCommandKind::_FunctionLoadCluster, args, response).into();
+
+  let timeout_dur = utils::prepare_command(client, &mut command);
+  client.send_command(command)?;
+
+  // each value in the response array is the response from a different primary node
+  match utils::timeout(rx, timeout_dur).await?? {
+    Resp3Frame::Array { mut data, .. } => {
+      if let Some(frame) = data.pop() {
+        protocol_utils::frame_to_results(frame)
+      } else {
+        Err(RedisError::new(
+          RedisErrorKind::Protocol,
+          "Missing library name response frame.",
+        ))
+      }
+    },
+    Resp3Frame::SimpleError { data, .. } => Err(protocol_utils::pretty_error(&data)),
+    Resp3Frame::BlobError { data, .. } => {
+      let parsed = str::from_utf8(&data)?;
+      Err(protocol_utils::pretty_error(parsed))
+    },
+    _ => Err(RedisError::new(RedisErrorKind::Protocol, "Invalid response type.")),
+  }
+}
+
+pub async fn function_restore<C: ClientLike>(
+  client: &C,
+  serialized: Bytes,
+  policy: FnPolicy,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2);
+    args.push(serialized.into());
+    args.push(policy.to_str().into());
+
+    Ok((RedisCommandKind::FunctionRestore, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn function_restore_cluster<C: ClientLike>(
+  client: &C,
+  serialized: Bytes,
+  policy: FnPolicy,
+) -> Result<(), RedisError> {
+  if !client.inner().config.server.is_clustered() {
+    return function_restore(client, serialized, policy).await.map(|_| ());
+  }
+
+  let (tx, rx) = oneshot_channel();
+  let args: Vec<RedisValue> = vec![serialized.into(), policy.to_str().into()];
+
+  let response = ResponseKind::new_buffer(tx);
+  let mut command: RedisCommand = (RedisCommandKind::_FunctionRestoreCluster, args, response).into();
+
+  let timeout_dur = utils::prepare_command(client, &mut command);
+  client.send_command(command)?;
+  let _ = utils::timeout(rx, timeout_dur).await??;
+  Ok(())
+}
+
+pub async fn function_stats<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  let inner = client.inner();
+  let command = RedisCommand::new(RedisCommandKind::FunctionStats, vec![]);
+
+  let frame = utils::backchannel_request_response(inner, command, true).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+value_cmd!(function_dump, FunctionDump);
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/memory.rs.html b/doc/src/fred/commands/impls/memory.rs.html new file mode 100644 index 00000000..4d17f59d --- /dev/null +++ b/doc/src/fred/commands/impls/memory.rs.html @@ -0,0 +1,73 @@ +memory.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+
use super::*;
+use crate::{
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  types::*,
+  utils,
+};
+
+value_cmd!(memory_doctor, MemoryDoctor);
+value_cmd!(memory_malloc_stats, MemoryMallocStats);
+ok_cmd!(memory_purge, MemoryPurge);
+
+pub async fn memory_stats<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  let response = utils::request_response(client, || Ok((RedisCommandKind::MemoryStats, vec![]))).await?;
+  protocol_utils::frame_to_results(response)
+}
+
+pub async fn memory_usage<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  samples: Option<u32>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(3);
+    args.push(key.into());
+
+    if let Some(samples) = samples {
+      args.push(static_val!(SAMPLES));
+      args.push(samples.into());
+    }
+
+    Ok((RedisCommandKind::MemoryUsage, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/mod.rs.html b/doc/src/fred/commands/impls/mod.rs.html new file mode 100644 index 00000000..3f07588b --- /dev/null +++ b/doc/src/fred/commands/impls/mod.rs.html @@ -0,0 +1,423 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+
#![allow(unused_macros)]
+#![allow(dead_code)]
+
+use crate::{
+  error::RedisError,
+  interfaces::ClientLike,
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  types::RedisValue,
+  utils,
+};
+
+pub static MATCH: &str = "MATCH";
+pub static COUNT: &str = "COUNT";
+pub static TYPE: &str = "TYPE";
+#[cfg(any(feature = "i-geo", feature = "i-sorted-sets"))]
+pub static CHANGED: &str = "CH";
+#[cfg(any(feature = "i-lists", feature = "i-sorted-sets", feature = "i-streams"))]
+pub static LIMIT: &str = "LIMIT";
+pub static GET: &str = "GET";
+pub static RESET: &str = "RESET";
+pub static TO: &str = "TO";
+pub static FORCE: &str = "FORCE";
+pub static ABORT: &str = "ABORT";
+pub static TIMEOUT: &str = "TIMEOUT";
+pub static LEN: &str = "LEN";
+pub static DB: &str = "DB";
+pub static REPLACE: &str = "REPLACE";
+pub static ID: &str = "ID";
+pub static ANY: &str = "ANY";
+pub static STORE: &str = "STORE";
+pub static WITH_VALUES: &str = "WITHVALUES";
+pub static SYNC: &str = "SYNC";
+pub static ASYNC: &str = "ASYNC";
+pub static RANK: &str = "RANK";
+pub static MAXLEN: &str = "MAXLEN";
+pub static REV: &str = "REV";
+pub static ABSTTL: &str = "ABSTTL";
+pub static IDLE_TIME: &str = "IDLETIME";
+pub static FREQ: &str = "FREQ";
+pub static FULL: &str = "FULL";
+pub static NOMKSTREAM: &str = "NOMKSTREAM";
+pub static MINID: &str = "MINID";
+pub static BLOCK: &str = "BLOCK";
+pub static STREAMS: &str = "STREAMS";
+pub static MKSTREAM: &str = "MKSTREAM";
+pub static GROUP: &str = "GROUP";
+pub static NOACK: &str = "NOACK";
+pub static IDLE: &str = "IDLE";
+pub static TIME: &str = "TIME";
+pub static RETRYCOUNT: &str = "RETRYCOUNT";
+pub static JUSTID: &str = "JUSTID";
+pub static SAMPLES: &str = "SAMPLES";
+pub static LIBRARYNAME: &str = "LIBRARYNAME";
+pub static WITHCODE: &str = "WITHCODE";
+pub static IDX: &str = "IDX";
+pub static MINMATCHLEN: &str = "MINMATCHLEN";
+pub static WITHMATCHLEN: &str = "WITHMATCHLEN";
+
+/// Macro to generate a command function that takes no arguments and expects an OK response - returning `()` to the
+/// caller.
+macro_rules! ok_cmd(
+  ($name:ident, $cmd:tt) => {
+    pub async fn $name<C: ClientLike>(client: &C) -> Result<(), RedisError> {
+      let frame = crate::utils::request_response(client, || Ok((RedisCommandKind::$cmd, vec![]))).await?;
+      let response = crate::protocol::utils::frame_to_results(frame)?;
+      crate::protocol::utils::expect_ok(&response)
+    }
+  }
+);
+
+// TODO clean this up
+/// Macro to generate a command function that takes no arguments and returns a single `RedisValue` to the caller.
+macro_rules! simple_cmd(
+  ($name:ident, $cmd:tt, $res:ty) => {
+    pub async fn $name<C: ClientLike>(client: &C) -> Result<$res, RedisError> {
+      let frame = crate::utils::request_response(client, || Ok((RedisCommandKind::$cmd, vec![]))).await?;
+      crate::protocol::utils::frame_to_results(frame)
+    }
+  }
+);
+
+/// Macro to generate a command function that takes no arguments and returns a single `RedisValue` to the caller.
+macro_rules! value_cmd(
+  ($name:ident, $cmd:tt) => {
+    simple_cmd!($name, $cmd, RedisValue);
+  }
+);
+
+/// Macro to generate a command function that takes no arguments and returns a potentially nested `RedisValue` to the
+/// caller.
+macro_rules! values_cmd(
+  ($name:ident, $cmd:tt) => {
+    pub async fn $name<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+      let frame = crate::utils::request_response(client, || Ok((RedisCommandKind::$cmd, vec![]))).await?;
+      crate::protocol::utils::frame_to_results(frame)
+    }
+  }
+);
+
+/// A function that issues a command that only takes one argument and returns a single `RedisValue`.
+pub async fn one_arg_value_cmd<C: ClientLike>(
+  client: &C,
+  kind: RedisCommandKind,
+  arg: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || Ok((kind, vec![arg]))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+/// A function that issues a command that only takes one argument and returns a potentially nested `RedisValue`.
+pub async fn one_arg_values_cmd<C: ClientLike>(
+  client: &C,
+  kind: RedisCommandKind,
+  arg: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || Ok((kind, vec![arg]))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+/// A function that issues a command that only takes one argument and expects an OK response - returning `()` to the
+/// caller.
+pub async fn one_arg_ok_cmd<C: ClientLike>(
+  client: &C,
+  kind: RedisCommandKind,
+  arg: RedisValue,
+) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, move || Ok((kind, vec![arg]))).await?;
+
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+/// A function that issues a command that takes any number of arguments and returns a single `RedisValue` to the
+/// caller.
+pub async fn args_value_cmd<C: ClientLike>(
+  client: &C,
+  kind: RedisCommandKind,
+  args: Vec<RedisValue>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || Ok((kind, args))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+/// A function that issues a command that takes any number of arguments and returns a potentially nested `RedisValue`
+/// to the caller.
+pub async fn args_values_cmd<C: ClientLike>(
+  client: &C,
+  kind: RedisCommandKind,
+  args: Vec<RedisValue>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || Ok((kind, args))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+/// A function that issues a command that takes any number of arguments and expects an OK response - returning `()` to
+/// the caller.
+pub async fn args_ok_cmd<C: ClientLike>(
+  client: &C,
+  kind: RedisCommandKind,
+  args: Vec<RedisValue>,
+) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, move || Ok((kind, args))).await?;
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+#[cfg(feature = "i-acl")]
+pub mod acl;
+#[cfg(feature = "i-client")]
+pub mod client;
+#[cfg(feature = "i-cluster")]
+pub mod cluster;
+#[cfg(feature = "i-config")]
+pub mod config;
+#[cfg(feature = "i-geo")]
+pub mod geo;
+#[cfg(feature = "i-hashes")]
+pub mod hashes;
+#[cfg(feature = "i-hyperloglog")]
+pub mod hyperloglog;
+#[cfg(feature = "i-keys")]
+pub mod keys;
+#[cfg(feature = "i-lists")]
+pub mod lists;
+#[cfg(feature = "i-scripts")]
+pub mod lua;
+#[cfg(feature = "i-memory")]
+pub mod memory;
+#[cfg(feature = "i-pubsub")]
+pub mod pubsub;
+#[cfg(feature = "i-redis-json")]
+pub mod redis_json;
+#[cfg(feature = "i-redisearch")]
+pub mod redisearch;
+pub mod scan;
+#[cfg(feature = "sentinel-client")]
+pub mod sentinel;
+pub mod server;
+#[cfg(feature = "i-sets")]
+pub mod sets;
+#[cfg(feature = "i-slowlog")]
+pub mod slowlog;
+#[cfg(feature = "i-sorted-sets")]
+pub mod sorted_sets;
+#[cfg(feature = "i-streams")]
+pub mod streams;
+pub mod strings;
+#[cfg(feature = "i-time-series")]
+pub mod timeseries;
+#[cfg(feature = "i-tracking")]
+pub mod tracking;
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/pubsub.rs.html b/doc/src/fred/commands/impls/pubsub.rs.html new file mode 100644 index 00000000..b577667a --- /dev/null +++ b/doc/src/fred/commands/impls/pubsub.rs.html @@ -0,0 +1,341 @@ +pubsub.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+
use super::*;
+use crate::{
+  protocol::{
+    command::{RedisCommand, RedisCommandKind},
+    utils as protocol_utils,
+  },
+  types::*,
+  utils,
+};
+use bytes_utils::Str;
+use redis_protocol::redis_keyslot;
+
+fn cluster_hash_legacy_command<C: ClientLike>(client: &C, command: &mut RedisCommand) {
+  if client.is_clustered() {
+    // send legacy (non-sharded) pubsub commands to the same node in a cluster so that `UNSUBSCRIBE` (without args)
+    // works correctly. otherwise we'd have to send `UNSUBSCRIBE` to every node.
+    let hash_slot = redis_keyslot(client.id().as_bytes());
+    command.hasher = ClusterHash::Custom(hash_slot);
+  }
+}
+
+pub async fn subscribe<C: ClientLike>(client: &C, channels: MultipleStrings) -> Result<(), RedisError> {
+  let args = channels.inner().into_iter().map(|c| c.into()).collect();
+  let mut command = RedisCommand::new(RedisCommandKind::Subscribe, args);
+  cluster_hash_legacy_command(client, &mut command);
+
+  let frame = utils::request_response(client, move || Ok(command)).await?;
+  protocol_utils::frame_to_results(frame).map(|_| ())
+}
+
+pub async fn unsubscribe<C: ClientLike>(client: &C, channels: MultipleStrings) -> Result<(), RedisError> {
+  let args = channels.inner().into_iter().map(|c| c.into()).collect();
+  let mut command = RedisCommand::new(RedisCommandKind::Unsubscribe, args);
+  cluster_hash_legacy_command(client, &mut command);
+
+  let frame = utils::request_response(client, move || Ok(command)).await?;
+  protocol_utils::frame_to_results(frame).map(|_| ())
+}
+
+pub async fn publish<C: ClientLike>(client: &C, channel: Str, message: RedisValue) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Publish, vec![channel.into(), message]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn psubscribe<C: ClientLike>(client: &C, patterns: MultipleStrings) -> Result<(), RedisError> {
+  let args = patterns.inner().into_iter().map(|c| c.into()).collect();
+  let mut command = RedisCommand::new(RedisCommandKind::Psubscribe, args);
+  cluster_hash_legacy_command(client, &mut command);
+
+  let frame = utils::request_response(client, move || Ok(command)).await?;
+  protocol_utils::frame_to_results(frame).map(|_| ())
+}
+
+pub async fn punsubscribe<C: ClientLike>(client: &C, patterns: MultipleStrings) -> Result<(), RedisError> {
+  let args = patterns.inner().into_iter().map(|c| c.into()).collect();
+  let mut command = RedisCommand::new(RedisCommandKind::Punsubscribe, args);
+  cluster_hash_legacy_command(client, &mut command);
+
+  let frame = utils::request_response(client, move || Ok(command)).await?;
+  protocol_utils::frame_to_results(frame).map(|_| ())
+}
+
+pub async fn spublish<C: ClientLike>(
+  client: &C,
+  channel: Str,
+  message: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut command: RedisCommand = (RedisCommandKind::Spublish, vec![channel.into(), message]).into();
+    command.hasher = ClusterHash::FirstKey;
+
+    Ok(command)
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ssubscribe<C: ClientLike>(client: &C, channels: MultipleStrings) -> Result<(), RedisError> {
+  let args = channels.inner().into_iter().map(|c| c.into()).collect();
+  let mut command = RedisCommand::new(RedisCommandKind::Ssubscribe, args);
+  command.hasher = ClusterHash::FirstKey;
+
+  let frame = utils::request_response(client, move || Ok(command)).await?;
+  protocol_utils::frame_to_results(frame).map(|_| ())
+}
+
+pub async fn sunsubscribe<C: ClientLike>(client: &C, channels: MultipleStrings) -> Result<(), RedisError> {
+  let args = channels.inner().into_iter().map(|c| c.into()).collect();
+  let mut command = RedisCommand::new(RedisCommandKind::Sunsubscribe, args);
+  command.hasher = ClusterHash::FirstKey;
+
+  let frame = utils::request_response(client, move || Ok(command)).await?;
+  protocol_utils::frame_to_results(frame).map(|_| ())
+}
+
+pub async fn pubsub_channels<C: ClientLike>(client: &C, pattern: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, || {
+    let args = if pattern.is_empty() {
+      vec![]
+    } else {
+      vec![pattern.into()]
+    };
+
+    let mut command: RedisCommand = RedisCommand::new(RedisCommandKind::PubsubChannels, args);
+    cluster_hash_legacy_command(client, &mut command);
+
+    Ok(command)
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn pubsub_numpat<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, || {
+    let mut command: RedisCommand = RedisCommand::new(RedisCommandKind::PubsubNumpat, vec![]);
+    cluster_hash_legacy_command(client, &mut command);
+
+    Ok(command)
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn pubsub_numsub<C: ClientLike>(client: &C, channels: MultipleStrings) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, || {
+    let args: Vec<RedisValue> = channels.inner().into_iter().map(|s| s.into()).collect();
+    let mut command: RedisCommand = RedisCommand::new(RedisCommandKind::PubsubNumsub, args);
+    cluster_hash_legacy_command(client, &mut command);
+
+    Ok(command)
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn pubsub_shardchannels<C: ClientLike>(client: &C, pattern: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, || {
+    Ok((RedisCommandKind::PubsubShardchannels, vec![pattern.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn pubsub_shardnumsub<C: ClientLike>(
+  client: &C,
+  channels: MultipleStrings,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, || {
+    let args: Vec<RedisValue> = channels.inner().into_iter().map(|s| s.into()).collect();
+    let has_args = !args.is_empty();
+    let mut command: RedisCommand = RedisCommand::new(RedisCommandKind::PubsubShardnumsub, args);
+    if !has_args {
+      cluster_hash_legacy_command(client, &mut command);
+    }
+
+    Ok(command)
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/redis_json.rs.html b/doc/src/fred/commands/impls/redis_json.rs.html new file mode 100644 index 00000000..cbd421b8 --- /dev/null +++ b/doc/src/fred/commands/impls/redis_json.rs.html @@ -0,0 +1,717 @@ +redis_json.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  interfaces::{ClientLike, RedisResult},
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  types::{MultipleKeys, MultipleStrings, RedisKey, RedisValue, SetOptions},
+  utils,
+};
+use bytes_utils::Str;
+use serde_json::Value;
+
+const INDENT: &str = "INDENT";
+const NEWLINE: &str = "NEWLINE";
+const SPACE: &str = "SPACE";
+
+fn key_path_args(key: RedisKey, path: Option<Str>, extra: usize) -> Vec<RedisValue> {
+  let mut out = Vec::with_capacity(2 + extra);
+  out.push(key.into());
+  if let Some(path) = path {
+    out.push(path.into());
+  }
+  out
+}
+
+/// Convert the provided json value to a redis value by serializing into a json string.
+fn value_to_bulk_str(value: &Value) -> Result<RedisValue, RedisError> {
+  Ok(match value {
+    Value::String(ref s) => RedisValue::String(Str::from(s)),
+    _ => RedisValue::String(Str::from(serde_json::to_string(value)?)),
+  })
+}
+
+/// Convert the provided json value to a redis value directly without serializing into a string. This only works with
+/// scalar values.
+fn json_to_redis(value: Value) -> Result<RedisValue, RedisError> {
+  let out = match value {
+    Value::String(s) => Some(RedisValue::String(Str::from(s))),
+    Value::Null => Some(RedisValue::Null),
+    Value::Number(n) => {
+      if n.is_f64() {
+        n.as_f64().map(RedisValue::Double)
+      } else {
+        n.as_i64().map(RedisValue::Integer)
+      }
+    },
+    Value::Bool(b) => Some(RedisValue::Boolean(b)),
+    _ => None,
+  };
+
+  out.ok_or(RedisError::new(
+    RedisErrorKind::InvalidArgument,
+    "Expected string or number.",
+  ))
+}
+
+fn values_to_bulk(values: &[Value]) -> Result<Vec<RedisValue>, RedisError> {
+  values.iter().map(value_to_bulk_str).collect()
+}
+
+pub async fn json_arrappend<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  path: Str,
+  values: Vec<Value>,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    let mut args = key_path_args(key, Some(path), values.len());
+    args.extend(values_to_bulk(&values)?);
+
+    Ok((RedisCommandKind::JsonArrAppend, args))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_arrindex<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  path: Str,
+  value: Value,
+  start: Option<i64>,
+  stop: Option<i64>,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    let mut args = Vec::with_capacity(5);
+    args.extend([key.into(), path.into(), value_to_bulk_str(&value)?]);
+    if let Some(start) = start {
+      args.push(start.into());
+    }
+    if let Some(stop) = stop {
+      args.push(stop.into());
+    }
+
+    Ok((RedisCommandKind::JsonArrIndex, args))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_arrinsert<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  path: Str,
+  index: i64,
+  values: Vec<Value>,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    let mut args = Vec::with_capacity(3 + values.len());
+    args.extend([key.into(), path.into(), index.into()]);
+    args.extend(values_to_bulk(&values)?);
+
+    Ok((RedisCommandKind::JsonArrInsert, args))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_arrlen<C: ClientLike>(client: &C, key: RedisKey, path: Option<Str>) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    Ok((RedisCommandKind::JsonArrLen, key_path_args(key, path, 0)))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_arrpop<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  path: Option<Str>,
+  index: Option<i64>,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    let mut args = key_path_args(key, path, 1);
+    if let Some(index) = index {
+      args.push(index.into());
+    }
+
+    Ok((RedisCommandKind::JsonArrPop, args))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_arrtrim<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  path: Str,
+  start: i64,
+  stop: i64,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    Ok((RedisCommandKind::JsonArrTrim, vec![
+      key.into(),
+      path.into(),
+      start.into(),
+      stop.into(),
+    ]))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_clear<C: ClientLike>(client: &C, key: RedisKey, path: Option<Str>) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    Ok((RedisCommandKind::JsonClear, key_path_args(key, path, 0)))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_debug_memory<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  path: Option<Str>,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    Ok((RedisCommandKind::JsonDebugMemory, key_path_args(key, path, 0)))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_del<C: ClientLike>(client: &C, key: RedisKey, path: Str) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    Ok((RedisCommandKind::JsonDel, key_path_args(key, Some(path), 0)))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_get<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  indent: Option<Str>,
+  newline: Option<Str>,
+  space: Option<Str>,
+  paths: MultipleStrings,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    let mut args = Vec::with_capacity(7 + paths.len());
+    args.push(key.into());
+    if let Some(indent) = indent {
+      args.push(static_val!(INDENT));
+      args.push(indent.into());
+    }
+    if let Some(newline) = newline {
+      args.push(static_val!(NEWLINE));
+      args.push(newline.into());
+    }
+    if let Some(space) = space {
+      args.push(static_val!(SPACE));
+      args.push(space.into());
+    }
+    args.extend(paths.into_values());
+
+    Ok((RedisCommandKind::JsonGet, args))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_merge<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  path: Str,
+  value: Value,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    Ok((RedisCommandKind::JsonMerge, vec![
+      key.into(),
+      path.into(),
+      value_to_bulk_str(&value)?,
+    ]))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_mget<C: ClientLike>(client: &C, keys: MultipleKeys, path: Str) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    let mut args = Vec::with_capacity(keys.len() + 1);
+    args.extend(keys.into_values());
+    args.push(path.into());
+
+    Ok((RedisCommandKind::JsonMGet, args))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_mset<C: ClientLike>(client: &C, values: Vec<(RedisKey, Str, Value)>) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    let mut args = Vec::with_capacity(values.len() * 3);
+    for (key, path, value) in values.into_iter() {
+      args.extend([key.into(), path.into(), value_to_bulk_str(&value)?]);
+    }
+
+    Ok((RedisCommandKind::JsonMSet, args))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_numincrby<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  path: Str,
+  value: Value,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    Ok((RedisCommandKind::JsonNumIncrBy, vec![
+      key.into(),
+      path.into(),
+      json_to_redis(value)?,
+    ]))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_objkeys<C: ClientLike>(client: &C, key: RedisKey, path: Option<Str>) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    Ok((RedisCommandKind::JsonObjKeys, key_path_args(key, path, 0)))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_objlen<C: ClientLike>(client: &C, key: RedisKey, path: Option<Str>) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    Ok((RedisCommandKind::JsonObjLen, key_path_args(key, path, 0)))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_resp<C: ClientLike>(client: &C, key: RedisKey, path: Option<Str>) -> RedisResult<RedisValue> {
+  let frame =
+    utils::request_response(client, || Ok((RedisCommandKind::JsonResp, key_path_args(key, path, 0)))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_set<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  path: Str,
+  value: Value,
+  options: Option<SetOptions>,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    let mut args = key_path_args(key, Some(path), 2);
+    args.push(value_to_bulk_str(&value)?);
+    if let Some(options) = options {
+      args.push(options.to_str().into());
+    }
+
+    Ok((RedisCommandKind::JsonSet, args))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_strappend<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  path: Option<Str>,
+  value: Value,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    let mut args = key_path_args(key, path, 1);
+    args.push(value_to_bulk_str(&value)?);
+
+    Ok((RedisCommandKind::JsonStrAppend, args))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_strlen<C: ClientLike>(client: &C, key: RedisKey, path: Option<Str>) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    Ok((RedisCommandKind::JsonStrLen, key_path_args(key, path, 0)))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_toggle<C: ClientLike>(client: &C, key: RedisKey, path: Str) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, || {
+    Ok((RedisCommandKind::JsonToggle, key_path_args(key, Some(path), 0)))
+  })
+  .await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn json_type<C: ClientLike>(client: &C, key: RedisKey, path: Option<Str>) -> RedisResult<RedisValue> {
+  let frame =
+    utils::request_response(client, || Ok((RedisCommandKind::JsonType, key_path_args(key, path, 0)))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/redisearch.rs.html b/doc/src/fred/commands/impls/redisearch.rs.html new file mode 100644 index 00000000..1768d68e --- /dev/null +++ b/doc/src/fred/commands/impls/redisearch.rs.html @@ -0,0 +1,1737 @@ +redisearch.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+
use crate::{
+  commands::{args_values_cmd, one_arg_values_cmd, COUNT, LEN, LIMIT},
+  error::RedisError,
+  interfaces::ClientLike,
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  types::{
+    AggregateOperation,
+    FtAggregateOptions,
+    FtAlterOptions,
+    FtCreateOptions,
+    FtSearchOptions,
+    Load,
+    MultipleStrings,
+    RedisKey,
+    RedisValue,
+    SearchSchema,
+    SearchSchemaKind,
+    SpellcheckTerms,
+  },
+  utils,
+};
+use bytes::Bytes;
+use bytes_utils::Str;
+
+static DD: &str = "DD";
+static DIALECT: &str = "DIALECT";
+static DISTANCE: &str = "DISTANCE";
+static INCLUDE: &str = "INCLUDE";
+static EXCLUDE: &str = "EXCLUDE";
+static TERMS: &str = "TERMS";
+static INCR: &str = "INCR";
+static PAYLOAD: &str = "PAYLOAD";
+static FUZZY: &str = "FUZZY";
+static WITHSCORES: &str = "WITHSCORES";
+static WITHPAYLOADS: &str = "WITHPAYLOADS";
+static MAX: &str = "MAX";
+static SKIPINITIALSCAN: &str = "SKIPINITIALSCAN";
+static NOCONTENT: &str = "NOCONTENT";
+static VERBATIM: &str = "VERBATIM";
+static NOSTOPWORDS: &str = "NOSTOPWORDS";
+static WITHSORTKEYS: &str = "WITHSORTKEYS";
+static FILTER: &str = "FILTER";
+static GEOFILTER: &str = "GEOFILTER";
+static INKEYS: &str = "INKEYS";
+static INFIELDS: &str = "INFIELDS";
+static _RETURN: &str = "RETURN";
+static AS: &str = "AS";
+static SUMMARIZE: &str = "SUMMARIZE";
+static FIELDS: &str = "FIELDS";
+static FRAGS: &str = "FRAGS";
+static SEPARATOR: &str = "SEPARATOR";
+static HIGHLIGHT: &str = "HIGHLIGHT";
+static TAGS: &str = "TAGS";
+static SLOP: &str = "SLOP";
+static TIMEOUT: &str = "TIMEOUT";
+static INORDER: &str = "INORDER";
+static LANGUAGE: &str = "LANGUAGE";
+static EXPANDER: &str = "EXPANDER";
+static SCORER: &str = "SCORER";
+static EXPLAINSCORE: &str = "EXPLAINSCORE";
+static SORTBY: &str = "SORTBY";
+static PARAMS: &str = "PARAMS";
+static WITHCOUNT: &str = "WITHCOUNT";
+static LOAD: &str = "LOAD";
+static WITHCURSOR: &str = "WITHCURSOR";
+static MAXIDLE: &str = "MAXIDLE";
+static APPLY: &str = "APPLY";
+static GROUPBY: &str = "GROUPBY";
+static REDUCE: &str = "REDUCE";
+static ON: &str = "ON";
+static HASH: &str = "HASH";
+static JSON: &str = "JSON";
+static PREFIX: &str = "PREFIX";
+static LANGUAGE_FIELD: &str = "LANGUAGE_FIELD";
+static SCORE: &str = "SCORE";
+static SCORE_FIELD: &str = "SCORE_FIELD";
+static PAYLOAD_FIELD: &str = "PAYLOAD_FIELD";
+static MAXTEXTFIELDS: &str = "MAXTEXTFIELDS";
+static TEMPORARY: &str = "TEMPORARY";
+static NOOFFSETS: &str = "NOOFFSETS";
+static NOHL: &str = "NOHL";
+static NOFIELDS: &str = "NOFIELDS";
+static NOFREQS: &str = "NOFREQS";
+static STOPWORDS: &str = "STOPWORDS";
+static SCHEMA: &str = "SCHEMA";
+static ADD: &str = "ADD";
+static SORTABLE: &str = "SORTABLE";
+static UNF: &str = "UNF";
+static NOINDEX: &str = "NOINDEX";
+static NOSTEM: &str = "NOSTEM";
+static PHONETIC: &str = "PHONETIC";
+static WEIGHT: &str = "WEIGHT";
+static CASESENSITIVE: &str = "CASESENSITIVE";
+static WITHSUFFIXTRIE: &str = "WITHSUFFIXTRIE";
+static TEXT: &str = "TEXT";
+static TAG: &str = "TAG";
+static NUMERIC: &str = "NUMERIC";
+static GEO: &str = "GEO";
+static VECTOR: &str = "VECTOR";
+static GEOSHAPE: &str = "GEOSHAPE";
+
+fn gen_aggregate_op(args: &mut Vec<RedisValue>, operation: AggregateOperation) -> Result<(), RedisError> {
+  match operation {
+    AggregateOperation::Filter { expression } => {
+      args.extend([static_val!(FILTER), expression.into()]);
+    },
+    AggregateOperation::Limit { offset, num } => {
+      args.extend([static_val!(LIMIT), offset.try_into()?, num.try_into()?]);
+    },
+    AggregateOperation::Apply { expression, name } => {
+      args.extend([static_val!(APPLY), expression.into(), static_val!(AS), name.into()]);
+    },
+    AggregateOperation::SortBy { properties, max } => {
+      args.extend([static_val!(SORTBY), (properties.len() * 2).try_into()?]);
+      for (property, order) in properties.into_iter() {
+        args.extend([property.into(), order.to_str().into()]);
+      }
+      if let Some(max) = max {
+        args.extend([static_val!(MAX), max.try_into()?]);
+      }
+    },
+    AggregateOperation::GroupBy { fields, reducers } => {
+      args.extend([static_val!(GROUPBY), fields.len().try_into()?]);
+      args.extend(fields.into_iter().map(|f| f.into()));
+
+      for reducer in reducers.into_iter() {
+        args.extend([
+          static_val!(REDUCE),
+          static_val!(reducer.func.to_str()),
+          reducer.args.len().try_into()?,
+        ]);
+        args.extend(reducer.args.into_iter().map(|a| a.into()));
+        if let Some(name) = reducer.name {
+          args.extend([static_val!(AS), name.into()]);
+        }
+      }
+    },
+  };
+
+  Ok(())
+}
+
+fn gen_aggregate_options(args: &mut Vec<RedisValue>, options: FtAggregateOptions) -> Result<(), RedisError> {
+  if options.verbatim {
+    args.push(static_val!(VERBATIM));
+  }
+  if let Some(load) = options.load {
+    match load {
+      Load::All => {
+        args.extend([static_val!(LOAD), static_val!("*")]);
+      },
+      Load::Some(fields) => {
+        if !fields.is_empty() {
+          args.extend([static_val!(LOAD), fields.len().try_into()?]);
+          for field in fields.into_iter() {
+            args.push(field.identifier.into());
+            if let Some(property) = field.property {
+              args.extend([static_val!(AS), property.into()]);
+            }
+          }
+        }
+      },
+    }
+  }
+  if let Some(timeout) = options.timeout {
+    args.extend([static_val!(TIMEOUT), timeout.into()]);
+  }
+  for operation in options.pipeline.into_iter() {
+    gen_aggregate_op(args, operation)?;
+  }
+  if let Some(cursor) = options.cursor {
+    args.push(static_val!(WITHCURSOR));
+    if let Some(count) = cursor.count {
+      args.extend([static_val!(COUNT), count.try_into()?]);
+    }
+    if let Some(idle) = cursor.max_idle {
+      args.extend([static_val!(MAXIDLE), idle.try_into()?]);
+    }
+  }
+  if !options.params.is_empty() {
+    args.extend([static_val!(PARAMS), options.params.len().try_into()?]);
+    for param in options.params.into_iter() {
+      args.extend([param.name.into(), param.value.into()]);
+    }
+  }
+  if let Some(dialect) = options.dialect {
+    args.extend([static_val!(DIALECT), dialect.into()]);
+  }
+
+  Ok(())
+}
+
+fn gen_search_options(args: &mut Vec<RedisValue>, options: FtSearchOptions) -> Result<(), RedisError> {
+  if options.nocontent {
+    args.push(static_val!(NOCONTENT));
+  }
+  if options.verbatim {
+    args.push(static_val!(VERBATIM));
+  }
+  if options.nostopwords {
+    args.push(static_val!(NOSTOPWORDS));
+  }
+  if options.withscores {
+    args.push(static_val!(WITHSCORES));
+  }
+  if options.withpayloads {
+    args.push(static_val!(WITHPAYLOADS));
+  }
+  if options.withsortkeys {
+    args.push(static_val!(WITHSORTKEYS));
+  }
+  for filter in options.filters.into_iter() {
+    args.extend([
+      static_val!(FILTER),
+      filter.attribute.into(),
+      filter.min.into_value()?,
+      filter.max.into_value()?,
+    ]);
+  }
+  for geo_filter in options.geofilters.into_iter() {
+    args.extend([
+      static_val!(GEOFILTER),
+      geo_filter.attribute.into(),
+      geo_filter.position.longitude.try_into()?,
+      geo_filter.position.latitude.try_into()?,
+      geo_filter.radius,
+      geo_filter.units.to_str().into(),
+    ]);
+  }
+  if !options.inkeys.is_empty() {
+    args.push(static_val!(INKEYS));
+    args.push(options.inkeys.len().try_into()?);
+    args.extend(options.inkeys.into_iter().map(|k| k.into()));
+  }
+  if !options.infields.is_empty() {
+    args.push(static_val!(INFIELDS));
+    args.push(options.infields.len().try_into()?);
+    args.extend(options.infields.into_iter().map(|s| s.into()));
+  }
+  if !options.r#return.is_empty() {
+    args.extend([static_val!(_RETURN), options.r#return.len().try_into()?]);
+    for field in options.r#return.into_iter() {
+      args.push(field.identifier.into());
+      if let Some(property) = field.property {
+        args.push(static_val!(AS));
+        args.push(property.into());
+      }
+    }
+  }
+  if let Some(summarize) = options.summarize {
+    args.push(static_val!(SUMMARIZE));
+    if !summarize.fields.is_empty() {
+      args.push(static_val!(FIELDS));
+      args.push(summarize.fields.len().try_into()?);
+      args.extend(summarize.fields.into_iter().map(|s| s.into()));
+    }
+    if let Some(frags) = summarize.frags {
+      args.push(static_val!(FRAGS));
+      args.push(frags.try_into()?);
+    }
+    if let Some(len) = summarize.len {
+      args.push(static_val!(LEN));
+      args.push(len.try_into()?);
+    }
+    if let Some(separator) = summarize.separator {
+      args.push(static_val!(SEPARATOR));
+      args.push(separator.into());
+    }
+  }
+  if let Some(highlight) = options.highlight {
+    args.push(static_val!(HIGHLIGHT));
+    if !highlight.fields.is_empty() {
+      args.push(static_val!(FIELDS));
+      args.push(highlight.fields.len().try_into()?);
+      args.extend(highlight.fields.into_iter().map(|s| s.into()));
+    }
+    if let Some((open, close)) = highlight.tags {
+      args.extend([static_val!(TAGS), open.into(), close.into()]);
+    }
+  }
+  if let Some(slop) = options.slop {
+    args.extend([static_val!(SLOP), slop.into()]);
+  }
+  if let Some(timeout) = options.timeout {
+    args.extend([static_val!(TIMEOUT), timeout.into()]);
+  }
+  if options.inorder {
+    args.push(static_val!(INORDER));
+  }
+  if let Some(language) = options.language {
+    args.extend([static_val!(LANGUAGE), language.into()]);
+  }
+  if let Some(expander) = options.expander {
+    args.extend([static_val!(EXPANDER), expander.into()]);
+  }
+  if let Some(scorer) = options.scorer {
+    args.extend([static_val!(SCORER), scorer.into()]);
+  }
+  if options.explainscore {
+    args.push(static_val!(EXPLAINSCORE));
+  }
+  if let Some(payload) = options.payload {
+    args.extend([static_val!(PAYLOAD), RedisValue::Bytes(payload)]);
+  }
+  if let Some(sort) = options.sortby {
+    args.push(static_val!(SORTBY));
+    args.push(sort.attribute.into());
+    if let Some(order) = sort.order {
+      args.push(order.to_str().into());
+    }
+    if sort.withcount {
+      args.push(static_val!(WITHCOUNT));
+    }
+  }
+  if let Some((offset, count)) = options.limit {
+    args.extend([static_val!(LIMIT), offset.into(), count.into()]);
+  }
+  if !options.params.is_empty() {
+    args.push(static_val!(PARAMS));
+    args.push(options.params.len().try_into()?);
+    for param in options.params.into_iter() {
+      args.extend([param.name.into(), param.value.into()]);
+    }
+  }
+  if let Some(dialect) = options.dialect {
+    args.extend([static_val!(DIALECT), dialect.into()]);
+  }
+
+  Ok(())
+}
+
+fn gen_schema_kind(args: &mut Vec<RedisValue>, kind: SearchSchemaKind) -> Result<(), RedisError> {
+  match kind {
+    SearchSchemaKind::Custom { name, arguments } => {
+      args.push(name.into());
+      args.extend(arguments);
+    },
+    SearchSchemaKind::GeoShape { noindex } => {
+      args.push(static_val!(GEOSHAPE));
+      if noindex {
+        args.push(static_val!(NOINDEX));
+      }
+    },
+    SearchSchemaKind::Vector { noindex } => {
+      args.push(static_val!(VECTOR));
+      if noindex {
+        args.push(static_val!(NOINDEX));
+      }
+    },
+    SearchSchemaKind::Geo { sortable, unf, noindex } => {
+      args.push(static_val!(GEO));
+      if sortable {
+        args.push(static_val!(SORTABLE));
+      }
+      if unf {
+        args.push(static_val!(UNF));
+      }
+      if noindex {
+        args.push(static_val!(NOINDEX));
+      }
+    },
+    SearchSchemaKind::Numeric { sortable, unf, noindex } => {
+      args.push(static_val!(NUMERIC));
+      if sortable {
+        args.push(static_val!(SORTABLE));
+      }
+      if unf {
+        args.push(static_val!(UNF));
+      }
+      if noindex {
+        args.push(static_val!(NOINDEX));
+      }
+    },
+    SearchSchemaKind::Tag {
+      sortable,
+      unf,
+      separator,
+      casesensitive,
+      withsuffixtrie,
+      noindex,
+    } => {
+      args.push(static_val!(TAG));
+      if sortable {
+        args.push(static_val!(SORTABLE));
+      }
+      if unf {
+        args.push(static_val!(UNF));
+      }
+      if let Some(separator) = separator {
+        args.extend([static_val!(SEPARATOR), separator.to_string().into()]);
+      }
+      if casesensitive {
+        args.push(static_val!(CASESENSITIVE));
+      }
+      if withsuffixtrie {
+        args.push(static_val!(WITHSUFFIXTRIE));
+      }
+      if noindex {
+        args.push(static_val!(NOINDEX));
+      }
+    },
+    SearchSchemaKind::Text {
+      sortable,
+      unf,
+      nostem,
+      phonetic,
+      weight,
+      withsuffixtrie,
+      noindex,
+    } => {
+      args.push(static_val!(TEXT));
+      if sortable {
+        args.push(static_val!(SORTABLE));
+      }
+      if unf {
+        args.push(static_val!(UNF));
+      }
+      if nostem {
+        args.push(static_val!(NOSTEM));
+      }
+      if let Some(matcher) = phonetic {
+        args.extend([static_val!(PHONETIC), matcher.into()]);
+      }
+      if let Some(weight) = weight {
+        args.extend([static_val!(WEIGHT), weight.into()]);
+      }
+      if withsuffixtrie {
+        args.push(static_val!(WITHSUFFIXTRIE));
+      }
+      if noindex {
+        args.push(static_val!(NOINDEX));
+      }
+    },
+  };
+
+  Ok(())
+}
+
+fn gen_alter_options(args: &mut Vec<RedisValue>, options: FtAlterOptions) -> Result<(), RedisError> {
+  if options.skipinitialscan {
+    args.push(static_val!(SKIPINITIALSCAN));
+  }
+  args.extend([static_val!(SCHEMA), static_val!(ADD), options.attribute.into()]);
+  gen_schema_kind(args, options.options)?;
+
+  Ok(())
+}
+
+fn gen_create_options(args: &mut Vec<RedisValue>, options: FtCreateOptions) -> Result<(), RedisError> {
+  if let Some(kind) = options.on {
+    args.extend([static_val!(ON), kind.to_str().into()]);
+  }
+  if !options.prefixes.is_empty() {
+    args.extend([static_val!(PREFIX), options.prefixes.len().try_into()?]);
+    args.extend(options.prefixes.into_iter().map(|s| s.into()));
+  }
+  if let Some(filter) = options.filter {
+    args.extend([static_val!(FILTER), filter.into()]);
+  }
+  if let Some(language) = options.language {
+    args.extend([static_val!(LANGUAGE), language.into()]);
+  }
+  if let Some(language_field) = options.language_field {
+    args.extend([static_val!(LANGUAGE_FIELD), language_field.into()]);
+  }
+  if let Some(score) = options.score {
+    args.extend([static_val!(SCORE), score.try_into()?]);
+  }
+  if let Some(score_field) = options.score_field {
+    args.extend([static_val!(SCORE_FIELD), score_field.try_into()?]);
+  }
+  if let Some(payload_field) = options.payload_field {
+    args.extend([static_val!(PAYLOAD_FIELD), payload_field.into()]);
+  }
+  if options.maxtextfields {
+    args.push(static_val!(MAXTEXTFIELDS));
+  }
+  if let Some(temporary) = options.temporary {
+    args.extend([static_val!(TEMPORARY), temporary.try_into()?]);
+  }
+  if options.nooffsets {
+    args.push(static_val!(NOOFFSETS));
+  }
+  if options.nohl {
+    args.push(static_val!(NOHL));
+  }
+  if options.nofields {
+    args.push(static_val!(NOFIELDS));
+  }
+  if options.nofreqs {
+    args.push(static_val!(NOFREQS));
+  }
+  if !options.stopwords.is_empty() {
+    args.extend([static_val!(STOPWORDS), options.stopwords.len().try_into()?]);
+    args.extend(options.stopwords.into_iter().map(|s| s.into()));
+  }
+  if options.skipinitialscan {
+    args.push(static_val!(SKIPINITIALSCAN));
+  }
+
+  Ok(())
+}
+
+// does not include the prefix SCHEMA
+fn gen_schema_args(args: &mut Vec<RedisValue>, options: SearchSchema) -> Result<(), RedisError> {
+  args.push(options.field_name.into());
+  if let Some(alias) = options.alias {
+    args.extend([static_val!(AS), alias.into()]);
+  }
+  gen_schema_kind(args, options.kind)?;
+
+  Ok(())
+}
+
+pub async fn ft_list<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::FtList, vec![]).await
+}
+
+pub async fn ft_aggregate<C: ClientLike>(
+  client: &C,
+  index: Str,
+  query: Str,
+  options: FtAggregateOptions,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2 + options.num_args());
+    args.push(index.into());
+    args.push(query.into());
+    gen_aggregate_options(&mut args, options)?;
+
+    Ok((RedisCommandKind::FtAggregate, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ft_search<C: ClientLike>(
+  client: &C,
+  index: Str,
+  query: Str,
+  options: FtSearchOptions,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2 + options.num_args());
+    args.extend([index.into(), query.into()]);
+    gen_search_options(&mut args, options)?;
+
+    Ok((RedisCommandKind::FtSearch, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ft_create<C: ClientLike>(
+  client: &C,
+  index: Str,
+  options: FtCreateOptions,
+  schema: Vec<SearchSchema>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let schema_num_args = schema.iter().fold(0, |m, s| m + s.num_args());
+    let mut args = Vec::with_capacity(2 + options.num_args() + schema_num_args);
+    args.push(index.into());
+    gen_create_options(&mut args, options)?;
+
+    args.push(static_val!(SCHEMA));
+    for schema in schema.into_iter() {
+      gen_schema_args(&mut args, schema)?;
+    }
+
+    Ok((RedisCommandKind::FtCreate, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ft_alter<C: ClientLike>(
+  client: &C,
+  index: Str,
+  options: FtAlterOptions,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1 + options.num_args());
+    args.push(index.into());
+    gen_alter_options(&mut args, options)?;
+
+    Ok((RedisCommandKind::FtAlter, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ft_aliasadd<C: ClientLike>(client: &C, alias: Str, index: Str) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::FtAliasAdd, vec![alias.into(), index.into()]).await
+}
+
+pub async fn ft_aliasdel<C: ClientLike>(client: &C, alias: Str) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::FtAliasDel, vec![alias.into()]).await
+}
+
+pub async fn ft_aliasupdate<C: ClientLike>(client: &C, alias: Str, index: Str) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::FtAliasUpdate, vec![
+    alias.into(),
+    index.into(),
+  ])
+  .await
+}
+
+pub async fn ft_config_get<C: ClientLike>(client: &C, option: Str) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::FtConfigGet, vec![option.into()]).await
+}
+
+pub async fn ft_config_set<C: ClientLike>(
+  client: &C,
+  option: Str,
+  value: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::FtConfigSet, vec![option.into(), value]).await
+}
+
+pub async fn ft_cursor_del<C: ClientLike>(
+  client: &C,
+  index: Str,
+  cursor: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::FtCursorDel, vec![index.into(), cursor]).await
+}
+
+pub async fn ft_cursor_read<C: ClientLike>(
+  client: &C,
+  index: Str,
+  cursor: RedisValue,
+  count: Option<u64>,
+) -> Result<RedisValue, RedisError> {
+  let args = if let Some(count) = count {
+    vec![index.into(), cursor, static_val!(COUNT), count.try_into()?]
+  } else {
+    vec![index.into(), cursor]
+  };
+
+  args_values_cmd(client, RedisCommandKind::FtCursorRead, args).await
+}
+
+pub async fn ft_dictadd<C: ClientLike>(
+  client: &C,
+  dict: Str,
+  terms: MultipleStrings,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(terms.len() + 1);
+    args.push(dict.into());
+    for term in terms.inner().into_iter() {
+      args.push(term.into());
+    }
+
+    Ok((RedisCommandKind::FtDictAdd, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ft_dictdel<C: ClientLike>(
+  client: &C,
+  dict: Str,
+  terms: MultipleStrings,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(terms.len() + 1);
+    args.push(dict.into());
+    for term in terms.inner().into_iter() {
+      args.push(term.into());
+    }
+
+    Ok((RedisCommandKind::FtDictDel, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ft_dictdump<C: ClientLike>(client: &C, dict: Str) -> Result<RedisValue, RedisError> {
+  one_arg_values_cmd(client, RedisCommandKind::FtDictDump, dict.into()).await
+}
+
+pub async fn ft_dropindex<C: ClientLike>(client: &C, index: Str, dd: bool) -> Result<RedisValue, RedisError> {
+  let args = if dd {
+    vec![index.into(), static_val!(DD)]
+  } else {
+    vec![index.into()]
+  };
+
+  args_values_cmd(client, RedisCommandKind::FtDropIndex, args).await
+}
+
+pub async fn ft_explain<C: ClientLike>(
+  client: &C,
+  index: Str,
+  query: Str,
+  dialect: Option<i64>,
+) -> Result<RedisValue, RedisError> {
+  let args = if let Some(dialect) = dialect {
+    vec![index.into(), query.into(), static_val!(DIALECT), dialect.into()]
+  } else {
+    vec![index.into(), query.into()]
+  };
+
+  args_values_cmd(client, RedisCommandKind::FtExplain, args).await
+}
+
+pub async fn ft_info<C: ClientLike>(client: &C, index: Str) -> Result<RedisValue, RedisError> {
+  one_arg_values_cmd(client, RedisCommandKind::FtInfo, index.into()).await
+}
+
+pub async fn ft_spellcheck<C: ClientLike>(
+  client: &C,
+  index: Str,
+  query: Str,
+  distance: Option<u8>,
+  terms: Option<SpellcheckTerms>,
+  dialect: Option<i64>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let terms_len = terms.as_ref().map(|t| t.num_args()).unwrap_or(0);
+    let mut args = Vec::with_capacity(9 + terms_len);
+    args.push(index.into());
+    args.push(query.into());
+
+    if let Some(distance) = distance {
+      args.push(static_val!(DISTANCE));
+      args.push(distance.into());
+    }
+    if let Some(terms) = terms {
+      args.push(static_val!(TERMS));
+      let (dictionary, terms) = match terms {
+        SpellcheckTerms::Include { dictionary, terms } => {
+          args.push(static_val!(INCLUDE));
+          (dictionary, terms)
+        },
+        SpellcheckTerms::Exclude { dictionary, terms } => {
+          args.push(static_val!(EXCLUDE));
+          (dictionary, terms)
+        },
+      };
+
+      args.push(dictionary.into());
+      for term in terms.into_iter() {
+        args.push(term.into());
+      }
+    }
+    if let Some(dialect) = dialect {
+      args.extend([static_val!(DIALECT), dialect.into()]);
+    }
+
+    Ok((RedisCommandKind::FtSpellCheck, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ft_sugadd<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  string: Str,
+  score: f64,
+  incr: bool,
+  payload: Option<Bytes>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(6);
+    args.extend([key.into(), string.into(), score.try_into()?]);
+
+    if incr {
+      args.push(static_val!(INCR));
+    }
+    if let Some(payload) = payload {
+      args.extend([static_val!(PAYLOAD), RedisValue::Bytes(payload)]);
+    }
+
+    Ok((RedisCommandKind::FtSugAdd, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ft_sugdel<C: ClientLike>(client: &C, key: RedisKey, string: Str) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::FtSugDel, vec![key.into(), string.into()]).await
+}
+
+pub async fn ft_sugget<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  prefix: Str,
+  fuzzy: bool,
+  withscores: bool,
+  withpayloads: bool,
+  max: Option<u64>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(7);
+    args.push(key.into());
+    args.push(prefix.into());
+    if fuzzy {
+      args.push(static_val!(FUZZY));
+    }
+    if withscores {
+      args.push(static_val!(WITHSCORES));
+    }
+    if withpayloads {
+      args.push(static_val!(WITHPAYLOADS));
+    }
+    if let Some(max) = max {
+      args.extend([static_val!(MAX), max.try_into()?]);
+    }
+
+    Ok((RedisCommandKind::FtSugGet, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ft_suglen<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_values_cmd(client, RedisCommandKind::FtSugLen, key.into()).await
+}
+
+pub async fn ft_syndump<C: ClientLike>(client: &C, index: Str) -> Result<RedisValue, RedisError> {
+  one_arg_values_cmd(client, RedisCommandKind::FtSynDump, index.into()).await
+}
+
+pub async fn ft_synupdate<C: ClientLike>(
+  client: &C,
+  index: Str,
+  synonym_group_id: Str,
+  skipinitialscan: bool,
+  terms: MultipleStrings,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(3 + terms.len());
+    args.push(index.into());
+    args.push(synonym_group_id.into());
+    if skipinitialscan {
+      args.push(static_val!(SKIPINITIALSCAN));
+    }
+    for term in terms.inner().into_iter() {
+      args.push(term.into());
+    }
+
+    Ok((RedisCommandKind::FtSynUpdate, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ft_tagvals<C: ClientLike>(client: &C, index: Str, field_name: Str) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::FtTagVals, vec![
+    index.into(),
+    field_name.into(),
+  ])
+  .await
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/scan.rs.html b/doc/src/fred/commands/impls/scan.rs.html new file mode 100644 index 00000000..9131f131 --- /dev/null +++ b/doc/src/fred/commands/impls/scan.rs.html @@ -0,0 +1,461 @@ +scan.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+
use super::*;
+use crate::{
+  error::*,
+  interfaces,
+  modules::inner::RedisClientInner,
+  protocol::{
+    command::{RedisCommand, RedisCommandKind},
+    responders::ResponseKind,
+    types::*,
+  },
+  runtime::{rx_stream, unbounded_channel, RefCount},
+  types::*,
+  utils,
+};
+use bytes_utils::Str;
+use futures::stream::{Stream, TryStreamExt};
+
+#[cfg(feature = "glommio")]
+use crate::runtime::UnboundedSender;
+
+static STARTING_CURSOR: &str = "0";
+
+fn values_args(key: RedisKey, pattern: Str, count: Option<u32>) -> Vec<RedisValue> {
+  let mut args = Vec::with_capacity(6);
+  args.push(key.into());
+  args.push(static_val!(STARTING_CURSOR));
+  args.push(static_val!(MATCH));
+  args.push(pattern.into());
+
+  if let Some(count) = count {
+    args.push(static_val!(COUNT));
+    args.push(count.into());
+  }
+
+  args
+}
+
+pub fn scan_cluster(
+  inner: &RefCount<RedisClientInner>,
+  pattern: Str,
+  count: Option<u32>,
+  r#type: Option<ScanType>,
+) -> impl Stream<Item = Result<ScanResult, RedisError>> {
+  let (tx, rx) = unbounded_channel();
+  #[cfg(feature = "glommio")]
+  let tx: UnboundedSender<_> = tx.into();
+
+  let hash_slots = inner.with_cluster_state(|state| Ok(state.unique_hash_slots()));
+  let hash_slots = match hash_slots {
+    Ok(slots) => slots,
+    Err(e) => {
+      let _ = tx.send(Err(e));
+      return rx_stream(rx);
+    },
+  };
+
+  let mut args = Vec::with_capacity(7);
+  args.push(static_val!(STARTING_CURSOR));
+  args.push(static_val!(MATCH));
+  args.push(pattern.into());
+
+  if let Some(count) = count {
+    args.push(static_val!(COUNT));
+    args.push(count.into());
+  }
+  if let Some(r#type) = r#type {
+    args.push(static_val!(TYPE));
+    args.push(r#type.to_str().into());
+  }
+
+  for slot in hash_slots.into_iter() {
+    _trace!(inner, "Scan cluster hash slot server: {}", slot);
+    let response = ResponseKind::KeyScan(KeyScanInner {
+      hash_slot:  Some(slot),
+      args:       args.clone(),
+      cursor_idx: 0,
+      tx:         tx.clone(),
+      server:     None,
+    });
+    let command: RedisCommand = (RedisCommandKind::Scan, Vec::new(), response).into();
+
+    if let Err(e) = interfaces::default_send_command(inner, command) {
+      let _ = tx.send(Err(e));
+      break;
+    }
+  }
+
+  rx_stream(rx)
+}
+
+pub fn scan(
+  inner: &RefCount<RedisClientInner>,
+  pattern: Str,
+  count: Option<u32>,
+  r#type: Option<ScanType>,
+  server: Option<Server>,
+) -> impl Stream<Item = Result<ScanResult, RedisError>> {
+  let (tx, rx) = unbounded_channel();
+  #[cfg(feature = "glommio")]
+  let tx: UnboundedSender<_> = tx.into();
+
+  let hash_slot = if inner.config.server.is_clustered() {
+    if utils::clustered_scan_pattern_has_hash_tag(inner, &pattern) {
+      Some(redis_protocol::redis_keyslot(pattern.as_bytes()))
+    } else {
+      None
+    }
+  } else {
+    None
+  };
+
+  let mut args = Vec::with_capacity(7);
+  args.push(static_val!(STARTING_CURSOR));
+  args.push(static_val!(MATCH));
+  args.push(pattern.into());
+
+  if let Some(count) = count {
+    args.push(static_val!(COUNT));
+    args.push(count.into());
+  }
+  if let Some(r#type) = r#type {
+    args.push(static_val!(TYPE));
+    args.push(r#type.to_str().into());
+  }
+
+  let response = ResponseKind::KeyScan(KeyScanInner {
+    hash_slot,
+    args,
+    server,
+    cursor_idx: 0,
+    tx: tx.clone(),
+  });
+  let command: RedisCommand = (RedisCommandKind::Scan, Vec::new(), response).into();
+
+  if let Err(e) = interfaces::default_send_command(inner, command) {
+    let _ = tx.send(Err(e));
+  }
+
+  rx_stream(rx)
+}
+
+pub fn hscan(
+  inner: &RefCount<RedisClientInner>,
+  key: RedisKey,
+  pattern: Str,
+  count: Option<u32>,
+) -> impl Stream<Item = Result<HScanResult, RedisError>> {
+  let (tx, rx) = unbounded_channel();
+  let args = values_args(key, pattern, count);
+
+  #[cfg(feature = "glommio")]
+  let tx: UnboundedSender<_> = tx.into();
+  let response = ResponseKind::ValueScan(ValueScanInner {
+    tx: tx.clone(),
+    cursor_idx: 1,
+    args,
+  });
+  let command: RedisCommand = (RedisCommandKind::Hscan, Vec::new(), response).into();
+
+  if let Err(e) = interfaces::default_send_command(inner, command) {
+    let _ = tx.send(Err(e));
+  }
+
+  rx_stream(rx).try_filter_map(|result| async move {
+    match result {
+      ValueScanResult::HScan(res) => Ok(Some(res)),
+      _ => Err(RedisError::new(RedisErrorKind::Protocol, "Expected HSCAN result.")),
+    }
+  })
+}
+
+pub fn sscan(
+  inner: &RefCount<RedisClientInner>,
+  key: RedisKey,
+  pattern: Str,
+  count: Option<u32>,
+) -> impl Stream<Item = Result<SScanResult, RedisError>> {
+  let (tx, rx) = unbounded_channel();
+  let args = values_args(key, pattern, count);
+
+  #[cfg(feature = "glommio")]
+  let tx: UnboundedSender<_> = tx.into();
+  let response = ResponseKind::ValueScan(ValueScanInner {
+    tx: tx.clone(),
+    cursor_idx: 1,
+    args,
+  });
+  let command: RedisCommand = (RedisCommandKind::Sscan, Vec::new(), response).into();
+
+  if let Err(e) = interfaces::default_send_command(inner, command) {
+    let _ = tx.send(Err(e));
+  }
+
+  rx_stream(rx).try_filter_map(|result| async move {
+    match result {
+      ValueScanResult::SScan(res) => Ok(Some(res)),
+      _ => Err(RedisError::new(RedisErrorKind::Protocol, "Expected SSCAN result.")),
+    }
+  })
+}
+
+pub fn zscan(
+  inner: &RefCount<RedisClientInner>,
+  key: RedisKey,
+  pattern: Str,
+  count: Option<u32>,
+) -> impl Stream<Item = Result<ZScanResult, RedisError>> {
+  let (tx, rx) = unbounded_channel();
+  let args = values_args(key, pattern, count);
+
+  #[cfg(feature = "glommio")]
+  let tx: UnboundedSender<_> = tx.into();
+  let response = ResponseKind::ValueScan(ValueScanInner {
+    tx: tx.clone(),
+    cursor_idx: 1,
+    args,
+  });
+  let command: RedisCommand = (RedisCommandKind::Zscan, Vec::new(), response).into();
+
+  if let Err(e) = interfaces::default_send_command(inner, command) {
+    let _ = tx.send(Err(e));
+  }
+
+  rx_stream(rx).try_filter_map(|result| async move {
+    match result {
+      ValueScanResult::ZScan(res) => Ok(Some(res)),
+      _ => Err(RedisError::new(RedisErrorKind::Protocol, "Expected ZSCAN result.")),
+    }
+  })
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/sentinel.rs.html b/doc/src/fred/commands/impls/sentinel.rs.html new file mode 100644 index 00000000..74caff65 --- /dev/null +++ b/doc/src/fred/commands/impls/sentinel.rs.html @@ -0,0 +1,401 @@ +sentinel.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+
use super::*;
+use crate::{
+  error::RedisError,
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  router::sentinel::{
+    CKQUORUM,
+    CONFIG,
+    FAILOVER,
+    FLUSHCONFIG,
+    GET_MASTER_ADDR_BY_NAME,
+    INFO_CACHE,
+    MASTER,
+    MASTERS,
+    MONITOR,
+    MYID,
+    PENDING_SCRIPTS,
+    REMOVE,
+    REPLICAS,
+    SENTINELS,
+    SET,
+    SIMULATE_FAILURE,
+  },
+  types::*,
+  utils,
+};
+use bytes_utils::Str;
+use std::net::IpAddr;
+
+pub async fn config_get<C: ClientLike>(client: &C, name: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args = vec![static_val!(CONFIG), static_val!(GET), name.into()];
+    Ok((RedisCommandKind::Sentinel, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn config_set<C: ClientLike>(client: &C, name: Str, value: RedisValue) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Sentinel, vec![
+      static_val!(CONFIG),
+      static_val!(SET),
+      name.into(),
+      value,
+    ]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ckquorum<C: ClientLike>(client: &C, name: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Sentinel, vec![static_val!(CKQUORUM), name.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn flushconfig<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::Sentinel, vec![static_val!(FLUSHCONFIG)]).await
+}
+
+pub async fn failover<C: ClientLike>(client: &C, name: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Sentinel, vec![static_val!(FAILOVER), name.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn get_master_addr_by_name<C: ClientLike>(client: &C, name: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Sentinel, vec![
+      static_val!(GET_MASTER_ADDR_BY_NAME),
+      name.into(),
+    ]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn info_cache<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::Sentinel, vec![static_val!(INFO_CACHE)]).await
+}
+
+pub async fn masters<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::Sentinel, vec![static_val!(MASTERS)]).await
+}
+
+pub async fn master<C: ClientLike>(client: &C, name: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Sentinel, vec![static_val!(MASTER), name.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn monitor<C: ClientLike>(
+  client: &C,
+  name: Str,
+  ip: IpAddr,
+  port: u16,
+  quorum: u32,
+) -> Result<RedisValue, RedisError> {
+  let ip = ip.to_string();
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Sentinel, vec![
+      static_val!(MONITOR),
+      name.into(),
+      ip.into(),
+      port.into(),
+      quorum.into(),
+    ]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn myid<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::Sentinel, vec![static_val!(MYID)]).await
+}
+
+pub async fn pending_scripts<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::Sentinel, vec![static_val!(PENDING_SCRIPTS)]).await
+}
+
+pub async fn remove<C: ClientLike>(client: &C, name: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Sentinel, vec![static_val!(REMOVE), name.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn replicas<C: ClientLike>(client: &C, name: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Sentinel, vec![static_val!(REPLICAS), name.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn sentinels<C: ClientLike>(client: &C, name: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Sentinel, vec![static_val!(SENTINELS), name.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn set<C: ClientLike>(client: &C, name: Str, options: RedisMap) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2 + options.len());
+    args.push(static_val!(SET));
+    args.push(name.into());
+
+    for (key, value) in options.inner().into_iter() {
+      args.push(key.into());
+      args.push(value);
+    }
+    Ok((RedisCommandKind::Sentinel, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn simulate_failure<C: ClientLike>(
+  client: &C,
+  kind: SentinelFailureKind,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Sentinel, vec![
+      static_val!(SIMULATE_FAILURE),
+      kind.to_str().into(),
+    ]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn reset<C: ClientLike>(client: &C, pattern: Str) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Sentinel, vec![static_val!(RESET), pattern.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/server.rs.html b/doc/src/fred/commands/impls/server.rs.html new file mode 100644 index 00000000..3cdac6e4 --- /dev/null +++ b/doc/src/fred/commands/impls/server.rs.html @@ -0,0 +1,589 @@ +server.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+
use super::*;
+use crate::{
+  clients::RedisClient,
+  error::*,
+  interfaces,
+  modules::inner::RedisClientInner,
+  prelude::Resp3Frame,
+  protocol::{
+    command::{RedisCommand, RedisCommandKind, RouterCommand},
+    responders::ResponseKind,
+    utils as protocol_utils,
+  },
+  runtime::{oneshot_channel, RefCount},
+  types::*,
+  utils,
+};
+use bytes_utils::Str;
+
+pub async fn active_connections<C: ClientLike>(client: &C) -> Result<Vec<Server>, RedisError> {
+  let (tx, rx) = oneshot_channel();
+  let command = RouterCommand::Connections { tx };
+  interfaces::send_to_router(client.inner(), command)?;
+
+  rx.await.map_err(|e| e.into())
+}
+
+pub async fn quit<C: ClientLike>(client: &C) -> Result<(), RedisError> {
+  let inner = client.inner().clone();
+  _debug!(inner, "Closing Redis connection with Quit command.");
+
+  let (tx, rx) = oneshot_channel();
+  let mut command: RedisCommand = if inner.config.server.is_clustered() {
+    let response = ResponseKind::new_buffer(tx);
+    (RedisCommandKind::Quit, vec![], response).into()
+  } else {
+    let response = ResponseKind::Respond(Some(tx));
+    (RedisCommandKind::Quit, vec![], response).into()
+  };
+  utils::set_client_state(&inner.state, ClientState::Disconnecting);
+  inner.notifications.broadcast_close();
+
+  let timeout_dur = utils::prepare_command(client, &mut command);
+  client.send_command(command)?;
+  let _ = utils::timeout(rx, timeout_dur).await??;
+  inner
+    .notifications
+    .close_public_receivers(inner.with_perf_config(|c| c.broadcast_channel_capacity));
+  inner.backchannel.write().await.check_and_disconnect(&inner, None).await;
+
+  Ok(())
+}
+
+pub async fn shutdown<C: ClientLike>(client: &C, flags: Option<ShutdownFlags>) -> Result<(), RedisError> {
+  let inner = client.inner().clone();
+  _debug!(inner, "Shutting down server.");
+
+  let args = if let Some(flags) = flags {
+    vec![flags.to_str().into()]
+  } else {
+    Vec::new()
+  };
+  let (tx, rx) = oneshot_channel();
+  let mut command: RedisCommand = if inner.config.server.is_clustered() {
+    let response = ResponseKind::new_buffer(tx);
+    (RedisCommandKind::Shutdown, args, response).into()
+  } else {
+    let response = ResponseKind::Respond(Some(tx));
+    (RedisCommandKind::Shutdown, args, response).into()
+  };
+  utils::set_client_state(&inner.state, ClientState::Disconnecting);
+  inner.notifications.broadcast_close();
+
+  let timeout_dur = utils::prepare_command(client, &mut command);
+  client.send_command(command)?;
+  let _ = utils::timeout(rx, timeout_dur).await??;
+  inner
+    .notifications
+    .close_public_receivers(inner.with_perf_config(|c| c.broadcast_channel_capacity));
+  inner.backchannel.write().await.check_and_disconnect(&inner, None).await;
+
+  Ok(())
+}
+
+/// Create a new client struct for each unique primary cluster node based on the cached cluster state.
+pub fn split(inner: &RefCount<RedisClientInner>) -> Result<Vec<RedisClient>, RedisError> {
+  if !inner.config.server.is_clustered() {
+    return Err(RedisError::new(
+      RedisErrorKind::Config,
+      "Expected clustered redis deployment.",
+    ));
+  }
+  let servers = inner.with_cluster_state(|state| Ok(state.unique_primary_nodes()))?;
+  _debug!(inner, "Unique primary nodes in split: {:?}", servers);
+
+  Ok(
+    servers
+      .into_iter()
+      .map(|server| {
+        let mut config = inner.config.as_ref().clone();
+        config.server = ServerConfig::Centralized { server };
+        let perf = inner.performance_config();
+        let policy = inner.reconnect_policy();
+        let connection = inner.connection_config();
+
+        RedisClient::new(config, Some(perf), Some(connection), policy)
+      })
+      .collect(),
+  )
+}
+
+pub async fn force_reconnection(inner: &RefCount<RedisClientInner>) -> Result<(), RedisError> {
+  let (tx, rx) = oneshot_channel();
+  let command = RouterCommand::Reconnect {
+    server:                               None,
+    force:                                true,
+    tx:                                   Some(tx),
+    #[cfg(feature = "replicas")]
+    replica:                              false,
+  };
+  interfaces::send_to_router(inner, command)?;
+
+  rx.await?.map(|_| ())
+}
+
+pub async fn flushall<C: ClientLike>(client: &C, r#async: bool) -> Result<RedisValue, RedisError> {
+  let args = if r#async { vec![static_val!(ASYNC)] } else { Vec::new() };
+  let frame = utils::request_response(client, move || Ok((RedisCommandKind::FlushAll, args))).await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn flushall_cluster<C: ClientLike>(client: &C) -> Result<(), RedisError> {
+  if !client.inner().config.server.is_clustered() {
+    return flushall(client, false).await.map(|_| ());
+  }
+
+  let (tx, rx) = oneshot_channel();
+  let response = ResponseKind::new_buffer(tx);
+  let mut command: RedisCommand = (RedisCommandKind::_FlushAllCluster, vec![], response).into();
+  let timeout_dur = utils::prepare_command(client, &mut command);
+  client.send_command(command)?;
+
+  let _ = utils::timeout(rx, timeout_dur).await??;
+  Ok(())
+}
+
+pub async fn ping<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, || Ok((RedisCommandKind::Ping, vec![]))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn select<C: ClientLike>(client: &C, db: u8) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, || Ok((RedisCommandKind::Select, vec![db.into()]))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn info<C: ClientLike>(client: &C, section: Option<InfoKind>) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1);
+    if let Some(section) = section {
+      args.push(section.to_str().into());
+    }
+
+    Ok((RedisCommandKind::Info, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn hello<C: ClientLike>(
+  client: &C,
+  version: RespVersion,
+  auth: Option<(Str, Str)>,
+  setname: Option<Str>,
+) -> Result<(), RedisError> {
+  let mut args = if let Some((username, password)) = auth {
+    vec![username.into(), password.into()]
+  } else {
+    vec![]
+  };
+  if let Some(name) = setname {
+    args.push(name.into());
+  }
+
+  if client.inner().config.server.is_clustered() {
+    let (tx, rx) = oneshot_channel();
+    let mut command: RedisCommand = RedisCommandKind::_HelloAllCluster(version).into();
+    command.response = ResponseKind::new_buffer(tx);
+
+    let timeout_dur = utils::prepare_command(client, &mut command);
+    client.send_command(command)?;
+    let _ = utils::timeout(rx, timeout_dur).await??;
+    Ok(())
+  } else {
+    let frame = utils::request_response(client, move || Ok((RedisCommandKind::_Hello(version), args))).await?;
+    let _ = protocol_utils::frame_to_results(frame)?;
+    Ok(())
+  }
+}
+
+pub async fn auth<C: ClientLike>(client: &C, username: Option<String>, password: Str) -> Result<(), RedisError> {
+  let mut args = Vec::with_capacity(2);
+  if let Some(username) = username {
+    args.push(username.into());
+  }
+  args.push(password.into());
+
+  if client.inner().config.server.is_clustered() {
+    let (tx, rx) = oneshot_channel();
+    let response = ResponseKind::new_buffer(tx);
+    let mut command: RedisCommand = (RedisCommandKind::_AuthAllCluster, args, response).into();
+
+    let timeout_dur = utils::prepare_command(client, &mut command);
+    client.send_command(command)?;
+    let _ = utils::timeout(rx, timeout_dur).await??;
+    Ok(())
+  } else {
+    let frame = utils::request_response(client, move || Ok((RedisCommandKind::Auth, args))).await?;
+
+    let response = protocol_utils::frame_to_results(frame)?;
+    protocol_utils::expect_ok(&response)
+  }
+}
+
+pub async fn custom<C: ClientLike>(
+  client: &C,
+  cmd: CustomCommand,
+  args: Vec<RedisValue>,
+) -> Result<RedisValue, RedisError> {
+  args_values_cmd(client, RedisCommandKind::_Custom(cmd), args).await
+}
+
+pub async fn custom_raw<C: ClientLike>(
+  client: &C,
+  cmd: CustomCommand,
+  args: Vec<RedisValue>,
+) -> Result<Resp3Frame, RedisError> {
+  utils::request_response(client, move || Ok((RedisCommandKind::_Custom(cmd), args))).await
+}
+
+#[cfg(feature = "i-server")]
+value_cmd!(dbsize, DBSize);
+#[cfg(feature = "i-server")]
+value_cmd!(bgrewriteaof, BgreWriteAof);
+#[cfg(feature = "i-server")]
+value_cmd!(bgsave, BgSave);
+
+#[cfg(feature = "i-server")]
+pub async fn failover<C: ClientLike>(
+  client: &C,
+  to: Option<(String, u16)>,
+  force: bool,
+  abort: bool,
+  timeout: Option<u32>,
+) -> Result<(), RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(7);
+    if let Some((host, port)) = to {
+      args.push(static_val!(TO));
+      args.push(host.into());
+      args.push(port.into());
+    }
+    if force {
+      args.push(static_val!(FORCE));
+    }
+    if abort {
+      args.push(static_val!(ABORT));
+    }
+    if let Some(timeout) = timeout {
+      args.push(static_val!(TIMEOUT));
+      args.push(timeout.into());
+    }
+
+    Ok((RedisCommandKind::Failover, args))
+  })
+  .await?;
+
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)
+}
+
+#[cfg(feature = "i-server")]
+value_cmd!(lastsave, LastSave);
+
+#[cfg(feature = "i-server")]
+pub async fn wait<C: ClientLike>(client: &C, numreplicas: i64, timeout: i64) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::Wait, vec![numreplicas.into(), timeout.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/sets.rs.html b/doc/src/fred/commands/impls/sets.rs.html new file mode 100644 index 00000000..3df8f0dc --- /dev/null +++ b/doc/src/fred/commands/impls/sets.rs.html @@ -0,0 +1,453 @@ +sets.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+
use super::*;
+use crate::{
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  types::*,
+  utils,
+};
+use std::convert::TryInto;
+
+pub async fn sadd<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  members: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let members = members.into_multiple_values();
+    let mut args = Vec::with_capacity(1 + members.len());
+    args.push(key.into());
+
+    for member in members.into_iter() {
+      args.push(member);
+    }
+    Ok((RedisCommandKind::Sadd, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn scard<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::Scard, key.into()).await
+}
+
+pub async fn sdiff<C: ClientLike>(client: &C, keys: MultipleKeys) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(keys.len());
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    Ok((RedisCommandKind::Sdiff, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn sdiffstore<C: ClientLike>(
+  client: &C,
+  dest: RedisKey,
+  keys: MultipleKeys,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1 + keys.len());
+    args.push(dest.into());
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    Ok((RedisCommandKind::Sdiffstore, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn sinter<C: ClientLike>(client: &C, keys: MultipleKeys) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(keys.len());
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    Ok((RedisCommandKind::Sinter, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn sinterstore<C: ClientLike>(
+  client: &C,
+  dest: RedisKey,
+  keys: MultipleKeys,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1 + keys.len());
+    args.push(dest.into());
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    Ok((RedisCommandKind::Sinterstore, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn sismember<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  member: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  args_value_cmd(client, RedisCommandKind::Sismember, vec![key.into(), member]).await
+}
+
+pub async fn smismember<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  members: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let members = members.into_multiple_values();
+    let mut args = Vec::with_capacity(1 + members.len());
+    args.push(key.into());
+
+    for member in members.into_iter() {
+      args.push(member);
+    }
+    Ok((RedisCommandKind::Smismember, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn smembers<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_values_cmd(client, RedisCommandKind::Smembers, key.into()).await
+}
+
+pub async fn smove<C: ClientLike>(
+  client: &C,
+  source: RedisKey,
+  dest: RedisKey,
+  member: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  let args = vec![source.into(), dest.into(), member];
+  args_value_cmd(client, RedisCommandKind::Smove, args).await
+}
+
+pub async fn spop<C: ClientLike>(client: &C, key: RedisKey, count: Option<usize>) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2);
+    args.push(key.into());
+
+    if let Some(count) = count {
+      args.push(count.try_into()?);
+    }
+    Ok((RedisCommandKind::Spop, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn srandmember<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  count: Option<usize>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2);
+    args.push(key.into());
+
+    if let Some(count) = count {
+      args.push(count.try_into()?);
+    }
+    Ok((RedisCommandKind::Srandmember, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn srem<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  members: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let members = members.into_multiple_values();
+    let mut args = Vec::with_capacity(1 + members.len());
+    args.push(key.into());
+
+    for member in members.into_iter() {
+      args.push(member);
+    }
+    Ok((RedisCommandKind::Srem, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn sunion<C: ClientLike>(client: &C, keys: MultipleKeys) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(keys.len());
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    Ok((RedisCommandKind::Sunion, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn sunionstore<C: ClientLike>(
+  client: &C,
+  dest: RedisKey,
+  keys: MultipleKeys,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1 + keys.len());
+    args.push(dest.into());
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    Ok((RedisCommandKind::Sunionstore, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/slowlog.rs.html b/doc/src/fred/commands/impls/slowlog.rs.html new file mode 100644 index 00000000..cf5cc53c --- /dev/null +++ b/doc/src/fred/commands/impls/slowlog.rs.html @@ -0,0 +1,61 @@ +slowlog.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+
use super::*;
+use crate::{
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  utils,
+};
+
+pub async fn slowlog_get<C: ClientLike>(client: &C, count: Option<i64>) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2);
+    args.push(static_val!(GET));
+
+    if let Some(count) = count {
+      args.push(count.into());
+    }
+
+    Ok((RedisCommandKind::Slowlog, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn slowlog_length<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, || Ok((RedisCommandKind::Slowlog, vec![LEN.into()]))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn slowlog_reset<C: ClientLike>(client: &C) -> Result<(), RedisError> {
+  args_ok_cmd(client, RedisCommandKind::Slowlog, vec![static_val!(RESET)]).await
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/sorted_sets.rs.html b/doc/src/fred/commands/impls/sorted_sets.rs.html new file mode 100644 index 00000000..9ca034f2 --- /dev/null +++ b/doc/src/fred/commands/impls/sorted_sets.rs.html @@ -0,0 +1,1545 @@ +sorted_sets.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+
use super::*;
+use crate::{
+  error::*,
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  types::*,
+  utils,
+};
+use std::convert::TryInto;
+
+static INCR: &str = "INCR";
+static WITH_SCORES: &str = "WITHSCORES";
+static AGGREGATE: &str = "AGGREGATE";
+static WEIGHTS: &str = "WEIGHTS";
+
+fn new_range_error(kind: &Option<ZSort>) -> Result<(), RedisError> {
+  if let Some(ref sort) = *kind {
+    Err(RedisError::new(
+      RedisErrorKind::InvalidArgument,
+      format!("Invalid range bound with {} sort", sort.to_str()),
+    ))
+  } else {
+    Err(RedisError::new(
+      RedisErrorKind::InvalidArgument,
+      "Invalid index range bound.",
+    ))
+  }
+}
+
+fn check_range_type(range: &ZRange, kind: &Option<ZSort>) -> Result<(), RedisError> {
+  match kind {
+    Some(_kind) => match _kind {
+      ZSort::ByLex => match range.range {
+        ZRangeBound::Lex(_) | ZRangeBound::InfiniteLex | ZRangeBound::NegInfinityLex => Ok(()),
+        _ => new_range_error(kind),
+      },
+      ZSort::ByScore => match range.range {
+        ZRangeBound::Score(_) | ZRangeBound::InfiniteScore | ZRangeBound::NegInfiniteScore => Ok(()),
+        _ => new_range_error(kind),
+      },
+    },
+    None => match range.range {
+      ZRangeBound::Index(_) => Ok(()),
+      _ => new_range_error(kind),
+    },
+  }
+}
+
+fn check_range_types(min: &ZRange, max: &ZRange, kind: &Option<ZSort>) -> Result<(), RedisError> {
+  check_range_type(min, kind)?;
+  check_range_type(max, kind)?;
+  Ok(())
+}
+
+pub async fn bzmpop<C: ClientLike>(
+  client: &C,
+  timeout: f64,
+  keys: MultipleKeys,
+  sort: ZCmp,
+  count: Option<i64>,
+) -> Result<RedisValue, RedisError> {
+  let timeout: RedisValue = timeout.try_into()?;
+
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(keys.len() + 4);
+    args.push(timeout);
+    args.push(keys.len().try_into()?);
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    args.push(sort.to_str().into());
+    if let Some(count) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.into());
+    }
+
+    Ok((RedisCommandKind::BzmPop, args))
+  })
+  .await?;
+
+  protocol_utils::check_null_timeout(&frame)?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn bzpopmin<C: ClientLike>(client: &C, keys: MultipleKeys, timeout: f64) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1 + keys.len());
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    args.push(timeout.try_into()?);
+
+    Ok((RedisCommandKind::BzPopMin, args))
+  })
+  .await?;
+
+  protocol_utils::check_null_timeout(&frame)?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn bzpopmax<C: ClientLike>(client: &C, keys: MultipleKeys, timeout: f64) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1 + keys.len());
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    args.push(timeout.try_into()?);
+
+    Ok((RedisCommandKind::BzPopMax, args))
+  })
+  .await?;
+
+  protocol_utils::check_null_timeout(&frame)?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zadd<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  options: Option<SetOptions>,
+  ordering: Option<Ordering>,
+  changed: bool,
+  incr: bool,
+  values: MultipleZaddValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(5 + (values.len() * 2));
+    args.push(key.into());
+
+    if let Some(options) = options {
+      args.push(options.to_str().into());
+    }
+    if let Some(ordering) = ordering {
+      args.push(ordering.to_str().into());
+    }
+    if changed {
+      args.push(static_val!(CHANGED));
+    }
+    if incr {
+      args.push(static_val!(INCR));
+    }
+
+    for (score, value) in values.inner().into_iter() {
+      args.push(score.try_into()?);
+      args.push(value);
+    }
+
+    Ok((RedisCommandKind::Zadd, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zcard<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::Zcard, key.into()).await
+}
+
+pub async fn zcount<C: ClientLike>(client: &C, key: RedisKey, min: f64, max: f64) -> Result<RedisValue, RedisError> {
+  let (min, max) = (min.try_into()?, max.try_into()?);
+  args_value_cmd(client, RedisCommandKind::Zcount, vec![key.into(), min, max]).await
+}
+
+pub async fn zdiff<C: ClientLike>(
+  client: &C,
+  keys: MultipleKeys,
+  withscores: bool,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2 + keys.len());
+    args.push(keys.len().try_into()?);
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    if withscores {
+      args.push(static_val!(WITH_SCORES));
+    }
+
+    Ok((RedisCommandKind::Zdiff, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zdiffstore<C: ClientLike>(
+  client: &C,
+  dest: RedisKey,
+  keys: MultipleKeys,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2 + keys.len());
+    args.push(dest.into());
+    args.push(keys.len().try_into()?);
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    Ok((RedisCommandKind::Zdiffstore, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zincrby<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  increment: f64,
+  member: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  let increment = increment.try_into()?;
+  let args = vec![key.into(), increment, member];
+  args_value_cmd(client, RedisCommandKind::Zincrby, args).await
+}
+
+pub async fn zinter<C: ClientLike>(
+  client: &C,
+  keys: MultipleKeys,
+  weights: MultipleWeights,
+  aggregate: Option<AggregateOptions>,
+  withscores: bool,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args_len = 6 + keys.len() + weights.len();
+    let mut args = Vec::with_capacity(args_len);
+    args.push(keys.len().try_into()?);
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    if weights.len() > 0 {
+      args.push(static_val!(WEIGHTS));
+      for weight in weights.inner().into_iter() {
+        args.push(weight.try_into()?);
+      }
+    }
+    if let Some(options) = aggregate {
+      args.push(static_val!(AGGREGATE));
+      args.push(options.to_str().into());
+    }
+    if withscores {
+      args.push(static_val!(WITH_SCORES));
+    }
+
+    Ok((RedisCommandKind::Zinter, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zinterstore<C: ClientLike>(
+  client: &C,
+  dest: RedisKey,
+  keys: MultipleKeys,
+  weights: MultipleWeights,
+  aggregate: Option<AggregateOptions>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args_len = 5 + keys.len() + weights.len();
+    let mut args = Vec::with_capacity(args_len);
+    args.push(dest.into());
+    args.push(keys.len().try_into()?);
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    if weights.len() > 0 {
+      args.push(static_val!(WEIGHTS));
+      for weight in weights.inner().into_iter() {
+        args.push(weight.try_into()?);
+      }
+    }
+    if let Some(options) = aggregate {
+      args.push(static_val!(AGGREGATE));
+      args.push(options.to_str().into());
+    }
+
+    Ok((RedisCommandKind::Zinterstore, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zlexcount<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  min: ZRange,
+  max: ZRange,
+) -> Result<RedisValue, RedisError> {
+  check_range_types(&min, &max, &Some(ZSort::ByLex))?;
+
+  let args = vec![key.into(), min.into_value()?, max.into_value()?];
+  args_value_cmd(client, RedisCommandKind::Zlexcount, args).await
+}
+
+pub async fn zpopmax<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  count: Option<usize>,
+) -> Result<RedisValue, RedisError> {
+  let args = if let Some(count) = count {
+    vec![key.into(), count.try_into()?]
+  } else {
+    vec![key.into()]
+  };
+
+  args_values_cmd(client, RedisCommandKind::Zpopmax, args).await
+}
+
+pub async fn zpopmin<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  count: Option<usize>,
+) -> Result<RedisValue, RedisError> {
+  let args = if let Some(count) = count {
+    vec![key.into(), count.try_into()?]
+  } else {
+    vec![key.into()]
+  };
+
+  args_values_cmd(client, RedisCommandKind::Zpopmin, args).await
+}
+
+pub async fn zmpop<C: ClientLike>(
+  client: &C,
+  keys: MultipleKeys,
+  sort: ZCmp,
+  count: Option<i64>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(keys.len() + 3);
+    args.push(keys.len().try_into()?);
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    args.push(sort.to_str().into());
+    if let Some(count) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.into());
+    }
+
+    Ok((RedisCommandKind::Zmpop, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zrandmember<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  count: Option<(i64, bool)>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(3);
+    args.push(key.into());
+
+    if let Some((count, withscores)) = count {
+      args.push(count.into());
+      if withscores {
+        args.push(static_val!(WITH_SCORES));
+      }
+    }
+
+    Ok((RedisCommandKind::Zrandmember, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zrangestore<C: ClientLike>(
+  client: &C,
+  dest: RedisKey,
+  source: RedisKey,
+  min: ZRange,
+  max: ZRange,
+  sort: Option<ZSort>,
+  rev: bool,
+  limit: Option<Limit>,
+) -> Result<RedisValue, RedisError> {
+  check_range_types(&min, &max, &sort)?;
+
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(9);
+    args.push(dest.into());
+    args.push(source.into());
+    args.push(min.into_value()?);
+    args.push(max.into_value()?);
+
+    if let Some(sort) = sort {
+      args.push(sort.to_str().into());
+    }
+    if rev {
+      args.push(static_val!(REV));
+    }
+    if let Some((offset, count)) = limit {
+      args.push(static_val!(LIMIT));
+      args.push(offset.into());
+      args.push(count.into());
+    }
+
+    Ok((RedisCommandKind::Zrangestore, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zrange<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  min: ZRange,
+  max: ZRange,
+  sort: Option<ZSort>,
+  rev: bool,
+  limit: Option<Limit>,
+  withscores: bool,
+) -> Result<RedisValue, RedisError> {
+  check_range_types(&min, &max, &sort)?;
+
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(9);
+    args.push(key.into());
+    args.push(min.into_value()?);
+    args.push(max.into_value()?);
+
+    if let Some(sort) = sort {
+      args.push(sort.to_str().into());
+    }
+    if rev {
+      args.push(static_val!(REV));
+    }
+    if let Some((offset, count)) = limit {
+      args.push(static_val!(LIMIT));
+      args.push(offset.into());
+      args.push(count.into());
+    }
+    if withscores {
+      args.push(static_val!(WITH_SCORES));
+    }
+
+    Ok((RedisCommandKind::Zrange, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zrangebylex<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  min: ZRange,
+  max: ZRange,
+  limit: Option<Limit>,
+) -> Result<RedisValue, RedisError> {
+  check_range_types(&min, &max, &Some(ZSort::ByLex))?;
+
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(6);
+    args.push(key.into());
+    args.push(min.into_value()?);
+    args.push(max.into_value()?);
+
+    if let Some((offset, count)) = limit {
+      args.push(static_val!(LIMIT));
+      args.push(offset.into());
+      args.push(count.into());
+    }
+
+    Ok((RedisCommandKind::Zrangebylex, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zrevrangebylex<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  max: ZRange,
+  min: ZRange,
+  limit: Option<Limit>,
+) -> Result<RedisValue, RedisError> {
+  check_range_types(&min, &max, &Some(ZSort::ByLex))?;
+
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(6);
+    args.push(key.into());
+    args.push(max.into_value()?);
+    args.push(min.into_value()?);
+
+    if let Some((offset, count)) = limit {
+      args.push(static_val!(LIMIT));
+      args.push(offset.into());
+      args.push(count.into());
+    }
+
+    Ok((RedisCommandKind::Zrevrangebylex, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zrangebyscore<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  min: ZRange,
+  max: ZRange,
+  withscores: bool,
+  limit: Option<Limit>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(7);
+    args.push(key.into());
+    args.push(min.into_value()?);
+    args.push(max.into_value()?);
+
+    if withscores {
+      args.push(static_val!(WITH_SCORES));
+    }
+    if let Some((offset, count)) = limit {
+      args.push(static_val!(LIMIT));
+      args.push(offset.into());
+      args.push(count.into());
+    }
+
+    Ok((RedisCommandKind::Zrangebyscore, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zrevrangebyscore<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  max: ZRange,
+  min: ZRange,
+  withscores: bool,
+  limit: Option<Limit>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(7);
+    args.push(key.into());
+    args.push(max.into_value()?);
+    args.push(min.into_value()?);
+
+    if withscores {
+      args.push(static_val!(WITH_SCORES));
+    }
+    if let Some((offset, count)) = limit {
+      args.push(static_val!(LIMIT));
+      args.push(offset.into());
+      args.push(count.into());
+    }
+
+    Ok((RedisCommandKind::Zrevrangebyscore, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zrank<C: ClientLike>(client: &C, key: RedisKey, member: RedisValue) -> Result<RedisValue, RedisError> {
+  args_value_cmd(client, RedisCommandKind::Zrank, vec![key.into(), member]).await
+}
+
+pub async fn zrem<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  members: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let members = members.into_multiple_values();
+    let mut args = Vec::with_capacity(1 + members.len());
+    args.push(key.into());
+
+    for member in members.into_iter() {
+      args.push(member);
+    }
+    Ok((RedisCommandKind::Zrem, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zremrangebylex<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  min: ZRange,
+  max: ZRange,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    check_range_types(&min, &max, &Some(ZSort::ByLex))?;
+
+    Ok((RedisCommandKind::Zremrangebylex, vec![
+      key.into(),
+      min.into_value()?,
+      max.into_value()?,
+    ]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zremrangebyrank<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  start: i64,
+  stop: i64,
+) -> Result<RedisValue, RedisError> {
+  let (start, stop) = (start.into(), stop.into());
+  args_value_cmd(client, RedisCommandKind::Zremrangebyrank, vec![key.into(), start, stop]).await
+}
+
+pub async fn zremrangebyscore<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  min: ZRange,
+  max: ZRange,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    check_range_types(&min, &max, &Some(ZSort::ByScore))?;
+
+    Ok((RedisCommandKind::Zremrangebyscore, vec![
+      key.into(),
+      min.into_value()?,
+      max.into_value()?,
+    ]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zrevrange<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  start: i64,
+  stop: i64,
+  withscores: bool,
+) -> Result<RedisValue, RedisError> {
+  let (start, stop) = (start.into(), stop.into());
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(4);
+    args.push(key.into());
+    args.push(start);
+    args.push(stop);
+
+    if withscores {
+      args.push(static_val!(WITH_SCORES));
+    }
+
+    Ok((RedisCommandKind::Zrevrange, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zrevrank<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  member: RedisValue,
+) -> Result<RedisValue, RedisError> {
+  args_value_cmd(client, RedisCommandKind::Zrevrank, vec![key.into(), member]).await
+}
+
+pub async fn zscore<C: ClientLike>(client: &C, key: RedisKey, member: RedisValue) -> Result<RedisValue, RedisError> {
+  args_value_cmd(client, RedisCommandKind::Zscore, vec![key.into(), member]).await
+}
+
+pub async fn zunion<C: ClientLike>(
+  client: &C,
+  keys: MultipleKeys,
+  weights: MultipleWeights,
+  aggregate: Option<AggregateOptions>,
+  withscores: bool,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args_len = keys.len() + weights.len();
+    let mut args = Vec::with_capacity(5 + args_len);
+    args.push(keys.len().try_into()?);
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    if weights.len() > 0 {
+      args.push(static_val!(WEIGHTS));
+      for weight in weights.inner().into_iter() {
+        args.push(weight.try_into()?);
+      }
+    }
+
+    if let Some(aggregate) = aggregate {
+      args.push(static_val!(AGGREGATE));
+      args.push(aggregate.to_str().into());
+    }
+    if withscores {
+      args.push(static_val!(WITH_SCORES));
+    }
+
+    Ok((RedisCommandKind::Zunion, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zunionstore<C: ClientLike>(
+  client: &C,
+  dest: RedisKey,
+  keys: MultipleKeys,
+  weights: MultipleWeights,
+  aggregate: Option<AggregateOptions>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args_len = keys.len() + weights.len();
+    let mut args = Vec::with_capacity(5 + args_len);
+    args.push(dest.into());
+    args.push(keys.len().try_into()?);
+
+    for key in keys.inner().into_iter() {
+      args.push(key.into());
+    }
+    if weights.len() > 0 {
+      args.push(static_val!(WEIGHTS));
+      for weight in weights.inner().into_iter() {
+        args.push(weight.try_into()?);
+      }
+    }
+
+    if let Some(aggregate) = aggregate {
+      args.push(static_val!(AGGREGATE));
+      args.push(aggregate.to_str().into());
+    }
+
+    Ok((RedisCommandKind::Zunionstore, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn zmscore<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  members: MultipleValues,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let members = members.into_multiple_values();
+    let mut args = Vec::with_capacity(1 + members.len());
+    args.push(key.into());
+
+    for member in members.into_iter() {
+      args.push(member);
+    }
+    Ok((RedisCommandKind::Zmscore, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/streams.rs.html b/doc/src/fred/commands/impls/streams.rs.html new file mode 100644 index 00000000..622ab420 --- /dev/null +++ b/doc/src/fred/commands/impls/streams.rs.html @@ -0,0 +1,1031 @@ +streams.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+
use super::*;
+use crate::{
+  error::RedisError,
+  protocol::{
+    command::{RedisCommand, RedisCommandKind},
+    hashers::ClusterHash,
+    utils as protocol_utils,
+  },
+  types::{
+    MultipleIDs,
+    MultipleKeys,
+    MultipleOrderedPairs,
+    MultipleStrings,
+    RedisKey,
+    RedisValue,
+    XCap,
+    XPendingArgs,
+    XID,
+  },
+  utils,
+};
+use bytes_utils::Str;
+use std::convert::TryInto;
+
+fn encode_cap(args: &mut Vec<RedisValue>, cap: XCap) {
+  if let Some((kind, trim, threshold, limit)) = cap.into_parts() {
+    args.push(kind.to_str().into());
+    args.push(trim.to_str().into());
+    args.push(threshold.into_arg());
+    if let Some(count) = limit {
+      args.push(static_val!(LIMIT));
+      args.push(count.into());
+    }
+  }
+}
+
+pub async fn xinfo_consumers<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  groupname: Str,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let args = vec![key.into(), groupname.into()];
+    Ok((RedisCommandKind::XinfoConsumers, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xinfo_groups<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || Ok((RedisCommandKind::XinfoGroups, vec![key.into()]))).await?;
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xinfo_stream<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  full: bool,
+  count: Option<u64>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(4);
+    args.push(key.into());
+
+    if full {
+      args.push(static_val!(FULL));
+      if let Some(count) = count {
+        args.push(static_val!(COUNT));
+        args.push(count.try_into()?);
+      }
+    }
+
+    Ok((RedisCommandKind::XinfoStream, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xadd<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  nomkstream: bool,
+  cap: XCap,
+  id: XID,
+  fields: MultipleOrderedPairs,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(8 + (fields.len() * 2));
+    args.push(key.into());
+
+    if nomkstream {
+      args.push(static_val!(NOMKSTREAM));
+    }
+    encode_cap(&mut args, cap);
+
+    args.push(id.into_str().into());
+    for (key, value) in fields.inner().into_iter() {
+      args.push(key.into());
+      args.push(value);
+    }
+
+    Ok((RedisCommandKind::Xadd, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xtrim<C: ClientLike>(client: &C, key: RedisKey, cap: XCap) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(6);
+    args.push(key.into());
+    encode_cap(&mut args, cap);
+
+    Ok((RedisCommandKind::Xtrim, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xdel<C: ClientLike>(client: &C, key: RedisKey, ids: MultipleStrings) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(1 + ids.len());
+    args.push(key.into());
+
+    for id in ids.inner().into_iter() {
+      args.push(id.into());
+    }
+    Ok((RedisCommandKind::Xdel, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xrange<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  start: RedisValue,
+  end: RedisValue,
+  count: Option<u64>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(5);
+    args.push(key.into());
+    args.push(start);
+    args.push(end);
+
+    if let Some(count) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.try_into()?);
+    }
+
+    Ok((RedisCommandKind::Xrange, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xrevrange<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  end: RedisValue,
+  start: RedisValue,
+  count: Option<u64>,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(5);
+    args.push(key.into());
+    args.push(end);
+    args.push(start);
+
+    if let Some(count) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.try_into()?);
+    }
+
+    Ok((RedisCommandKind::Xrevrange, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xlen<C: ClientLike>(client: &C, key: RedisKey) -> Result<RedisValue, RedisError> {
+  one_arg_value_cmd(client, RedisCommandKind::Xlen, key.into()).await
+}
+
+pub async fn xread<C: ClientLike>(
+  client: &C,
+  count: Option<u64>,
+  block: Option<u64>,
+  keys: MultipleKeys,
+  ids: MultipleIDs,
+) -> Result<RedisValue, RedisError> {
+  let is_clustered = client.inner().config.server.is_clustered();
+  let frame = utils::request_response(client, move || {
+    let is_blocking = block.is_some();
+    let mut hash_slot = None;
+    let mut args = Vec::with_capacity(5 + keys.len() + ids.len());
+
+    if let Some(count) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.try_into()?);
+    }
+    if let Some(block) = block {
+      args.push(static_val!(BLOCK));
+      args.push(block.try_into()?);
+    }
+    args.push(static_val!(STREAMS));
+
+    for (idx, key) in keys.inner().into_iter().enumerate() {
+      // set the hash slot from the first key. if any other keys hash into another slot the server will return
+      // CROSSSLOT error
+      if is_clustered && idx == 0 {
+        hash_slot = Some(ClusterHash::Offset(args.len()));
+      }
+
+      args.push(key.into());
+    }
+    for id in ids.inner().into_iter() {
+      args.push(id.into_str().into());
+    }
+
+    let mut command: RedisCommand = (RedisCommandKind::Xread, args).into();
+    command.can_pipeline = !is_blocking;
+    command.hasher = hash_slot.unwrap_or(ClusterHash::Random);
+    Ok(command)
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xgroup_create<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  groupname: Str,
+  id: XID,
+  mkstream: bool,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(4);
+    args.push(key.into());
+    args.push(groupname.into());
+    args.push(id.into_str().into());
+    if mkstream {
+      args.push(static_val!(MKSTREAM));
+    }
+
+    Ok((RedisCommandKind::Xgroupcreate, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xgroup_createconsumer<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  groupname: Str,
+  consumername: Str,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::XgroupCreateConsumer, vec![
+      key.into(),
+      groupname.into(),
+      consumername.into(),
+    ]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xgroup_delconsumer<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  groupname: Str,
+  consumername: Str,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::XgroupDelConsumer, vec![
+      key.into(),
+      groupname.into(),
+      consumername.into(),
+    ]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xgroup_destroy<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  groupname: Str,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::XgroupDestroy, vec![key.into(), groupname.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xgroup_setid<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  groupname: Str,
+  id: XID,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::XgroupSetId, vec![
+      key.into(),
+      groupname.into(),
+      id.into_str().into(),
+    ]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xreadgroup<C: ClientLike>(
+  client: &C,
+  group: Str,
+  consumer: Str,
+  count: Option<u64>,
+  block: Option<u64>,
+  noack: bool,
+  keys: MultipleKeys,
+  ids: MultipleIDs,
+) -> Result<RedisValue, RedisError> {
+  let is_clustered = client.inner().config.server.is_clustered();
+  let frame = utils::request_response(client, move || {
+    let is_blocking = block.is_some();
+    let mut hash_slot = None;
+
+    let mut args = Vec::with_capacity(9 + keys.len() + ids.len());
+    args.push(static_val!(GROUP));
+    args.push(group.into());
+    args.push(consumer.into());
+
+    if let Some(count) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.try_into()?);
+    }
+    if let Some(block) = block {
+      args.push(static_val!(BLOCK));
+      args.push(block.try_into()?);
+    }
+    if noack {
+      args.push(static_val!(NOACK));
+    }
+
+    args.push(static_val!(STREAMS));
+    for (idx, key) in keys.inner().into_iter().enumerate() {
+      if is_clustered && idx == 0 {
+        hash_slot = Some(ClusterHash::Offset(args.len()));
+      }
+
+      args.push(key.into());
+    }
+    for id in ids.inner().into_iter() {
+      args.push(id.into_str().into());
+    }
+
+    let mut command: RedisCommand = (RedisCommandKind::Xreadgroup, args).into();
+    command.can_pipeline = !is_blocking;
+    command.hasher = hash_slot.unwrap_or(ClusterHash::Random);
+    Ok(command)
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xack<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  group: Str,
+  ids: MultipleIDs,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2 + ids.len());
+    args.push(key.into());
+    args.push(group.into());
+
+    for id in ids.inner().into_iter() {
+      args.push(id.into_str().into());
+    }
+    Ok((RedisCommandKind::Xack, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xclaim<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  group: Str,
+  consumer: Str,
+  min_idle_time: u64,
+  ids: MultipleIDs,
+  idle: Option<u64>,
+  time: Option<u64>,
+  retry_count: Option<u64>,
+  force: bool,
+  justid: bool,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(12 + ids.len());
+    args.push(key.into());
+    args.push(group.into());
+    args.push(consumer.into());
+    args.push(min_idle_time.try_into()?);
+
+    for id in ids.inner().into_iter() {
+      args.push(id.into_str().into());
+    }
+    if let Some(idle) = idle {
+      args.push(static_val!(IDLE));
+      args.push(idle.try_into()?);
+    }
+    if let Some(time) = time {
+      args.push(static_val!(TIME));
+      args.push(time.try_into()?);
+    }
+    if let Some(retry_count) = retry_count {
+      args.push(static_val!(RETRYCOUNT));
+      args.push(retry_count.try_into()?);
+    }
+    if force {
+      args.push(static_val!(FORCE));
+    }
+    if justid {
+      args.push(static_val!(JUSTID));
+    }
+
+    Ok((RedisCommandKind::Xclaim, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xautoclaim<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  group: Str,
+  consumer: Str,
+  min_idle_time: u64,
+  start: XID,
+  count: Option<u64>,
+  justid: bool,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(8);
+    args.push(key.into());
+    args.push(group.into());
+    args.push(consumer.into());
+    args.push(min_idle_time.try_into()?);
+    args.push(start.into_str().into());
+
+    if let Some(count) = count {
+      args.push(static_val!(COUNT));
+      args.push(count.try_into()?);
+    }
+    if justid {
+      args.push(static_val!(JUSTID));
+    }
+
+    Ok((RedisCommandKind::Xautoclaim, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn xpending<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  group: Str,
+  cmd_args: XPendingArgs,
+) -> Result<RedisValue, RedisError> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(8);
+    args.push(key.into());
+    args.push(group.into());
+
+    if let Some((idle, start, end, count, consumer)) = cmd_args.into_parts()? {
+      if let Some(idle) = idle {
+        args.push(static_val!(IDLE));
+        args.push(idle.try_into()?);
+      }
+      args.push(start.into_str().into());
+      args.push(end.into_str().into());
+      args.push(count.try_into()?);
+      if let Some(consumer) = consumer {
+        args.push(consumer.into());
+      }
+    }
+
+    Ok((RedisCommandKind::Xpending, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/strings.rs.html b/doc/src/fred/commands/impls/strings.rs.html new file mode 100644 index 00000000..a3aa99fc --- /dev/null +++ b/doc/src/fred/commands/impls/strings.rs.html @@ -0,0 +1,3 @@ +strings.rs - source
1
+

+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/timeseries.rs.html b/doc/src/fred/commands/impls/timeseries.rs.html new file mode 100644 index 00000000..e2b878c2 --- /dev/null +++ b/doc/src/fred/commands/impls/timeseries.rs.html @@ -0,0 +1,1149 @@ +timeseries.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+
use crate::{
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  prelude::RedisKey,
+  protocol::{command::RedisCommandKind, utils as protocol_utils},
+  types::{
+    Aggregator,
+    DuplicatePolicy,
+    Encoding,
+    GetLabels,
+    GetTimestamp,
+    GroupBy,
+    RangeAggregation,
+    RedisMap,
+    RedisValue,
+    Timestamp,
+  },
+  utils,
+};
+use bytes_utils::Str;
+
+static LATEST: &str = "LATEST";
+static FILTER_BY_TS: &str = "FILTER_BY_TS";
+static FILTER_BY_VALUE: &str = "FILTER_BY_VALUE";
+static COUNT: &str = "COUNT";
+static ALIGN: &str = "ALIGN";
+static AGGREGATION: &str = "AGGREGATION";
+static BUCKETTIMESTAMP: &str = "BUCKETTIMESTAMP";
+static EMPTY: &str = "EMPTY";
+static WITHLABELS: &str = "WITHLABELS";
+static SELECTED_LABELS: &str = "SELECTED_LABELS";
+static FILTER: &str = "FILTER";
+static GROUPBY: &str = "GROUPBY";
+static REDUCE: &str = "REDUCE";
+static RETENTION: &str = "RETENTION";
+static ENCODING: &str = "ENCODING";
+static CHUNK_SIZE: &str = "CHUNK_SIZE";
+static ON_DUPLICATE: &str = "ON_DUPLICATE";
+static DUPLICATE_POLICY: &str = "DUPLICATE_POLICY";
+static LABELS: &str = "LABELS";
+static UNCOMPRESSED: &str = "UNCOMPRESSED";
+static TIMESTAMP: &str = "TIMESTAMP";
+static DEBUG: &str = "DEBUG";
+
+fn add_labels(args: &mut Vec<RedisValue>, labels: RedisMap) {
+  if !labels.is_empty() {
+    args.push(static_val!(LABELS));
+
+    for (label, value) in labels.inner().into_iter() {
+      args.push(label.into());
+      args.push(value);
+    }
+  }
+}
+
+fn add_retention(args: &mut Vec<RedisValue>, retention: Option<u64>) -> Result<(), RedisError> {
+  if let Some(retention) = retention {
+    args.push(static_val!(RETENTION));
+    args.push(retention.try_into()?);
+  }
+
+  Ok(())
+}
+
+fn add_encoding(args: &mut Vec<RedisValue>, encoding: Option<Encoding>) {
+  if let Some(encoding) = encoding {
+    args.push(static_val!(ENCODING));
+    args.push(encoding.to_str().into());
+  }
+}
+
+fn add_chunk_size(args: &mut Vec<RedisValue>, chunk_size: Option<u64>) -> Result<(), RedisError> {
+  if let Some(chunk_size) = chunk_size {
+    args.push(static_val!(CHUNK_SIZE));
+    args.push(chunk_size.try_into()?);
+  }
+
+  Ok(())
+}
+
+fn add_timestamp(args: &mut Vec<RedisValue>, timestamp: Option<Timestamp>) {
+  if let Some(timestamp) = timestamp {
+    args.push(static_val!(TIMESTAMP));
+    args.push(timestamp.to_value());
+  }
+}
+
+fn add_duplicate_policy(args: &mut Vec<RedisValue>, duplicate_policy: Option<DuplicatePolicy>) {
+  if let Some(duplicate) = duplicate_policy {
+    args.push(static_val!(DUPLICATE_POLICY));
+    args.push(duplicate.to_str().into());
+  }
+}
+
+fn add_count(args: &mut Vec<RedisValue>, count: Option<u64>) -> Result<(), RedisError> {
+  if let Some(count) = count {
+    args.push(static_val!(COUNT));
+    args.push(count.try_into()?);
+  }
+  Ok(())
+}
+
+fn add_get_labels(args: &mut Vec<RedisValue>, labels: Option<GetLabels>) {
+  if let Some(labels) = labels {
+    match labels {
+      GetLabels::WithLabels => args.push(static_val!(WITHLABELS)),
+      GetLabels::SelectedLabels(labels) => {
+        args.push(static_val!(SELECTED_LABELS));
+        args.extend(labels.into_iter().map(|v| v.into()));
+      },
+    }
+  }
+}
+
+fn add_range_aggregation(
+  args: &mut Vec<RedisValue>,
+  aggregation: Option<RangeAggregation>,
+) -> Result<(), RedisError> {
+  if let Some(aggregation) = aggregation {
+    if let Some(align) = aggregation.align {
+      args.push(static_val!(ALIGN));
+      args.push(align.to_value());
+    }
+
+    args.push(static_val!(AGGREGATION));
+    args.push(aggregation.aggregation.to_str().into());
+    args.push(aggregation.bucket_duration.try_into()?);
+
+    if let Some(bucket_timestamp) = aggregation.bucket_timestamp {
+      args.push(static_val!(BUCKETTIMESTAMP));
+      args.push(bucket_timestamp.to_str().into());
+    }
+    if aggregation.empty {
+      args.push(static_val!(EMPTY));
+    }
+  }
+
+  Ok(())
+}
+
+fn add_groupby(args: &mut Vec<RedisValue>, group_by: Option<GroupBy>) {
+  if let Some(group_by) = group_by {
+    args.push(static_val!(GROUPBY));
+    args.push(group_by.groupby.into());
+    args.push(static_val!(REDUCE));
+    args.push(group_by.reduce.to_str().into());
+  }
+}
+
+fn add_filters(args: &mut Vec<RedisValue>, filters: Vec<Str>) {
+  args.push(static_val!(FILTER));
+  args.extend(filters.into_iter().map(|s| s.into()));
+}
+
+pub async fn ts_add<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  timestamp: Timestamp,
+  value: f64,
+  retention: Option<u64>,
+  encoding: Option<Encoding>,
+  chunk_size: Option<u64>,
+  on_duplicate: Option<DuplicatePolicy>,
+  labels: RedisMap,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(12 + labels.len() * 2);
+    args.push(key.into());
+    args.push(timestamp.to_value());
+    args.push(value.into());
+
+    add_retention(&mut args, retention)?;
+    add_encoding(&mut args, encoding);
+    add_chunk_size(&mut args, chunk_size)?;
+    if let Some(duplicate) = on_duplicate {
+      args.push(static_val!(ON_DUPLICATE));
+      args.push(duplicate.to_str().into());
+    }
+
+    add_labels(&mut args, labels);
+    Ok((RedisCommandKind::TsAdd, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_alter<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  retention: Option<u64>,
+  chunk_size: Option<u64>,
+  duplicate_policy: Option<DuplicatePolicy>,
+  labels: RedisMap,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(8 + labels.len() * 2);
+    args.push(key.into());
+
+    add_retention(&mut args, retention)?;
+    add_chunk_size(&mut args, chunk_size)?;
+    add_duplicate_policy(&mut args, duplicate_policy);
+    add_labels(&mut args, labels);
+    Ok((RedisCommandKind::TsAlter, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_create<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  retention: Option<u64>,
+  encoding: Option<Encoding>,
+  chunk_size: Option<u64>,
+  duplicate_policy: Option<DuplicatePolicy>,
+  labels: RedisMap,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(10 + labels.len() * 2);
+    args.push(key.into());
+
+    add_retention(&mut args, retention)?;
+    add_encoding(&mut args, encoding);
+    add_chunk_size(&mut args, chunk_size)?;
+    add_duplicate_policy(&mut args, duplicate_policy);
+    add_labels(&mut args, labels);
+    Ok((RedisCommandKind::TsCreate, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_createrule<C: ClientLike>(
+  client: &C,
+  src: RedisKey,
+  dest: RedisKey,
+  aggregation: (Aggregator, u64),
+  align_timestamp: Option<u64>,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(6);
+    args.extend([
+      src.into(),
+      dest.into(),
+      static_val!(AGGREGATION),
+      aggregation.0.to_str().into(),
+      aggregation.1.try_into()?,
+    ]);
+
+    if let Some(align) = align_timestamp {
+      args.push(align.try_into()?)
+    }
+    Ok((RedisCommandKind::TsCreateRule, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_decrby<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  subtrahend: f64,
+  timestamp: Option<Timestamp>,
+  retention: Option<u64>,
+  uncompressed: bool,
+  chunk_size: Option<u64>,
+  labels: RedisMap,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(10 + labels.len() * 2);
+    args.push(key.into());
+    args.push(subtrahend.into());
+
+    add_timestamp(&mut args, timestamp);
+    add_retention(&mut args, retention)?;
+    if uncompressed {
+      args.push(static_val!(UNCOMPRESSED));
+    }
+    add_chunk_size(&mut args, chunk_size)?;
+    add_labels(&mut args, labels);
+
+    Ok((RedisCommandKind::TsDecrBy, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_del<C: ClientLike>(client: &C, key: RedisKey, from: i64, to: i64) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::TsDel, vec![key.into(), from.into(), to.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_deleterule<C: ClientLike>(client: &C, src: RedisKey, dest: RedisKey) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    Ok((RedisCommandKind::TsDeleteRule, vec![src.into(), dest.into()]))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_get<C: ClientLike>(client: &C, key: RedisKey, latest: bool) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2);
+    args.push(key.into());
+    if latest {
+      args.push(static_val!(LATEST));
+    }
+
+    Ok((RedisCommandKind::TsGet, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_incrby<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  addend: f64,
+  timestamp: Option<Timestamp>,
+  retention: Option<u64>,
+  uncompressed: bool,
+  chunk_size: Option<u64>,
+  labels: RedisMap,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(10 + labels.len() * 2);
+    args.push(key.into());
+    args.push(addend.into());
+
+    add_timestamp(&mut args, timestamp);
+    add_retention(&mut args, retention)?;
+    if uncompressed {
+      args.push(static_val!(UNCOMPRESSED));
+    }
+    add_chunk_size(&mut args, chunk_size)?;
+    add_labels(&mut args, labels);
+
+    Ok((RedisCommandKind::TsIncrBy, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_info<C: ClientLike>(client: &C, key: RedisKey, debug: bool) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(2);
+    args.push(key.into());
+    if debug {
+      args.push(static_val!(DEBUG));
+    }
+
+    Ok((RedisCommandKind::TsInfo, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_madd<C: ClientLike>(client: &C, samples: Vec<(RedisKey, Timestamp, f64)>) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(samples.len() * 3);
+    for (key, timestamp, value) in samples.into_iter() {
+      args.extend([key.into(), timestamp.to_value(), value.into()]);
+    }
+    Ok((RedisCommandKind::TsMAdd, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_mget<C: ClientLike>(
+  client: &C,
+  latest: bool,
+  labels: Option<GetLabels>,
+  filters: Vec<Str>,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let labels_len = labels.as_ref().map(|l| l.args_len()).unwrap_or(0);
+    let mut args = Vec::with_capacity(2 + labels_len + filters.len());
+    if latest {
+      args.push(static_val!(LATEST));
+    }
+    add_get_labels(&mut args, labels);
+    add_filters(&mut args, filters);
+
+    Ok((RedisCommandKind::TsMGet, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_mrange<C: ClientLike>(
+  client: &C,
+  from: GetTimestamp,
+  to: GetTimestamp,
+  latest: bool,
+  filter_by_ts: Vec<i64>,
+  filter_by_value: Option<(i64, i64)>,
+  labels: Option<GetLabels>,
+  count: Option<u64>,
+  aggregation: Option<RangeAggregation>,
+  filters: Vec<Str>,
+  group_by: Option<GroupBy>,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let labels_len = labels.as_ref().map(|l| l.args_len()).unwrap_or(0);
+    let mut args = Vec::with_capacity(18 + filter_by_ts.len() + labels_len + filters.len());
+
+    args.extend([from.to_value(), to.to_value()]);
+    if latest {
+      args.push(static_val!(LATEST));
+    }
+    if !filter_by_ts.is_empty() {
+      args.push(static_val!(FILTER_BY_TS));
+      args.extend(filter_by_ts.into_iter().map(|t| t.into()));
+    }
+    if let Some((min, max)) = filter_by_value {
+      args.push(static_val!(FILTER_BY_VALUE));
+      args.extend([min.into(), max.into()]);
+    }
+    add_get_labels(&mut args, labels);
+    add_count(&mut args, count)?;
+    add_range_aggregation(&mut args, aggregation)?;
+    add_filters(&mut args, filters);
+    add_groupby(&mut args, group_by);
+
+    Ok((RedisCommandKind::TsMRange, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_mrevrange<C: ClientLike>(
+  client: &C,
+  from: GetTimestamp,
+  to: GetTimestamp,
+  latest: bool,
+  filter_by_ts: Vec<i64>,
+  filter_by_value: Option<(i64, i64)>,
+  labels: Option<GetLabels>,
+  count: Option<u64>,
+  aggregation: Option<RangeAggregation>,
+  filters: Vec<Str>,
+  group_by: Option<GroupBy>,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let labels_len = labels.as_ref().map(|l| l.args_len()).unwrap_or(0);
+    let mut args = Vec::with_capacity(18 + filter_by_ts.len() + labels_len + filters.len());
+
+    args.extend([from.to_value(), to.to_value()]);
+    if latest {
+      args.push(static_val!(LATEST));
+    }
+    if !filter_by_ts.is_empty() {
+      args.push(static_val!(FILTER_BY_TS));
+      args.extend(filter_by_ts.into_iter().map(|t| t.into()));
+    }
+    if let Some((min, max)) = filter_by_value {
+      args.push(static_val!(FILTER_BY_VALUE));
+      args.extend([min.into(), max.into()]);
+    }
+    add_get_labels(&mut args, labels);
+    add_count(&mut args, count)?;
+    add_range_aggregation(&mut args, aggregation)?;
+    add_filters(&mut args, filters);
+    add_groupby(&mut args, group_by);
+
+    Ok((RedisCommandKind::TsMRevRange, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_queryindex<C: ClientLike>(client: &C, filters: Vec<Str>) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    Ok((
+      RedisCommandKind::TsQueryIndex,
+      filters.into_iter().map(|v| v.into()).collect(),
+    ))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_range<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  from: GetTimestamp,
+  to: GetTimestamp,
+  latest: bool,
+  filter_by_ts: Vec<i64>,
+  filter_by_value: Option<(i64, i64)>,
+  count: Option<u64>,
+  aggregation: Option<RangeAggregation>,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(14 + filter_by_ts.len());
+    args.push(key.into());
+    args.extend([from.to_value(), to.to_value()]);
+
+    if latest {
+      args.push(static_val!(LATEST));
+    }
+    if !filter_by_ts.is_empty() {
+      args.push(static_val!(FILTER_BY_TS));
+      args.extend(filter_by_ts.into_iter().map(|v| v.into()));
+    }
+    if let Some((min, max)) = filter_by_value {
+      args.push(static_val!(FILTER_BY_VALUE));
+      args.extend([min.into(), max.into()]);
+    }
+    add_count(&mut args, count)?;
+    add_range_aggregation(&mut args, aggregation)?;
+
+    Ok((RedisCommandKind::TsRange, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
+pub async fn ts_revrange<C: ClientLike>(
+  client: &C,
+  key: RedisKey,
+  from: GetTimestamp,
+  to: GetTimestamp,
+  latest: bool,
+  filter_by_ts: Vec<i64>,
+  filter_by_value: Option<(i64, i64)>,
+  count: Option<u64>,
+  aggregation: Option<RangeAggregation>,
+) -> RedisResult<RedisValue> {
+  let frame = utils::request_response(client, move || {
+    let mut args = Vec::with_capacity(14 + filter_by_ts.len());
+    args.push(key.into());
+    args.extend([from.to_value(), to.to_value()]);
+
+    if latest {
+      args.push(static_val!(LATEST));
+    }
+    if !filter_by_ts.is_empty() {
+      args.push(static_val!(FILTER_BY_TS));
+      args.extend(filter_by_ts.into_iter().map(|v| v.into()));
+    }
+    if let Some((min, max)) = filter_by_value {
+      args.push(static_val!(FILTER_BY_VALUE));
+      args.extend([min.into(), max.into()]);
+    }
+    add_count(&mut args, count)?;
+    add_range_aggregation(&mut args, aggregation)?;
+
+    Ok((RedisCommandKind::TsRevRange, args))
+  })
+  .await?;
+
+  protocol_utils::frame_to_results(frame)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/impls/tracking.rs.html b/doc/src/fred/commands/impls/tracking.rs.html new file mode 100644 index 00000000..13f63e1d --- /dev/null +++ b/doc/src/fred/commands/impls/tracking.rs.html @@ -0,0 +1,343 @@ +tracking.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  interfaces::ClientLike,
+  protocol::{
+    command::{RedisCommand, RedisCommandKind},
+    responders::ResponseKind,
+    utils as protocol_utils,
+  },
+  runtime::oneshot_channel,
+  types::{ClusterHash, MultipleStrings, RedisValue, Toggle},
+  utils,
+};
+use redis_protocol::redis_keyslot;
+
+pub static PREFIX: &str = "PREFIX";
+pub static REDIRECT: &str = "REDIRECT";
+pub static BCAST: &str = "BCAST";
+pub static OPTIN: &str = "OPTIN";
+pub static OPTOUT: &str = "OPTOUT";
+pub static NOLOOP: &str = "NOLOOP";
+pub static YES: &str = "YES";
+pub static NO: &str = "NO";
+
+fn tracking_args(
+  toggle: Toggle,
+  redirect: Option<i64>,
+  prefixes: MultipleStrings,
+  bcast: bool,
+  optin: bool,
+  optout: bool,
+  noloop: bool,
+) -> Vec<RedisValue> {
+  let mut args = Vec::with_capacity(prefixes.len() * 2 + 7);
+  args.push(static_val!(toggle.to_str()));
+  if let Some(redirect) = redirect {
+    args.push(static_val!(REDIRECT));
+    args.push(redirect.into());
+  }
+  for prefix in prefixes.inner().into_iter() {
+    args.push(static_val!(PREFIX));
+    args.push(prefix.into());
+  }
+  if bcast {
+    args.push(static_val!(BCAST));
+  }
+  if optin {
+    args.push(static_val!(OPTIN));
+  }
+  if optout {
+    args.push(static_val!(OPTOUT));
+  }
+  if noloop {
+    args.push(static_val!(NOLOOP));
+  }
+
+  args
+}
+
+pub async fn start_tracking<C: ClientLike>(
+  client: &C,
+  prefixes: MultipleStrings,
+  bcast: bool,
+  optin: bool,
+  optout: bool,
+  noloop: bool,
+) -> Result<(), RedisError> {
+  if !client.inner().is_resp3() {
+    return Err(RedisError::new(
+      RedisErrorKind::Config,
+      "Client tracking requires RESP3.",
+    ));
+  }
+
+  let args = tracking_args(Toggle::On, None, prefixes, bcast, optin, optout, noloop);
+  if client.inner().config.server.is_clustered() {
+    if bcast {
+      // only send the tracking command on one connection when in bcast mode
+      let frame = utils::request_response(client, move || {
+        let mut command = RedisCommand::new(RedisCommandKind::ClientTracking, args);
+        command.hasher = ClusterHash::Custom(redis_keyslot(client.id().as_bytes()));
+        Ok(command)
+      })
+      .await?;
+
+      protocol_utils::frame_to_results(frame)?.convert()
+    } else {
+      // send the tracking command to all nodes when not in bcast mode
+      let (tx, rx) = oneshot_channel();
+      let response = ResponseKind::new_buffer(tx);
+      let command: RedisCommand = (RedisCommandKind::_ClientTrackingCluster, args, response).into();
+      client.send_command(command)?;
+
+      let frame = utils::timeout(rx, client.inner().internal_command_timeout()).await??;
+      let _ = protocol_utils::frame_to_results(frame)?;
+      Ok(())
+    }
+  } else {
+    utils::request_response(client, move || Ok((RedisCommandKind::ClientTracking, args)))
+      .await
+      .and_then(protocol_utils::frame_to_results)
+      .and_then(|v| v.convert())
+  }
+}
+
+pub async fn stop_tracking<C: ClientLike>(client: &C) -> Result<(), RedisError> {
+  if !client.inner().is_resp3() {
+    return Err(RedisError::new(
+      RedisErrorKind::Config,
+      "Client tracking requires RESP3.",
+    ));
+  }
+
+  let args = vec![static_val!(Toggle::Off.to_str())];
+  if client.is_clustered() {
+    // turn off tracking on all connections
+    let (tx, rx) = oneshot_channel();
+    let response = ResponseKind::new_buffer(tx);
+    let command: RedisCommand = (RedisCommandKind::_ClientTrackingCluster, args, response).into();
+    client.send_command(command)?;
+
+    let frame = utils::timeout(rx, client.inner().internal_command_timeout()).await??;
+    let _ = protocol_utils::frame_to_results(frame)?;
+    Ok(())
+  } else {
+    utils::request_response(client, move || Ok((RedisCommandKind::ClientTracking, args)))
+      .await
+      .and_then(protocol_utils::frame_to_results)
+      .and_then(|v| v.convert())
+  }
+}
+
+pub async fn client_tracking<C: ClientLike>(
+  client: &C,
+  toggle: Toggle,
+  redirect: Option<i64>,
+  prefixes: MultipleStrings,
+  bcast: bool,
+  optin: bool,
+  optout: bool,
+  noloop: bool,
+) -> Result<RedisValue, RedisError> {
+  let args = tracking_args(toggle, redirect, prefixes, bcast, optin, optout, noloop);
+
+  utils::request_response(client, move || Ok((RedisCommandKind::ClientTracking, args)))
+    .await
+    .and_then(protocol_utils::frame_to_results)
+}
+
+pub async fn client_trackinginfo<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  utils::request_response(client, move || Ok((RedisCommandKind::ClientTrackingInfo, vec![])))
+    .await
+    .and_then(protocol_utils::frame_to_results)
+}
+
+pub async fn client_getredir<C: ClientLike>(client: &C) -> Result<RedisValue, RedisError> {
+  utils::request_response(client, move || Ok((RedisCommandKind::ClientGetRedir, vec![])))
+    .await
+    .and_then(protocol_utils::frame_to_results)
+}
+
+pub async fn client_caching<C: ClientLike>(client: &C, enabled: bool) -> Result<RedisValue, RedisError> {
+  let args = if enabled {
+    vec![static_val!(YES)]
+  } else {
+    vec![static_val!(NO)]
+  };
+
+  utils::request_response(client, move || Ok((RedisCommandKind::ClientCaching, args)))
+    .await
+    .and_then(protocol_utils::frame_to_results)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/acl.rs.html b/doc/src/fred/commands/interfaces/acl.rs.html new file mode 100644 index 00000000..e8b65d6c --- /dev/null +++ b/doc/src/fred/commands/interfaces/acl.rs.html @@ -0,0 +1,285 @@ +acl.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  types::{FromRedis, MultipleStrings, MultipleValues},
+};
+use bytes_utils::Str;
+use futures::Future;
+use rm_send_macros::rm_send_if;
+
+/// Functions that implement the [ACL](https://redis.io/commandserver) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait AclInterface: ClientLike + Sized {
+  /// Create an ACL user with the specified rules or modify the rules of an existing user.
+  ///
+  /// <https://redis.io/commands/acl-setuser>
+  fn acl_setuser<S, V>(&self, username: S, rules: V) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<Str> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(username);
+      try_into!(rules);
+      commands::acl::acl_setuser(self, username, rules).await
+    }
+  }
+
+  /// When Redis is configured to use an ACL file (with the aclfile configuration option), this command will reload
+  /// the ACLs from the file, replacing all the current ACL rules with the ones defined in the file.
+  ///
+  /// <https://redis.io/commands/acl-load>
+  fn acl_load(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::acl::acl_load(self).await }
+  }
+
+  /// When Redis is configured to use an ACL file (with the aclfile configuration option), this command will save the
+  /// currently defined ACLs from the server memory to the ACL file.
+  ///
+  /// <https://redis.io/commands/acl-save>
+  fn acl_save(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::acl::acl_save(self).await }
+  }
+
+  /// The command shows the currently active ACL rules in the Redis server.
+  ///
+  /// <https://redis.io/commands/acl-list>\
+  fn acl_list<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::acl::acl_list(self).await?.convert() }
+  }
+
+  /// The command shows a list of all the usernames of the currently configured users in the Redis ACL system.
+  ///
+  /// <https://redis.io/commands/acl-users>
+  fn acl_users<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::acl::acl_users(self).await?.convert() }
+  }
+
+  /// The command returns all the rules defined for an existing ACL user.
+  ///
+  /// <https://redis.io/commands/acl-getuser>
+  fn acl_getuser<R, S>(&self, username: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(username);
+      commands::acl::acl_getuser(self, username).await?.convert()
+    }
+  }
+
+  /// Delete all the specified ACL users and terminate all the connections that are authenticated with such users.
+  ///
+  /// <https://redis.io/commands/acl-deluser>
+  fn acl_deluser<R, S>(&self, usernames: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(usernames);
+      commands::acl::acl_deluser(self, usernames).await?.convert()
+    }
+  }
+
+  /// The command shows the available ACL categories if called without arguments. If a category name is given,
+  /// the command shows all the Redis commands in the specified category.
+  ///
+  /// <https://redis.io/commands/acl-cat>
+  fn acl_cat<R>(&self, category: Option<Str>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::acl::acl_cat(self, category).await?.convert() }
+  }
+
+  /// Generate a password with length `bits`, returning the password.
+  ///
+  /// <https://redis.io/commands/acl-genpass>
+  fn acl_genpass<R>(&self, bits: Option<u16>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::acl::acl_genpass(self, bits).await?.convert() }
+  }
+
+  /// Return the username the current connection is authenticated with. New connections are authenticated
+  /// with the "default" user.
+  ///
+  /// <https://redis.io/commands/acl-whoami>
+  fn acl_whoami<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::acl::acl_whoami(self).await?.convert() }
+  }
+
+  /// Read `count` recent ACL security events.
+  ///
+  /// <https://redis.io/commands/acl-log>
+  fn acl_log_count<R>(&self, count: Option<u32>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::acl::acl_log_count(self, count).await?.convert() }
+  }
+
+  /// Clear the ACL security events logs.
+  ///
+  /// <https://redis.io/commands/acl-log>
+  fn acl_log_reset(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::acl::acl_log_reset(self).await }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/client.rs.html b/doc/src/fred/commands/interfaces/client.rs.html new file mode 100644 index 00000000..092a234a --- /dev/null +++ b/doc/src/fred/commands/interfaces/client.rs.html @@ -0,0 +1,487 @@ +client.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+
use crate::{
+  commands,
+  interfaces::{ClientLike, RedisResult},
+  types::{
+    ClientKillFilter,
+    ClientKillType,
+    ClientPauseKind,
+    ClientReplyFlag,
+    ClientUnblockFlag,
+    FromRedis,
+    RedisValue,
+    Server,
+  },
+};
+#[cfg(feature = "i-tracking")]
+use crate::{
+  error::RedisError,
+  types::{MultipleStrings, Toggle},
+};
+use bytes_utils::Str;
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use std::collections::HashMap;
+
+/// Functions that implement the [client](https://redis.io/commands#connection) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait ClientInterface: ClientLike + Sized {
+  /// Return the ID of the current connection.
+  ///
+  /// Note: Against a clustered deployment this will return the ID of a random connection. See
+  /// [connection_ids](Self::connection_ids) for  more information.
+  ///
+  /// <https://redis.io/commands/client-id>
+  fn client_id<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::client::client_id(self).await?.convert() }
+  }
+
+  /// Read the connection IDs for the active connections to each server.
+  ///
+  /// The returned map contains each server's `host:port` and the result of calling `CLIENT ID` on the connection.
+  ///
+  /// Note: despite being async this function will return cached information from the client if possible.
+  fn connection_ids(&self) -> impl Future<Output = HashMap<Server, i64>> + Send {
+    async move { self.inner().backchannel.write().await.connection_ids.clone() }
+  }
+
+  /// The command returns information and statistics about the current client connection in a mostly human readable
+  /// format.
+  ///
+  /// <https://redis.io/commands/client-info>
+  fn client_info<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::client::client_info(self).await?.convert() }
+  }
+
+  /// Close a given connection or set of connections.
+  ///
+  /// <https://redis.io/commands/client-kill>
+  fn client_kill<R>(&self, filters: Vec<ClientKillFilter>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::client::client_kill(self, filters).await?.convert() }
+  }
+
+  /// The CLIENT LIST command returns information and statistics about the client connections server in a mostly human
+  /// readable format.
+  ///
+  /// <https://redis.io/commands/client-list>
+  fn client_list<R, I>(
+    &self,
+    r#type: Option<ClientKillType>,
+    ids: Option<Vec<String>>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::client::client_list(self, r#type, ids).await?.convert() }
+  }
+
+  /// The CLIENT GETNAME returns the name of the current connection as set by CLIENT SETNAME.
+  ///
+  /// <https://redis.io/commands/client-getname>
+  fn client_getname<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::client::client_getname(self).await?.convert() }
+  }
+
+  /// Assign a name to the current connection.
+  ///
+  /// **Note: The client automatically generates a unique name for each client that is shared by all underlying
+  /// connections. Use `self.id() to read the automatically generated name.**
+  ///
+  /// <https://redis.io/commands/client-setname>
+  fn client_setname<S>(&self, name: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(name);
+      commands::client::client_setname(self, name).await
+    }
+  }
+
+  /// CLIENT PAUSE is a connections control command able to suspend all the Redis clients for the specified amount of
+  /// time (in milliseconds).
+  ///
+  /// <https://redis.io/commands/client-pause>
+  fn client_pause(
+    &self,
+    timeout: i64,
+    mode: Option<ClientPauseKind>,
+  ) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::client::client_pause(self, timeout, mode).await }
+  }
+
+  /// CLIENT UNPAUSE is used to resume command processing for all clients that were paused by CLIENT PAUSE.
+  ///
+  /// <https://redis.io/commands/client-unpause>
+  fn client_unpause(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::client::client_unpause(self).await }
+  }
+
+  /// The CLIENT REPLY command controls whether the server will reply the client's commands. The following modes are
+  /// available:
+  ///
+  /// <https://redis.io/commands/client-reply>
+  fn client_reply(&self, flag: ClientReplyFlag) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::client::client_reply(self, flag).await }
+  }
+
+  /// This command can unblock, from a different connection, a client blocked in a blocking operation, such as for
+  /// instance BRPOP or XREAD or WAIT.
+  ///
+  /// Note: this command is sent on a backchannel connection and will work even when the main connection is blocked.
+  ///
+  /// <https://redis.io/commands/client-unblock>
+  fn client_unblock<R, S>(
+    &self,
+    id: S,
+    flag: Option<ClientUnblockFlag>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<RedisValue> + Send,
+  {
+    async move {
+      into!(id);
+      commands::client::client_unblock(self, id, flag).await?.convert()
+    }
+  }
+
+  /// A convenience function to unblock any blocked connection on this client.
+  fn unblock_self(&self, flag: Option<ClientUnblockFlag>) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::client::unblock_self(self, flag).await }
+  }
+
+  /// This command enables the tracking feature of the Redis server that is used for server assisted client side
+  /// caching.
+  ///
+  /// <https://redis.io/commands/client-tracking/>
+  ///
+  /// This function is designed to work against a specific server, either via a centralized server config or
+  /// [with_options](crate::interfaces::ClientLike::with_options). See
+  /// [crate::interfaces::TrackingInterface::start_tracking] for a version that works with all server deployment
+  /// modes.
+  #[cfg(feature = "i-tracking")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+  fn client_tracking<R, T, P>(
+    &self,
+    toggle: T,
+    redirect: Option<i64>,
+    prefixes: P,
+    bcast: bool,
+    optin: bool,
+    optout: bool,
+    noloop: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    T: TryInto<Toggle> + Send,
+    T::Error: Into<RedisError> + Send,
+    P: Into<MultipleStrings> + Send,
+  {
+    async move {
+      try_into!(toggle);
+      into!(prefixes);
+      commands::tracking::client_tracking(self, toggle, redirect, prefixes, bcast, optin, optout, noloop)
+        .await?
+        .convert()
+    }
+  }
+
+  /// The command returns information about the current client connection's use of the server assisted client side
+  /// caching feature.
+  ///
+  /// <https://redis.io/commands/client-trackinginfo/>
+
+  #[cfg(feature = "i-tracking")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+  fn client_trackinginfo<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::tracking::client_trackinginfo(self).await?.convert() }
+  }
+
+  /// This command returns the client ID we are redirecting our tracking notifications to.
+  ///
+  /// <https://redis.io/commands/client-getredir/>
+  #[cfg(feature = "i-tracking")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+  fn client_getredir<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::tracking::client_getredir(self).await?.convert() }
+  }
+
+  /// This command controls the tracking of the keys in the next command executed by the connection, when tracking is
+  /// enabled in OPTIN or OPTOUT mode.
+  ///
+  /// <https://redis.io/commands/client-caching/>
+  ///
+  /// This function is designed to work against a specific server. See
+  /// [with_options](crate::interfaces::ClientLike::with_options) for a variation that works with all deployment
+  /// types.
+  #[cfg(feature = "i-tracking")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+  fn client_caching<R>(&self, enabled: bool) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::tracking::client_caching(self, enabled).await?.convert() }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/cluster.rs.html b/doc/src/fred/commands/interfaces/cluster.rs.html new file mode 100644 index 00000000..35bdab13 --- /dev/null +++ b/doc/src/fred/commands/interfaces/cluster.rs.html @@ -0,0 +1,547 @@ +cluster.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  protocol::types::ClusterRouting,
+  types::{ClusterFailoverFlag, ClusterResetFlag, ClusterSetSlotState, FromRedis, MultipleHashSlots, RedisKey},
+};
+use bytes_utils::Str;
+use futures::Future;
+use rm_send_macros::rm_send_if;
+
+/// Functions that implement the [cluster](https://redis.io/commands#cluster) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait ClusterInterface: ClientLike + Sized {
+  /// Read the cached cluster state used for routing commands to the correct cluster nodes.
+  fn cached_cluster_state(&self) -> Option<ClusterRouting> {
+    self.inner().with_cluster_state(|state| Ok(state.clone())).ok()
+  }
+
+  /// Read the number of known primary cluster nodes, or `0` if the cluster state is not known.
+  fn num_primary_cluster_nodes(&self) -> usize {
+    self
+      .inner()
+      .with_cluster_state(|state| Ok(state.unique_primary_nodes().len()))
+      .unwrap_or(0)
+  }
+
+  /// Update the cached cluster state and add or remove any changed cluster node connections.
+  fn sync_cluster(&self) -> impl Future<Output = Result<(), RedisError>> + Send {
+    async move { commands::cluster::sync_cluster(self).await }
+  }
+
+  /// Advances the cluster config epoch.
+  ///
+  /// <https://redis.io/commands/cluster-bumpepoch>
+  fn cluster_bumpepoch<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::cluster::cluster_bumpepoch(self).await?.convert() }
+  }
+
+  /// Deletes all slots from a node.
+  ///
+  /// <https://redis.io/commands/cluster-flushslots>
+  fn cluster_flushslots(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::cluster::cluster_flushslots(self).await }
+  }
+
+  /// Returns the node's id.
+  ///
+  /// <https://redis.io/commands/cluster-myid>
+  fn cluster_myid<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::cluster::cluster_myid(self).await?.convert() }
+  }
+
+  /// Read the current cluster node configuration.
+  ///
+  /// Note: The client keeps a cached, parsed version of the cluster state in memory available at
+  /// [cached_cluster_state](Self::cached_cluster_state).
+  ///
+  /// <https://redis.io/commands/cluster-nodes>
+  fn cluster_nodes<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::cluster::cluster_nodes(self).await?.convert() }
+  }
+
+  /// Forces a node to save the nodes.conf configuration on disk.
+  ///
+  /// <https://redis.io/commands/cluster-saveconfig>
+  fn cluster_saveconfig(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::cluster::cluster_saveconfig(self).await }
+  }
+
+  /// CLUSTER SLOTS returns details about which cluster slots map to which Redis instances.
+  ///
+  /// <https://redis.io/commands/cluster-slots>
+  fn cluster_slots<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::cluster::cluster_slots(self).await?.convert() }
+  }
+
+  /// CLUSTER INFO provides INFO style information about Redis Cluster vital parameters.
+  ///
+  /// <https://redis.io/commands/cluster-info>
+  fn cluster_info<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::cluster::cluster_info(self).await?.convert() }
+  }
+
+  /// This command is useful in order to modify a node's view of the cluster configuration. Specifically it assigns a
+  /// set of hash slots to the node receiving the command.
+  ///
+  /// <https://redis.io/commands/cluster-addslots>
+  fn cluster_add_slots<S>(&self, slots: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<MultipleHashSlots> + Send,
+  {
+    async move {
+      into!(slots);
+      commands::cluster::cluster_add_slots(self, slots).await
+    }
+  }
+
+  /// The command returns the number of failure reports for the specified node.
+  ///
+  /// <https://redis.io/commands/cluster-count-failure-reports>
+  fn cluster_count_failure_reports<R, S>(&self, node_id: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(node_id);
+      commands::cluster::cluster_count_failure_reports(self, node_id)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns the number of keys in the specified Redis Cluster hash slot.
+  ///
+  /// <https://redis.io/commands/cluster-countkeysinslot>
+  fn cluster_count_keys_in_slot<R>(&self, slot: u16) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move {
+      commands::cluster::cluster_count_keys_in_slot(self, slot)
+        .await?
+        .convert()
+    }
+  }
+
+  /// The CLUSTER DELSLOTS command asks a particular Redis Cluster node to forget which master is serving the hash
+  /// slots specified as arguments.
+  ///
+  /// <https://redis.io/commands/cluster-delslots>
+  fn cluster_del_slots<S>(&self, slots: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<MultipleHashSlots> + Send,
+  {
+    async move {
+      into!(slots);
+      commands::cluster::cluster_del_slots(self, slots).await
+    }
+  }
+
+  /// This command, that can only be sent to a Redis Cluster replica node, forces the replica to start a manual
+  /// failover of its master instance.
+  ///
+  /// <https://redis.io/commands/cluster-failover>
+  fn cluster_failover(&self, flag: Option<ClusterFailoverFlag>) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::cluster::cluster_failover(self, flag).await }
+  }
+
+  /// The command is used in order to remove a node, specified via its node ID, from the set of known nodes of the
+  /// Redis Cluster node receiving the command. In other words the specified node is removed from the nodes table of
+  /// the node receiving the command.
+  ///
+  /// <https://redis.io/commands/cluster-forget>
+  fn cluster_forget<S>(&self, node_id: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(node_id);
+      commands::cluster::cluster_forget(self, node_id).await
+    }
+  }
+
+  /// The command returns an array of keys names stored in the contacted node and hashing to the specified hash slot.
+  ///
+  /// <https://redis.io/commands/cluster-getkeysinslot>
+  fn cluster_get_keys_in_slot<R>(&self, slot: u16, count: u64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move {
+      commands::cluster::cluster_get_keys_in_slot(self, slot, count)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns an integer identifying the hash slot the specified key hashes to.
+  ///
+  /// <https://redis.io/commands/cluster-keyslot>
+  fn cluster_keyslot<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::cluster::cluster_keyslot(self, key).await?.convert()
+    }
+  }
+
+  /// CLUSTER MEET is used in order to connect different Redis nodes with cluster support enabled, into a working
+  /// cluster.
+  ///
+  /// <https://redis.io/commands/cluster-meet>
+  fn cluster_meet<S>(&self, ip: S, port: u16) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(ip);
+      commands::cluster::cluster_meet(self, ip, port).await
+    }
+  }
+
+  /// The command reconfigures a node as a replica of the specified master. If the node receiving the command is an
+  /// empty master, as a side effect of the command, the node role is changed from master to replica.
+  ///
+  /// <https://redis.io/commands/cluster-replicate>
+  fn cluster_replicate<S>(&self, node_id: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(node_id);
+      commands::cluster::cluster_replicate(self, node_id).await
+    }
+  }
+
+  /// The command provides a list of replica nodes replicating from the specified master node.
+  ///
+  /// <https://redis.io/commands/cluster-replicas>
+  fn cluster_replicas<R, S>(&self, node_id: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(node_id);
+      commands::cluster::cluster_replicas(self, node_id).await?.convert()
+    }
+  }
+
+  /// Reset a Redis Cluster node, in a more or less drastic way depending on the reset type, that can be hard or soft.
+  /// Note that this command does not work for masters if they hold one or more keys, in that case to completely
+  /// reset a master node keys must be removed first, e.g. by using FLUSHALL first, and then CLUSTER RESET.
+  ///
+  /// <https://redis.io/commands/cluster-reset>
+  fn cluster_reset(&self, mode: Option<ClusterResetFlag>) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::cluster::cluster_reset(self, mode).await }
+  }
+
+  /// This command sets a specific config epoch in a fresh node.
+  ///
+  /// <https://redis.io/commands/cluster-set-config-epoch>
+  fn cluster_set_config_epoch(&self, epoch: u64) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::cluster::cluster_set_config_epoch(self, epoch).await }
+  }
+
+  /// CLUSTER SETSLOT is responsible for changing the state of a hash slot in the receiving node in different ways.
+  ///
+  /// <https://redis.io/commands/cluster-setslot>
+  fn cluster_setslot(&self, slot: u16, state: ClusterSetSlotState) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::cluster::cluster_setslot(self, slot, state).await }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/config.rs.html b/doc/src/fred/commands/interfaces/config.rs.html new file mode 100644 index 00000000..38621d09 --- /dev/null +++ b/doc/src/fred/commands/interfaces/config.rs.html @@ -0,0 +1,121 @@ +config.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  types::{FromRedis, RedisValue},
+};
+use bytes_utils::Str;
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use std::convert::TryInto;
+
+/// Functions that implement the [config](https://redis.io/commands#server) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait ConfigInterface: ClientLike + Sized {
+  /// Resets the statistics reported by Redis using the INFO command.
+  ///
+  /// <https://redis.io/commands/config-resetstat>
+  fn config_resetstat(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::config::config_resetstat(self).await }
+  }
+
+  /// The CONFIG REWRITE command rewrites the redis.conf file the server was started with, applying the minimal
+  /// changes needed to make it reflect the configuration currently used by the server, which may be different
+  /// compared to the original one because of the use of the CONFIG SET command.
+  ///
+  /// <https://redis.io/commands/config-rewrite>
+  fn config_rewrite(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::config::config_rewrite(self).await }
+  }
+
+  /// The CONFIG GET command is used to read the configuration parameters of a running Redis server.
+  ///
+  /// <https://redis.io/commands/config-get>
+  fn config_get<R, S>(&self, parameter: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(parameter);
+      commands::config::config_get(self, parameter).await?.convert()
+    }
+  }
+
+  /// The CONFIG SET command is used in order to reconfigure the server at run time without the need to restart Redis.
+  ///
+  /// <https://redis.io/commands/config-set>
+  fn config_set<P, V>(&self, parameter: P, value: V) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    P: Into<Str> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(parameter);
+      try_into!(value);
+      commands::config::config_set(self, parameter, value).await
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/geo.rs.html b/doc/src/fred/commands/interfaces/geo.rs.html new file mode 100644 index 00000000..0d930800 --- /dev/null +++ b/doc/src/fred/commands/interfaces/geo.rs.html @@ -0,0 +1,543 @@ +geo.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  types::{
+    Any,
+    FromRedis,
+    GeoPosition,
+    GeoUnit,
+    MultipleGeoValues,
+    MultipleValues,
+    RedisKey,
+    RedisValue,
+    SetOptions,
+    SortOrder,
+  },
+};
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use std::convert::TryInto;
+
+/// Functions that implement the [geo](https://redis.io/commands#geo) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait GeoInterface: ClientLike + Sized {
+  /// Adds the specified geospatial items (longitude, latitude, name) to the specified key.
+  ///
+  /// <https://redis.io/commands/geoadd>
+  fn geoadd<R, K, V>(
+    &self,
+    key: K,
+    options: Option<SetOptions>,
+    changed: bool,
+    values: V,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: Into<MultipleGeoValues> + Send,
+  {
+    async move {
+      into!(key, values);
+      commands::geo::geoadd(self, key, options, changed, values)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Return valid Geohash strings representing the position of one or more elements in a sorted set value
+  /// representing a geospatial index (where elements were added using GEOADD).
+  ///
+  /// <https://redis.io/commands/geohash>
+  fn geohash<R, K, V>(&self, key: K, members: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(members);
+      commands::geo::geohash(self, key, members).await?.convert()
+    }
+  }
+
+  /// Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by
+  /// the sorted set at key.
+  ///
+  /// Callers can use [as_geo_position](crate::types::RedisValue::as_geo_position) to lazily parse results as needed.
+  ///
+  /// <https://redis.io/commands/geopos>
+  fn geopos<R, K, V>(&self, key: K, members: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(members);
+      commands::geo::geopos(self, key, members).await?.convert()
+    }
+  }
+
+  /// Return the distance between two members in the geospatial index represented by the sorted set.
+  ///
+  /// <https://redis.io/commands/geodist>
+  fn geodist<R, K, S, D>(
+    &self,
+    key: K,
+    src: S,
+    dest: D,
+    unit: Option<GeoUnit>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: TryInto<RedisValue> + Send,
+    S::Error: Into<RedisError> + Send,
+    D: TryInto<RedisValue> + Send,
+    D::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(src, dest);
+      commands::geo::geodist(self, key, src, dest, unit).await?.convert()
+    }
+  }
+
+  /// Return the members of a sorted set populated with geospatial information using GEOADD, which are within the
+  /// borders of the area specified with the center location and the maximum distance from the center (the radius).
+  ///
+  /// <https://redis.io/commands/georadius>
+  fn georadius<R, K, P>(
+    &self,
+    key: K,
+    position: P,
+    radius: f64,
+    unit: GeoUnit,
+    withcoord: bool,
+    withdist: bool,
+    withhash: bool,
+    count: Option<(u64, Any)>,
+    ord: Option<SortOrder>,
+    store: Option<RedisKey>,
+    storedist: Option<RedisKey>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<GeoPosition> + Send,
+  {
+    async move {
+      into!(key, position);
+      commands::geo::georadius(
+        self, key, position, radius, unit, withcoord, withdist, withhash, count, ord, store, storedist,
+      )
+      .await?
+      .convert()
+    }
+  }
+
+  /// This command is exactly like GEORADIUS with the sole difference that instead of taking, as the center of the
+  /// area to query, a longitude and latitude value, it takes the name of a member already existing inside the
+  /// geospatial index represented by the sorted set.
+  ///
+  /// <https://redis.io/commands/georadiusbymember>
+  fn georadiusbymember<R, K, V>(
+    &self,
+    key: K,
+    member: V,
+    radius: f64,
+    unit: GeoUnit,
+    withcoord: bool,
+    withdist: bool,
+    withhash: bool,
+    count: Option<(u64, Any)>,
+    ord: Option<SortOrder>,
+    store: Option<RedisKey>,
+    storedist: Option<RedisKey>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(member);
+      commands::geo::georadiusbymember(
+        self,
+        key,
+        to!(member)?,
+        radius,
+        unit,
+        withcoord,
+        withdist,
+        withhash,
+        count,
+        ord,
+        store,
+        storedist,
+      )
+      .await?
+      .convert()
+    }
+  }
+
+  /// Return the members of a sorted set populated with geospatial information using GEOADD, which are within the
+  /// borders of the area specified by a given shape.
+  ///
+  /// <https://redis.io/commands/geosearch>
+  fn geosearch<R, K>(
+    &self,
+    key: K,
+    from_member: Option<RedisValue>,
+    from_lonlat: Option<GeoPosition>,
+    by_radius: Option<(f64, GeoUnit)>,
+    by_box: Option<(f64, f64, GeoUnit)>,
+    ord: Option<SortOrder>,
+    count: Option<(u64, Any)>,
+    withcoord: bool,
+    withdist: bool,
+    withhash: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::geo::geosearch(
+        self,
+        key,
+        from_member,
+        from_lonlat,
+        by_radius,
+        by_box,
+        ord,
+        count,
+        withcoord,
+        withdist,
+        withhash,
+      )
+      .await?
+      .convert()
+    }
+  }
+
+  /// This command is like GEOSEARCH, but stores the result in destination key. Returns the number of members added to
+  /// the destination key.
+  ///
+  /// <https://redis.io/commands/geosearchstore>
+  fn geosearchstore<R, D, S>(
+    &self,
+    dest: D,
+    source: S,
+    from_member: Option<RedisValue>,
+    from_lonlat: Option<GeoPosition>,
+    by_radius: Option<(f64, GeoUnit)>,
+    by_box: Option<(f64, f64, GeoUnit)>,
+    ord: Option<SortOrder>,
+    count: Option<(u64, Any)>,
+    storedist: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    D: Into<RedisKey> + Send,
+    S: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(dest, source);
+      commands::geo::geosearchstore(
+        self,
+        dest,
+        source,
+        from_member,
+        from_lonlat,
+        by_radius,
+        by_box,
+        ord,
+        count,
+        storedist,
+      )
+      .await?
+      .convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/hashes.rs.html b/doc/src/fred/commands/interfaces/hashes.rs.html new file mode 100644 index 00000000..03277193 --- /dev/null +++ b/doc/src/fred/commands/interfaces/hashes.rs.html @@ -0,0 +1,491 @@ +hashes.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  types::{FromRedis, MultipleKeys, RedisKey, RedisMap, RedisValue},
+};
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use std::convert::TryInto;
+
+/// Functions that implement the [hashes](https://redis.io/commands#hashes) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait HashesInterface: ClientLike + Sized {
+  /// Returns all fields and values of the hash stored at `key`.
+  ///
+  /// <https://redis.io/commands/hgetall>
+  fn hgetall<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::hashes::hgetall(self, key).await?.convert()
+    }
+  }
+
+  /// Removes the specified fields from the hash stored at `key`.
+  ///
+  /// <https://redis.io/commands/hdel>
+  fn hdel<R, K, F>(&self, key: K, fields: F) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    F: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(key, fields);
+      commands::hashes::hdel(self, key, fields).await?.convert()
+    }
+  }
+
+  /// Returns if `field` is an existing field in the hash stored at `key`.
+  ///
+  /// <https://redis.io/commands/hexists>
+  fn hexists<R, K, F>(&self, key: K, field: F) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    F: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key, field);
+      commands::hashes::hexists(self, key, field).await?.convert()
+    }
+  }
+
+  /// Returns the value associated with `field` in the hash stored at `key`.
+  ///
+  /// <https://redis.io/commands/hget>
+  fn hget<R, K, F>(&self, key: K, field: F) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    F: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key, field);
+      commands::hashes::hget(self, key, field).await?.convert()
+    }
+  }
+
+  /// Increments the number stored at `field` in the hash stored at `key` by `increment`.
+  ///
+  /// <https://redis.io/commands/hincrby>
+  fn hincrby<R, K, F>(&self, key: K, field: F, increment: i64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    F: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key, field);
+      commands::hashes::hincrby(self, key, field, increment).await?.convert()
+    }
+  }
+
+  /// Increment the specified `field` of a hash stored at `key`, and representing a floating point number, by the
+  /// specified `increment`.
+  ///
+  /// <https://redis.io/commands/hincrbyfloat>
+  fn hincrbyfloat<R, K, F>(&self, key: K, field: F, increment: f64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    F: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key, field);
+      commands::hashes::hincrbyfloat(self, key, field, increment)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns all field names in the hash stored at `key`.
+  ///
+  /// <https://redis.io/commands/hkeys>
+  fn hkeys<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::hashes::hkeys(self, key).await?.convert()
+    }
+  }
+
+  /// Returns the number of fields contained in the hash stored at `key`.
+  ///
+  /// <https://redis.io/commands/hlen>
+  fn hlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::hashes::hlen(self, key).await?.convert()
+    }
+  }
+
+  /// Returns the values associated with the specified `fields` in the hash stored at `key`.
+  ///
+  /// <https://redis.io/commands/hmget>
+  fn hmget<R, K, F>(&self, key: K, fields: F) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    F: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(key, fields);
+      commands::hashes::hmget(self, key, fields).await?.convert()
+    }
+  }
+
+  /// Sets the specified fields to their respective values in the hash stored at `key`.
+  ///
+  /// <https://redis.io/commands/hmset>
+  fn hmset<R, K, V>(&self, key: K, values: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisMap> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(values);
+      commands::hashes::hmset(self, key, values).await?.convert()
+    }
+  }
+
+  /// Sets fields in the hash stored at `key` to their provided values.
+  ///
+  /// <https://redis.io/commands/hset>
+  fn hset<R, K, V>(&self, key: K, values: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisMap> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(values);
+      commands::hashes::hset(self, key, values).await?.convert()
+    }
+  }
+
+  /// Sets `field` in the hash stored at `key` to `value`, only if `field` does not yet exist.
+  ///
+  /// <https://redis.io/commands/hsetnx>
+  fn hsetnx<R, K, F, V>(&self, key: K, field: F, value: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    F: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key, field);
+      try_into!(value);
+      commands::hashes::hsetnx(self, key, field, value).await?.convert()
+    }
+  }
+
+  /// When called with just the `key` argument, return a random field from the hash value stored at `key`.
+  ///
+  /// If the provided `count` argument is positive, return an array of distinct fields.
+  ///
+  /// <https://redis.io/commands/hrandfield>
+  fn hrandfield<R, K>(&self, key: K, count: Option<(i64, bool)>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::hashes::hrandfield(self, key, count).await?.convert()
+    }
+  }
+
+  /// Returns the string length of the value associated with `field` in the hash stored at `key`.
+  ///
+  /// <https://redis.io/commands/hstrlen>
+  fn hstrlen<R, K, F>(&self, key: K, field: F) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    F: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key, field);
+      commands::hashes::hstrlen(self, key, field).await?.convert()
+    }
+  }
+
+  /// Returns all values in the hash stored at `key`.
+  ///
+  /// <https://redis.io/commands/hvals>
+  fn hvals<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::hashes::hvals(self, key).await?.convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/hyperloglog.rs.html b/doc/src/fred/commands/interfaces/hyperloglog.rs.html new file mode 100644 index 00000000..063d6dd5 --- /dev/null +++ b/doc/src/fred/commands/interfaces/hyperloglog.rs.html @@ -0,0 +1,131 @@ +hyperloglog.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  types::{FromRedis, MultipleKeys, MultipleValues, RedisKey},
+};
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use std::convert::TryInto;
+
+/// Functions that implement the [HyperLogLog](https://redis.io/commands#hyperloglog) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait HyperloglogInterface: ClientLike + Sized {
+  /// Adds all the element arguments to the HyperLogLog data structure stored at the variable name specified as first
+  /// argument.
+  ///
+  /// <https://redis.io/commands/pfadd>
+  fn pfadd<R, K, V>(&self, key: K, elements: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(elements);
+      commands::hyperloglog::pfadd(self, key, elements).await?.convert()
+    }
+  }
+
+  /// When called with a single key, returns the approximated cardinality computed by the HyperLogLog data structure
+  /// stored at the specified variable, which is 0 if the variable does not exist.
+  ///
+  /// When called with multiple keys, returns the approximated cardinality of the union of the HyperLogLogs passed, by
+  /// internally merging the HyperLogLogs stored at the provided keys into a temporary HyperLogLog.
+  ///
+  /// <https://redis.io/commands/pfcount>
+  fn pfcount<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::hyperloglog::pfcount(self, keys).await?.convert()
+    }
+  }
+
+  /// Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of the
+  /// observed sets of the source HyperLogLog structures.
+  ///
+  /// <https://redis.io/commands/pfmerge>
+  fn pfmerge<R, D, S>(&self, dest: D, sources: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    D: Into<RedisKey> + Send,
+    S: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(dest, sources);
+      commands::hyperloglog::pfmerge(self, dest, sources).await?.convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/keys.rs.html b/doc/src/fred/commands/interfaces/keys.rs.html new file mode 100644 index 00000000..058285de --- /dev/null +++ b/doc/src/fred/commands/interfaces/keys.rs.html @@ -0,0 +1,1203 @@ +keys.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  types::{Expiration, ExpireOptions, FromRedis, MultipleKeys, RedisKey, RedisMap, RedisValue, SetOptions},
+};
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use std::convert::TryInto;
+
+/// Functions that implement the generic [keys](https://redis.io/commands#generic) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait KeysInterface: ClientLike + Sized {
+  /// Marks the given keys to be watched for conditional execution of a transaction.
+  ///
+  /// <https://redis.io/commands/watch>
+  fn watch<K>(&self, keys: K) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::keys::watch(self, keys).await
+    }
+  }
+
+  /// Flushes all the previously watched keys for a transaction.
+  ///
+  /// <https://redis.io/commands/unwatch>
+  fn unwatch(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::keys::unwatch(self).await }
+  }
+
+  /// Return a random key from the currently selected database.
+  ///
+  /// <https://redis.io/commands/randomkey>
+  fn randomkey<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::keys::randomkey(self).await?.convert() }
+  }
+
+  /// This command copies the value stored at the source key to the destination key.
+  ///
+  /// <https://redis.io/commands/copy>
+  fn copy<R, S, D>(
+    &self,
+    source: S,
+    destination: D,
+    db: Option<u8>,
+    replace: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<RedisKey> + Send,
+    D: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(source, destination);
+      commands::keys::copy(self, source, destination, db, replace)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Serialize the value stored at `key` in a Redis-specific format and return it as bulk string.
+  ///
+  /// <https://redis.io/commands/dump>
+  fn dump<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::dump(self, key).await?.convert()
+    }
+  }
+
+  /// Create a key associated with a value that is obtained by deserializing the provided serialized value
+  ///
+  /// <https://redis.io/commands/restore>
+  fn restore<R, K>(
+    &self,
+    key: K,
+    ttl: i64,
+    serialized: RedisValue,
+    replace: bool,
+    absttl: bool,
+    idletime: Option<i64>,
+    frequency: Option<i64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::restore(self, key, ttl, serialized, replace, absttl, idletime, frequency)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Set a value with optional NX|XX, EX|PX|EXAT|PXAT|KEEPTTL, and GET arguments.
+  ///
+  /// Note: the `get` flag was added in 6.2.0. Setting it as `false` works with Redis versions <=6.2.0.
+  ///
+  /// <https://redis.io/commands/set>
+  fn set<R, K, V>(
+    &self,
+    key: K,
+    value: V,
+    expire: Option<Expiration>,
+    options: Option<SetOptions>,
+    get: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(value);
+      commands::keys::set(self, key, value, expire, options, get)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Read a value from the server.
+  ///
+  /// <https://redis.io/commands/get>
+  fn get<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::get(self, key).await?.convert()
+    }
+  }
+
+  /// Returns the substring of the string value stored at `key` with offsets `start` and `end` (both inclusive).
+  ///
+  /// Note: Command formerly called SUBSTR in Redis verison <=2.0.
+  ///
+  /// <https://redis.io/commands/getrange>
+  fn getrange<R, K>(&self, key: K, start: usize, end: usize) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::getrange(self, key, start, end).await?.convert()
+    }
+  }
+
+  /// Overwrites part of the string stored at `key`, starting at the specified `offset`, for the entire length of
+  /// `value`.
+  ///
+  /// <https://redis.io/commands/setrange>
+  fn setrange<R, K, V>(&self, key: K, offset: u32, value: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(value);
+      commands::keys::setrange(self, key, offset, value).await?.convert()
+    }
+  }
+
+  /// Atomically sets `key` to `value` and returns the old value stored at `key`.
+  ///
+  /// Returns an error if `key` does not hold string value. Returns nil if `key` does not exist.
+  ///
+  /// <https://redis.io/commands/getset>
+  fn getset<R, K, V>(&self, key: K, value: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(value);
+      commands::keys::getset(self, key, value).await?.convert()
+    }
+  }
+
+  /// Get the value of key and delete the key. This command is similar to GET, except for the fact that it also
+  /// deletes the key on success (if and only if the key's value type is a string).
+  ///
+  /// <https://redis.io/commands/getdel>
+  fn getdel<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::getdel(self, key).await?.convert()
+    }
+  }
+
+  /// Returns the length of the string value stored at key. An error is returned when key holds a non-string value.
+  ///
+  /// <https://redis.io/commands/strlen>
+  fn strlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::strlen(self, key).await?.convert()
+    }
+  }
+
+  /// Removes the specified keys. A key is ignored if it does not exist.
+  ///
+  /// Returns the number of keys removed.
+  ///
+  /// <https://redis.io/commands/del>
+  fn del<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::keys::del(self, keys).await?.convert()
+    }
+  }
+
+  /// Unlinks the specified keys. A key is ignored if it does not exist
+  ///
+  /// Returns the number of keys removed.
+  ///
+  /// <https://redis.io/commands/del>
+  fn unlink<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::keys::unlink(self, keys).await?.convert()
+    }
+  }
+
+  /// Renames `source` key to `destination`.
+  ///
+  /// Returns an error when `source` does not exist. If `destination` exists, it gets overwritten.
+  ///
+  /// <https://redis.io/commands/rename>
+  fn rename<R, S, D>(&self, source: S, destination: D) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<RedisKey> + Send,
+    D: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(source);
+      into!(destination);
+      commands::keys::rename(self, source, destination).await?.convert()
+    }
+  }
+
+  /// Renames `source` key to `destination` if `destination` does not yet exist.
+  ///
+  /// Returns an error when `source` does not exist.
+  ///
+  /// <https://redis.io/commands/renamenx>
+  fn renamenx<R, S, D>(&self, source: S, destination: D) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<RedisKey> + Send,
+    D: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(source);
+      into!(destination);
+      commands::keys::renamenx(self, source, destination).await?.convert()
+    }
+  }
+
+  /// Append `value` to `key` if it's a string.
+  ///
+  /// <https://redis.io/commands/append/>
+  fn append<R, K, V>(&self, key: K, value: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(value);
+      commands::keys::append(self, key, value).await?.convert()
+    }
+  }
+
+  /// Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the
+  /// special value nil is returned.
+  ///
+  /// <https://redis.io/commands/mget>
+  fn mget<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::keys::mget(self, keys).await?.convert()
+    }
+  }
+
+  /// Sets the given keys to their respective values.
+  ///
+  /// <https://redis.io/commands/mset>
+  fn mset<V>(&self, values: V) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    V: TryInto<RedisMap> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      try_into!(values);
+      commands::keys::mset(self, values).await?.convert()
+    }
+  }
+
+  /// Sets the given keys to their respective values. MSETNX will not perform any operation at all even if just a
+  /// single key already exists.
+  ///
+  /// <https://redis.io/commands/msetnx>
+  fn msetnx<R, V>(&self, values: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    V: TryInto<RedisMap> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      try_into!(values);
+      commands::keys::msetnx(self, values).await?.convert()
+    }
+  }
+
+  /// Increments the number stored at `key` by one. If the key does not exist, it is set to 0 before performing the
+  /// operation.
+  ///
+  /// Returns an error if the value at key is of the wrong type.
+  ///
+  /// <https://redis.io/commands/incr>
+  fn incr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::incr(self, key).await?.convert()
+    }
+  }
+
+  /// Increments the number stored at `key` by `val`. If the key does not exist, it is set to 0 before performing the
+  /// operation.
+  ///
+  /// Returns an error if the value at key is of the wrong type.
+  ///
+  /// <https://redis.io/commands/incrby>
+  fn incr_by<R, K>(&self, key: K, val: i64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::incr_by(self, key, val).await?.convert()
+    }
+  }
+
+  /// Increment the string representing a floating point number stored at key by `val`. If the key does not exist, it
+  /// is set to 0 before performing the operation.
+  ///
+  /// Returns an error if key value is the wrong type or if the current value cannot be parsed as a floating point
+  /// value.
+  ///
+  /// <https://redis.io/commands/incrbyfloat>
+  fn incr_by_float<R, K>(&self, key: K, val: f64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::incr_by_float(self, key, val).await?.convert()
+    }
+  }
+
+  /// Decrements the number stored at `key` by one. If the key does not exist, it is set to 0 before performing the
+  /// operation.
+  ///
+  /// Returns an error if the key contains a value of the wrong type.
+  ///
+  /// <https://redis.io/commands/decr>
+  fn decr<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::decr(self, key).await?.convert()
+    }
+  }
+
+  /// Decrements the number stored at `key` by `val`. If the key does not exist, it is set to 0 before performing the
+  /// operation.
+  ///
+  /// Returns an error if the key contains a value of the wrong type.
+  ///
+  /// <https://redis.io/commands/decrby>
+  fn decr_by<R, K>(&self, key: K, val: i64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::decr_by(self, key, val).await?.convert()
+    }
+  }
+
+  /// Returns the remaining time to live of a key that has a timeout, in seconds.
+  ///
+  /// <https://redis.io/commands/ttl>
+  fn ttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::ttl(self, key).await?.convert()
+    }
+  }
+
+  /// Returns the remaining time to live of a key that has a timeout, in milliseconds.
+  ///
+  /// <https://redis.io/commands/pttl>
+  fn pttl<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::pttl(self, key).await?.convert()
+    }
+  }
+
+  /// Remove the existing timeout on a key, turning the key from volatile (a key with an expiration)
+  /// to persistent (a key that will never expire as no timeout is associated).
+  ///
+  /// Returns a boolean value describing whether the timeout was removed.
+  ///
+  /// <https://redis.io/commands/persist>
+  fn persist<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::persist(self, key).await?.convert()
+    }
+  }
+
+  /// Set a timeout on key. After the timeout has expired, the key will be automatically deleted.
+  ///
+  /// <https://redis.io/commands/expire>
+  fn expire<R, K>(&self, key: K, seconds: i64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::expire(self, key, seconds).await?.convert()
+    }
+  }
+
+  /// Set a timeout on a key based on a UNIX timestamp.
+  ///
+  /// <https://redis.io/commands/expireat>
+  fn expire_at<R, K>(&self, key: K, timestamp: i64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::expire_at(self, key, timestamp).await?.convert()
+    }
+  }
+
+  /// This command works exactly like EXPIRE but the time to live of the key is specified in milliseconds instead of
+  /// seconds.
+  ///
+  /// <https://redis.io/docs/latest/commands/pexpire/>
+  fn pexpire<R, K>(
+    &self,
+    key: K,
+    milliseconds: i64,
+    options: Option<ExpireOptions>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::pexpire(self, key, milliseconds, options)
+        .await?
+        .convert()
+    }
+  }
+
+  /// PEXPIREAT has the same effect and semantic as EXPIREAT, but the Unix time at which the key will expire is
+  /// specified in milliseconds instead of seconds.
+  ///
+  /// <https://redis.io/docs/latest/commands/pexpireat/>
+  fn pexpire_at<R, K>(
+    &self,
+    key: K,
+    timestamp: i64,
+    options: Option<ExpireOptions>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::keys::pexpire_at(self, key, timestamp, options)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns number of keys that exist from the `keys` arguments.
+  ///
+  /// <https://redis.io/commands/exists>
+  fn exists<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::keys::exists(self, keys).await?.convert()
+    }
+  }
+
+  /// Runs the longest common subsequence algorithm on two keys.
+  ///
+  /// <https://redis.io/commands/lcs/>
+  fn lcs<R, K1, K2>(
+    &self,
+    key1: K1,
+    key2: K2,
+    len: bool,
+    idx: bool,
+    minmatchlen: Option<i64>,
+    withmatchlen: bool,
+  ) -> impl Future<Output = Result<R, RedisError>> + Send
+  where
+    R: FromRedis,
+    K1: Into<RedisKey> + Send,
+    K2: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key1, key2);
+      commands::keys::lcs(self, key1, key2, len, idx, minmatchlen, withmatchlen)
+        .await?
+        .convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/lists.rs.html b/doc/src/fred/commands/interfaces/lists.rs.html new file mode 100644 index 00000000..c9c0c225 --- /dev/null +++ b/doc/src/fred/commands/interfaces/lists.rs.html @@ -0,0 +1,967 @@ +lists.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  types::{
+    FromRedis,
+    LMoveDirection,
+    Limit,
+    ListLocation,
+    MultipleKeys,
+    MultipleStrings,
+    MultipleValues,
+    RedisKey,
+    RedisValue,
+    SortOrder,
+  },
+};
+use bytes_utils::Str;
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use std::convert::TryInto;
+
+/// Functions that implement the [lists](https://redis.io/commands#lists) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait ListInterface: ClientLike + Sized {
+  /// The blocking variant of [Self::lmpop].
+  ///
+  /// <https://redis.io/commands/blmpop/>
+  fn blmpop<R, K>(
+    &self,
+    timeout: f64,
+    keys: K,
+    direction: LMoveDirection,
+    count: Option<i64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::lists::blmpop(self, timeout, keys, direction, count)
+        .await?
+        .convert()
+    }
+  }
+
+  /// BLPOP is a blocking list pop primitive. It is the blocking version of LPOP because it blocks the connection when
+  /// there are no elements to pop from any of the given lists. An element is popped from the head of the first list
+  /// that is non-empty, with the given keys being checked in the order that they are given.
+  ///
+  /// <https://redis.io/commands/blpop>
+  fn blpop<R, K>(&self, keys: K, timeout: f64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::lists::blpop(self, keys, timeout).await?.convert()
+    }
+  }
+
+  /// BRPOP is a blocking list pop primitive. It is the blocking version of RPOP because it blocks the connection when
+  /// there are no elements to pop from any of the given lists. An element is popped from the tail of the first list
+  /// that is non-empty, with the given keys being checked in the order that they are given.
+  ///
+  /// <https://redis.io/commands/brpop>
+  fn brpop<R, K>(&self, keys: K, timeout: f64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::lists::brpop(self, keys, timeout).await?.convert()
+    }
+  }
+
+  /// The blocking equivalent of [Self::rpoplpush].
+  ///
+  /// <https://redis.io/commands/brpoplpush>
+  fn brpoplpush<R, S, D>(
+    &self,
+    source: S,
+    destination: D,
+    timeout: f64,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<RedisKey> + Send,
+    D: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(source, destination);
+      commands::lists::brpoplpush(self, source, destination, timeout)
+        .await?
+        .convert()
+    }
+  }
+
+  /// The blocking equivalent of [Self::lmove].
+  ///
+  /// <https://redis.io/commands/blmove>
+  fn blmove<R, S, D>(
+    &self,
+    source: S,
+    destination: D,
+    source_direction: LMoveDirection,
+    destination_direction: LMoveDirection,
+    timeout: f64,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<RedisKey> + Send,
+    D: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(source, destination);
+      commands::lists::blmove(
+        self,
+        source,
+        destination,
+        source_direction,
+        destination_direction,
+        timeout,
+      )
+      .await?
+      .convert()
+    }
+  }
+
+  /// Pops one or more elements from the first non-empty list key from the list of provided key names.
+  ///
+  /// <https://redis.io/commands/lmpop/>
+  fn lmpop<R, K>(
+    &self,
+    keys: K,
+    direction: LMoveDirection,
+    count: Option<i64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::lists::lmpop(self, keys, direction, count).await?.convert()
+    }
+  }
+
+  /// Returns the element at index in the list stored at key.
+  ///
+  /// <https://redis.io/commands/lindex>
+  fn lindex<R, K>(&self, key: K, index: i64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::lists::lindex(self, key, index).await?.convert()
+    }
+  }
+
+  /// Inserts element in the list stored at key either before or after the reference value `pivot`.
+  ///
+  /// <https://redis.io/commands/linsert>
+  fn linsert<R, K, P, V>(
+    &self,
+    key: K,
+    location: ListLocation,
+    pivot: P,
+    element: V,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: TryInto<RedisValue> + Send,
+    P::Error: Into<RedisError> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(pivot, element);
+      commands::lists::linsert(self, key, location, pivot, element)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns the length of the list stored at key.
+  ///
+  /// <https://redis.io/commands/llen>
+  fn llen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::lists::llen(self, key).await?.convert()
+    }
+  }
+
+  /// Removes and returns the first elements of the list stored at key.
+  ///
+  /// <https://redis.io/commands/lpop>
+  fn lpop<R, K>(&self, key: K, count: Option<usize>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::lists::lpop(self, key, count).await?.convert()
+    }
+  }
+
+  /// The command returns the index of matching elements inside a Redis list.
+  ///
+  /// <https://redis.io/commands/lpos>
+  fn lpos<R, K, V>(
+    &self,
+    key: K,
+    element: V,
+    rank: Option<i64>,
+    count: Option<i64>,
+    maxlen: Option<i64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(element);
+      commands::lists::lpos(self, key, element, rank, count, maxlen)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Insert all the specified values at the head of the list stored at `key`.
+  ///
+  /// <https://redis.io/commands/lpush>
+  fn lpush<R, K, V>(&self, key: K, elements: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(elements);
+      commands::lists::lpush(self, key, elements).await?.convert()
+    }
+  }
+
+  /// Inserts specified values at the head of the list stored at `key`, only if `key` already exists and holds a list.
+  ///
+  /// <https://redis.io/commands/lpushx>
+  fn lpushx<R, K, V>(&self, key: K, elements: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(elements);
+      commands::lists::lpushx(self, key, elements).await?.convert()
+    }
+  }
+
+  /// Returns the specified elements of the list stored at `key`.
+  ///
+  /// <https://redis.io/commands/lrange>
+  fn lrange<R, K>(&self, key: K, start: i64, stop: i64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::lists::lrange(self, key, start, stop).await?.convert()
+    }
+  }
+
+  /// Removes the first `count` occurrences of elements equal to `element` from the list stored at `key`.
+  ///
+  /// <https://redis.io/commands/lrem>
+  fn lrem<R, K, V>(&self, key: K, count: i64, element: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(element);
+      commands::lists::lrem(self, key, count, element).await?.convert()
+    }
+  }
+
+  /// Sets the list element at `index` to `element`.
+  ///
+  /// <https://redis.io/commands/lset>
+  fn lset<R, K, V>(&self, key: K, index: i64, element: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(element);
+      commands::lists::lset(self, key, index, element).await?.convert()
+    }
+  }
+
+  /// Trim an existing list so that it will contain only the specified range of elements specified.
+  ///
+  /// <https://redis.io/commands/ltrim>
+  fn ltrim<R, K>(&self, key: K, start: i64, stop: i64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::lists::ltrim(self, key, start, stop).await?.convert()
+    }
+  }
+
+  /// Removes and returns the last elements of the list stored at `key`.
+  ///
+  /// <https://redis.io/commands/rpop>
+  fn rpop<R, K>(&self, key: K, count: Option<usize>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::lists::rpop(self, key, count).await?.convert()
+    }
+  }
+
+  /// Atomically returns and removes the last element (tail) of the list stored at `source`, and pushes the element at
+  /// the first element (head) of the list stored at `destination`.
+  ///
+  /// <https://redis.io/commands/rpoplpush>
+  fn rpoplpush<R, S, D>(&self, source: S, dest: D) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<RedisKey> + Send,
+    D: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(source, dest);
+      commands::lists::rpoplpush(self, source, dest).await?.convert()
+    }
+  }
+
+  /// Atomically returns and removes the first/last element (head/tail depending on the source direction argument) of
+  /// the list stored at `source`, and pushes the element at the first/last element (head/tail depending on the
+  /// destination direction argument) of the list stored at `destination`.
+  ///
+  /// <https://redis.io/commands/lmove>
+  fn lmove<R, S, D>(
+    &self,
+    source: S,
+    dest: D,
+    source_direction: LMoveDirection,
+    dest_direction: LMoveDirection,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<RedisKey> + Send,
+    D: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(source, dest);
+      commands::lists::lmove(self, source, dest, source_direction, dest_direction)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Insert all the specified values at the tail of the list stored at `key`.
+  ///
+  /// <https://redis.io/commands/rpush>
+  fn rpush<R, K, V>(&self, key: K, elements: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(elements);
+      commands::lists::rpush(self, key, elements).await?.convert()
+    }
+  }
+
+  /// Inserts specified values at the tail of the list stored at `key`, only if key already exists and holds a list.
+  ///
+  /// <https://redis.io/commands/rpushx>
+  fn rpushx<R, K, V>(&self, key: K, elements: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(elements);
+      commands::lists::rpushx(self, key, elements).await?.convert()
+    }
+  }
+
+  /// Returns or stores the elements contained in the list, set or sorted set at `key`.
+  ///
+  /// <https://redis.io/commands/sort/>
+  fn sort<R, K, S>(
+    &self,
+    key: K,
+    by: Option<Str>,
+    limit: Option<Limit>,
+    get: S,
+    order: Option<SortOrder>,
+    alpha: bool,
+    store: Option<RedisKey>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(key, get);
+      commands::lists::sort(self, key, by, limit, get, order, alpha, store)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Read-only variant of the SORT command. It is exactly like the original SORT but refuses the STORE option and can
+  /// safely be used in read-only replicas.
+  ///
+  /// <https://redis.io/commands/sort_ro/>
+  fn sort_ro<R, K, S>(
+    &self,
+    key: K,
+    by: Option<Str>,
+    limit: Option<Limit>,
+    get: S,
+    order: Option<SortOrder>,
+    alpha: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(key, get);
+      commands::lists::sort_ro(self, key, by, limit, get, order, alpha)
+        .await?
+        .convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/lua.rs.html b/doc/src/fred/commands/interfaces/lua.rs.html new file mode 100644 index 00000000..17bd2e9d --- /dev/null +++ b/doc/src/fred/commands/interfaces/lua.rs.html @@ -0,0 +1,689 @@ +lua.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  types::{FnPolicy, FromRedis, MultipleKeys, MultipleStrings, MultipleValues, ScriptDebugFlag},
+};
+use bytes::Bytes;
+use bytes_utils::Str;
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use std::convert::TryInto;
+
+/// Functions that implement the [lua](https://redis.io/commands#lua) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait LuaInterface: ClientLike + Sized {
+  /// Load a script into the scripts cache, without executing it. After the specified command is loaded into the
+  /// script cache it will be callable using EVALSHA with the correct SHA1 digest of the script.
+  ///
+  /// Returns the SHA-1 hash of the script.
+  ///
+  /// <https://redis.io/commands/script-load>
+  fn script_load<R, S>(&self, script: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(script);
+      commands::lua::script_load(self, script).await?.convert()
+    }
+  }
+
+  /// A clustered variant of [script_load](Self::script_load) that loads the script on all primary nodes in a cluster.
+  ///
+  /// Returns the SHA-1 hash of the script.
+  #[cfg(feature = "sha-1")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "sha-1")))]
+  fn script_load_cluster<R, S>(&self, script: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(script);
+      commands::lua::script_load_cluster(self, script).await?.convert()
+    }
+  }
+
+  /// Kills the currently executing Lua script, assuming no write operation was yet performed by the script.
+  ///
+  /// <https://redis.io/commands/script-kill>
+  fn script_kill(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::lua::script_kill(self).await }
+  }
+
+  /// A clustered variant of the [script_kill](Self::script_kill) command that issues the command to all primary nodes
+  /// in the cluster.
+  fn script_kill_cluster(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::lua::script_kill_cluster(self).await }
+  }
+
+  /// Flush the Lua scripts cache.
+  ///
+  /// <https://redis.io/commands/script-flush>
+  fn script_flush(&self, r#async: bool) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::lua::script_flush(self, r#async).await }
+  }
+
+  /// A clustered variant of [script_flush](Self::script_flush) that flushes the script cache on all primary nodes in
+  /// the cluster.
+  fn script_flush_cluster(&self, r#async: bool) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::lua::script_flush_cluster(self, r#async).await }
+  }
+
+  /// Returns information about the existence of the scripts in the script cache.
+  ///
+  /// <https://redis.io/commands/script-exists>
+  fn script_exists<R, H>(&self, hashes: H) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    H: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(hashes);
+      commands::lua::script_exists(self, hashes).await?.convert()
+    }
+  }
+
+  /// Set the debug mode for subsequent scripts executed with EVAL.
+  ///
+  /// <https://redis.io/commands/script-debug>
+  fn script_debug(&self, flag: ScriptDebugFlag) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::lua::script_debug(self, flag).await }
+  }
+
+  /// Evaluates a script cached on the server side by its SHA1 digest.
+  ///
+  /// <https://redis.io/commands/evalsha>
+  ///
+  /// **Note: Use `None` to represent an empty set of keys or args.**
+  fn evalsha<R, S, K, V>(&self, hash: S, keys: K, args: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+    K: Into<MultipleKeys> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(hash, keys);
+      try_into!(args);
+      commands::lua::evalsha(self, hash, keys, args).await?.convert()
+    }
+  }
+
+  /// Evaluate a Lua script on the server.
+  ///
+  /// <https://redis.io/commands/eval>
+  ///
+  /// **Note: Use `None` to represent an empty set of keys or args.**
+  fn eval<R, S, K, V>(&self, script: S, keys: K, args: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+    K: Into<MultipleKeys> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(script, keys);
+      try_into!(args);
+      commands::lua::eval(self, script, keys, args).await?.convert()
+    }
+  }
+}
+
+/// Functions that implement the [function](https://redis.io/docs/manual/programmability/functions-intro/) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait FunctionInterface: ClientLike + Sized {
+  /// Invoke a function.
+  ///
+  /// <https://redis.io/commands/fcall/>
+  fn fcall<R, F, K, V>(&self, func: F, keys: K, args: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    F: Into<Str> + Send,
+    K: Into<MultipleKeys> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(func);
+      try_into!(keys, args);
+      commands::lua::fcall(self, func, keys, args).await?.convert()
+    }
+  }
+
+  /// This is a read-only variant of the FCALL command that cannot execute commands that modify data.
+  ///
+  /// <https://redis.io/commands/fcall_ro/>
+  fn fcall_ro<R, F, K, V>(&self, func: F, keys: K, args: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    F: Into<Str> + Send,
+    K: Into<MultipleKeys> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(func);
+      try_into!(keys, args);
+      commands::lua::fcall_ro(self, func, keys, args).await?.convert()
+    }
+  }
+
+  /// Delete a library and all its functions.
+  ///
+  /// <https://redis.io/commands/function-delete/>
+  fn function_delete<R, S>(&self, library_name: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(library_name);
+      commands::lua::function_delete(self, library_name).await?.convert()
+    }
+  }
+
+  /// Delete a library and all its functions from each cluster node concurrently.
+  ///
+  /// <https://redis.io/commands/function-delete/>
+  fn function_delete_cluster<S>(&self, library_name: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(library_name);
+      commands::lua::function_delete_cluster(self, library_name).await
+    }
+  }
+
+  /// Return the serialized payload of loaded libraries.
+  ///
+  /// <https://redis.io/commands/function-dump/>
+  fn function_dump<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::lua::function_dump(self).await?.convert() }
+  }
+
+  /// Deletes all the libraries.
+  ///
+  /// <https://redis.io/commands/function-flush/>
+  fn function_flush<R>(&self, r#async: bool) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::lua::function_flush(self, r#async).await?.convert() }
+  }
+
+  /// Deletes all the libraries on all cluster nodes concurrently.
+  ///
+  /// <https://redis.io/commands/function-flush/>
+  fn function_flush_cluster(&self, r#async: bool) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::lua::function_flush_cluster(self, r#async).await }
+  }
+
+  /// Kill a function that is currently executing.
+  ///
+  /// Note: This command runs on a backchannel connection to the server in order to take effect as quickly as
+  /// possible.
+  ///
+  /// <https://redis.io/commands/function-kill/>
+  fn function_kill<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::lua::function_kill(self).await?.convert() }
+  }
+
+  /// Return information about the functions and libraries.
+  ///
+  /// <https://redis.io/commands/function-list/>
+  fn function_list<R, S>(
+    &self,
+    library_name: Option<S>,
+    withcode: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+  {
+    async move {
+      let library_name = library_name.map(|l| l.into());
+      commands::lua::function_list(self, library_name, withcode)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Load a library to Redis.
+  ///
+  /// <https://redis.io/commands/function-load/>
+  fn function_load<R, S>(&self, replace: bool, code: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(code);
+      commands::lua::function_load(self, replace, code).await?.convert()
+    }
+  }
+
+  /// Load a library to Redis on all cluster nodes concurrently.
+  ///
+  /// <https://redis.io/commands/function-load/>
+  fn function_load_cluster<R, S>(&self, replace: bool, code: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(code);
+      commands::lua::function_load_cluster(self, replace, code)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Restore libraries from the serialized payload.
+  ///
+  /// <https://redis.io/commands/function-restore/>
+  ///
+  /// Note: Use `FnPolicy::default()` to use the default function restore policy (`"APPEND"`).
+  fn function_restore<R, B, P>(&self, serialized: B, policy: P) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    B: Into<Bytes> + Send,
+    P: TryInto<FnPolicy> + Send,
+    P::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(serialized);
+      try_into!(policy);
+      commands::lua::function_restore(self, serialized, policy)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Restore libraries from the serialized payload on all cluster nodes concurrently.
+  ///
+  /// <https://redis.io/commands/function-restore/>
+  ///
+  /// Note: Use `FnPolicy::default()` to use the default function restore policy (`"APPEND"`).
+  fn function_restore_cluster<B, P>(&self, serialized: B, policy: P) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    B: Into<Bytes> + Send,
+    P: TryInto<FnPolicy> + Send,
+    P::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(serialized);
+      try_into!(policy);
+      commands::lua::function_restore_cluster(self, serialized, policy).await
+    }
+  }
+
+  /// Return information about the function that's currently running and information about the available execution
+  /// engines.
+  ///
+  /// Note: This command runs on a backchannel connection to the server.
+  ///
+  /// <https://redis.io/commands/function-stats/>
+  fn function_stats<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::lua::function_stats(self).await?.convert() }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/memory.rs.html b/doc/src/fred/commands/interfaces/memory.rs.html new file mode 100644 index 00000000..d933836f --- /dev/null +++ b/doc/src/fred/commands/interfaces/memory.rs.html @@ -0,0 +1,129 @@ +memory.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+
use crate::{
+  commands,
+  interfaces::{ClientLike, RedisResult},
+  prelude::FromRedis,
+  types::RedisKey,
+};
+use futures::Future;
+use rm_send_macros::rm_send_if;
+
+/// Functions that implement the [memory](https://redis.io/commands#server) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait MemoryInterface: ClientLike + Sized {
+  /// The MEMORY DOCTOR command reports about different memory-related issues that the Redis server experiences, and
+  /// advises about possible remedies.
+  ///
+  /// <https://redis.io/commands/memory-doctor>
+  fn memory_doctor<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::memory::memory_doctor(self).await?.convert() }
+  }
+
+  /// The MEMORY MALLOC-STATS command provides an internal statistics report from the memory allocator.
+  ///
+  /// <https://redis.io/commands/memory-malloc-stats>
+  fn memory_malloc_stats<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::memory::memory_malloc_stats(self).await?.convert() }
+  }
+
+  /// The MEMORY PURGE command attempts to purge dirty pages so these can be reclaimed by the allocator.
+  ///
+  /// <https://redis.io/commands/memory-purge>
+  fn memory_purge(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::memory::memory_purge(self).await }
+  }
+
+  /// The MEMORY STATS command returns an Array reply about the memory usage of the server.
+  ///
+  /// <https://redis.io/commands/memory-stats>
+  fn memory_stats<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::memory::memory_stats(self).await?.convert() }
+  }
+
+  /// The MEMORY USAGE command reports the number of bytes that a key and its value require to be stored in RAM.
+  ///
+  /// <https://redis.io/commands/memory-usage>
+  fn memory_usage<R, K>(&self, key: K, samples: Option<u32>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::memory::memory_usage(self, key, samples).await?.convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/metrics.rs.html b/doc/src/fred/commands/interfaces/metrics.rs.html new file mode 100644 index 00000000..b8658270 --- /dev/null +++ b/doc/src/fred/commands/interfaces/metrics.rs.html @@ -0,0 +1,179 @@ +metrics.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+
use crate::interfaces::ClientLike;
+
+#[cfg(feature = "metrics")]
+use crate::modules::metrics::Stats;
+
+/// Functions that implement the internal metrics interface.
+pub trait MetricsInterface: ClientLike + Sized {
+  /// Read the number of request redeliveries.
+  ///
+  /// This is the number of times a request had to be sent again due to a connection closing while waiting on a
+  /// response.
+  fn read_redelivery_count(&self) -> usize {
+    self.inner().counters.read_redelivery_count()
+  }
+
+  /// Read and reset the number of request redeliveries.
+  fn take_redelivery_count(&self) -> usize {
+    self.inner().counters.take_redelivery_count()
+  }
+
+  /// Read the number of buffered commands that have not yet been sent to the server.
+  fn command_queue_len(&self) -> usize {
+    self.inner().counters.read_cmd_buffer_len()
+  }
+
+  /// Read latency metrics across all commands.
+  ///
+  /// This metric reflects the total latency experienced by callers, including time spent waiting in memory to be
+  /// written and network latency. Features such as automatic reconnect and frame serialization time can all affect
+  /// these values.
+  #[cfg(feature = "metrics")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
+  fn read_latency_metrics(&self) -> Stats {
+    self.inner().latency_stats.read().read_metrics()
+  }
+
+  /// Read and consume latency metrics, resetting their values afterwards.
+  #[cfg(feature = "metrics")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
+  fn take_latency_metrics(&self) -> Stats {
+    self.inner().latency_stats.write().take_metrics()
+  }
+
+  /// Read network latency metrics across all commands.
+  ///
+  /// This metric only reflects time spent waiting on a response. It will factor in reconnect time if a response
+  /// doesn't arrive due to a connection closing, but it does not include the time a command spends waiting to be
+  /// written, serialization time, backpressure, etc.
+  #[cfg(feature = "metrics")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
+  fn read_network_latency_metrics(&self) -> Stats {
+    self.inner().network_latency_stats.read().read_metrics()
+  }
+
+  /// Read and consume network latency metrics, resetting their values afterwards.
+  #[cfg(feature = "metrics")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
+  fn take_network_latency_metrics(&self) -> Stats {
+    self.inner().network_latency_stats.write().take_metrics()
+  }
+
+  /// Read request payload size metrics across all commands.
+  #[cfg(feature = "metrics")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
+  fn read_req_size_metrics(&self) -> Stats {
+    self.inner().req_size_stats.read().read_metrics()
+  }
+
+  /// Read and consume request payload size metrics, resetting their values afterwards.
+  #[cfg(feature = "metrics")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
+  fn take_req_size_metrics(&self) -> Stats {
+    self.inner().req_size_stats.write().take_metrics()
+  }
+
+  /// Read response payload size metrics across all commands.
+  #[cfg(feature = "metrics")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
+  fn read_res_size_metrics(&self) -> Stats {
+    self.inner().res_size_stats.read().read_metrics()
+  }
+
+  /// Read and consume response payload size metrics, resetting their values afterwards.
+  #[cfg(feature = "metrics")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
+  fn take_res_size_metrics(&self) -> Stats {
+    self.inner().res_size_stats.write().take_metrics()
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/mod.rs.html b/doc/src/fred/commands/interfaces/mod.rs.html new file mode 100644 index 00000000..fb638913 --- /dev/null +++ b/doc/src/fred/commands/interfaces/mod.rs.html @@ -0,0 +1,99 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+
#[cfg(feature = "i-acl")]
+pub mod acl;
+#[cfg(feature = "i-client")]
+pub mod client;
+#[cfg(feature = "i-cluster")]
+pub mod cluster;
+#[cfg(feature = "i-config")]
+pub mod config;
+#[cfg(feature = "i-geo")]
+pub mod geo;
+#[cfg(feature = "i-hashes")]
+pub mod hashes;
+#[cfg(feature = "i-hyperloglog")]
+pub mod hyperloglog;
+#[cfg(feature = "i-keys")]
+pub mod keys;
+#[cfg(feature = "i-lists")]
+pub mod lists;
+#[cfg(feature = "i-scripts")]
+pub mod lua;
+#[cfg(feature = "i-memory")]
+pub mod memory;
+pub mod metrics;
+#[cfg(feature = "i-pubsub")]
+pub mod pubsub;
+#[cfg(feature = "i-redis-json")]
+pub mod redis_json;
+#[cfg(feature = "i-redisearch")]
+pub mod redisearch;
+pub mod scan;
+#[cfg(feature = "sentinel-client")]
+pub mod sentinel;
+#[cfg(feature = "i-server")]
+pub mod server;
+#[cfg(feature = "i-sets")]
+pub mod sets;
+#[cfg(feature = "i-slowlog")]
+pub mod slowlog;
+#[cfg(feature = "i-sorted-sets")]
+pub mod sorted_sets;
+#[cfg(feature = "i-streams")]
+pub mod streams;
+pub mod strings;
+#[cfg(feature = "i-time-series")]
+pub mod timeseries;
+#[cfg(feature = "i-tracking")]
+pub mod tracking;
+#[cfg(feature = "transactions")]
+pub mod transactions;
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/pubsub.rs.html b/doc/src/fred/commands/interfaces/pubsub.rs.html new file mode 100644 index 00000000..c5dc0d0b --- /dev/null +++ b/doc/src/fred/commands/interfaces/pubsub.rs.html @@ -0,0 +1,393 @@ +pubsub.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  types::{FromRedis, MultipleStrings, RedisValue},
+};
+use bytes_utils::Str;
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use std::convert::TryInto;
+
+/// Functions that implement the [pubsub](https://redis.io/commands#pubsub) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait PubsubInterface: ClientLike + Sized + Send {
+  /// Subscribe to a channel on the publish-subscribe interface.
+  ///
+  /// <https://redis.io/commands/subscribe>
+  fn subscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(channels);
+      commands::pubsub::subscribe(self, channels).await
+    }
+  }
+
+  /// Unsubscribe from a channel on the PubSub interface.
+  ///
+  /// <https://redis.io/commands/unsubscribe>
+  fn unsubscribe<S>(&self, channels: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(channels);
+      commands::pubsub::unsubscribe(self, channels).await
+    }
+  }
+
+  /// Subscribes the client to the given patterns.
+  ///
+  /// <https://redis.io/commands/psubscribe>
+  fn psubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(patterns);
+      commands::pubsub::psubscribe(self, patterns).await
+    }
+  }
+
+  /// Unsubscribes the client from the given patterns, or from all of them if none is given.
+  ///
+  /// If no channels are provided this command returns an empty array.
+  ///
+  /// <https://redis.io/commands/punsubscribe>
+  fn punsubscribe<S>(&self, patterns: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(patterns);
+      commands::pubsub::punsubscribe(self, patterns).await
+    }
+  }
+
+  /// Publish a message on the PubSub interface, returning the number of clients that received the message.
+  ///
+  /// <https://redis.io/commands/publish>
+  fn publish<R, S, V>(&self, channel: S, message: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(channel);
+      try_into!(message);
+      commands::pubsub::publish(self, channel, message).await?.convert()
+    }
+  }
+
+  /// Subscribes the client to the specified shard channels.
+  ///
+  /// <https://redis.io/commands/ssubscribe/>
+  fn ssubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    C: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(channels);
+      commands::pubsub::ssubscribe(self, channels).await
+    }
+  }
+
+  /// Unsubscribes the client from the given shard channels, or from all of them if none is given.
+  ///
+  /// If no channels are provided this command returns an empty array.
+  ///
+  /// <https://redis.io/commands/sunsubscribe/>
+  fn sunsubscribe<C>(&self, channels: C) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    C: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(channels);
+      commands::pubsub::sunsubscribe(self, channels).await
+    }
+  }
+
+  /// Posts a message to the given shard channel.
+  ///
+  /// <https://redis.io/commands/spublish/>
+  fn spublish<R, S, V>(&self, channel: S, message: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(channel);
+      try_into!(message);
+      commands::pubsub::spublish(self, channel, message).await?.convert()
+    }
+  }
+
+  /// Lists the currently active channels.
+  ///
+  /// <https://redis.io/commands/pubsub-channels/>
+  fn pubsub_channels<R, S>(&self, pattern: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(pattern);
+      commands::pubsub::pubsub_channels(self, pattern).await?.convert()
+    }
+  }
+
+  /// Returns the number of unique patterns that are subscribed to by clients.
+  ///
+  /// <https://redis.io/commands/pubsub-numpat/>
+  fn pubsub_numpat<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::pubsub::pubsub_numpat(self).await?.convert() }
+  }
+
+  /// Returns the number of subscribers (exclusive of clients subscribed to patterns) for the specified channels.
+  ///
+  /// <https://redis.io/commands/pubsub-numsub/>
+  fn pubsub_numsub<R, S>(&self, channels: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(channels);
+      commands::pubsub::pubsub_numsub(self, channels).await?.convert()
+    }
+  }
+
+  /// Lists the currently active shard channels.
+  ///
+  /// <https://redis.io/commands/pubsub-shardchannels/>
+  fn pubsub_shardchannels<R, S>(&self, pattern: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(pattern);
+      commands::pubsub::pubsub_shardchannels(self, pattern).await?.convert()
+    }
+  }
+
+  /// Returns the number of subscribers for the specified shard channels.
+  ///
+  /// <https://redis.io/commands/pubsub-shardnumsub/>
+  fn pubsub_shardnumsub<R, S>(&self, channels: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(channels);
+      commands::pubsub::pubsub_shardnumsub(self, channels).await?.convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/redis_json.rs.html b/doc/src/fred/commands/interfaces/redis_json.rs.html new file mode 100644 index 00000000..8be14897 --- /dev/null +++ b/doc/src/fred/commands/interfaces/redis_json.rs.html @@ -0,0 +1,947 @@ +redis_json.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+
use crate::{
+  commands,
+  interfaces::{ClientLike, RedisResult},
+  types::{FromRedis, MultipleKeys, MultipleStrings, RedisKey, SetOptions},
+};
+use bytes_utils::Str;
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use serde_json::Value;
+
+/// The client commands in the [RedisJSON](https://redis.io/docs/data-types/json/) interface.
+///
+/// ## String Values
+///
+/// This interface uses [serde_json::Value](serde_json::Value) as the baseline type and will convert non-string values
+/// to RESP bulk strings via [to_string](serde_json::to_string).
+///
+/// Some of the RedisJSON commands include the following notice in the documentation:
+///
+/// > To specify a string as an array value to append, wrap the quoted string with an additional set of single quotes.
+/// > Example: '"silver"'.
+///
+/// The [serde_json::to_string](serde_json::to_string) functions are often the easiest way to do
+/// this. The [json_quote](crate::json_quote) macro can also help.
+///
+/// For example:
+///
+/// ```rust
+/// use fred::{json_quote, prelude::*};
+/// use serde_json::json;
+/// async fn example(client: &RedisClient) -> Result<(), RedisError> {
+///   let _: () = client.json_set("foo", "$", json!(["a", "b"]), None).await?;
+///
+///   // need to double quote string values in this context
+///   let size: i64 = client
+///     .json_arrappend("foo", Some("$"), vec![
+///       json!("c").to_string(),
+///       // or
+///       serde_json::to_string(&json!("d"))?,
+///       // or
+///       json_quote!("e"),
+///     ])
+///     .await?;
+///   assert_eq!(size, 5);
+///   Ok(())
+/// }
+/// ```
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))]
+#[rm_send_if(feature = "glommio")]
+pub trait RedisJsonInterface: ClientLike + Sized {
+  /// Append the json values into the array at path after the last element in it.
+  ///
+  /// <https://redis.io/commands/json.arrappend>
+  fn json_arrappend<R, K, P, V>(&self, key: K, path: P, values: Vec<V>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+    V: Into<Value> + Send,
+  {
+    async move {
+      into!(key, path);
+      let values = values.into_iter().map(|v| v.into()).collect();
+      commands::redis_json::json_arrappend(self, key, path, values)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Search for the first occurrence of a JSON value in an array.
+  ///
+  /// <https://redis.io/commands/json.arrindex/>
+  fn json_arrindex<R, K, P, V>(
+    &self,
+    key: K,
+    path: P,
+    value: V,
+    start: Option<i64>,
+    stop: Option<i64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+    V: Into<Value> + Send,
+  {
+    async move {
+      into!(key, path, value);
+      commands::redis_json::json_arrindex(self, key, path, value, start, stop)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Insert the json values into the array at path before the index (shifts to the right).
+  ///
+  /// <https://redis.io/commands/json.arrinsert/>
+  fn json_arrinsert<R, K, P, V>(
+    &self,
+    key: K,
+    path: P,
+    index: i64,
+    values: Vec<V>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+    V: Into<Value> + Send,
+  {
+    async move {
+      into!(key, path);
+      let values = values.into_iter().map(|v| v.into()).collect();
+      commands::redis_json::json_arrinsert(self, key, path, index, values)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Report the length of the JSON array at path in key.
+  ///
+  /// <https://redis.io/commands/json.arrlen/>
+  fn json_arrlen<R, K, P>(&self, key: K, path: Option<P>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(key);
+      let path = path.map(|p| p.into());
+      commands::redis_json::json_arrlen(self, key, path).await?.convert()
+    }
+  }
+
+  /// Remove and return an element from the index in the array
+  ///
+  /// <https://redis.io/commands/json.arrpop/>
+  fn json_arrpop<R, K, P>(
+    &self,
+    key: K,
+    path: Option<P>,
+    index: Option<i64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(key);
+      let path = path.map(|p| p.into());
+      commands::redis_json::json_arrpop(self, key, path, index)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Trim an array so that it contains only the specified inclusive range of elements
+  ///
+  /// <https://redis.io/commands/json.arrtrim/>
+  fn json_arrtrim<R, K, P>(
+    &self,
+    key: K,
+    path: P,
+    start: i64,
+    stop: i64,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(key, path);
+      commands::redis_json::json_arrtrim(self, key, path, start, stop)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Clear container values (arrays/objects) and set numeric values to 0
+  ///
+  /// <https://redis.io/commands/json.clear/>
+  fn json_clear<R, K, P>(&self, key: K, path: Option<P>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(key);
+      let path = path.map(|p| p.into());
+      commands::redis_json::json_clear(self, key, path).await?.convert()
+    }
+  }
+
+  /// Report a value's memory usage in bytes
+  ///
+  /// <https://redis.io/commands/json.debug-memory/>
+  fn json_debug_memory<R, K, P>(&self, key: K, path: Option<P>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(key);
+      let path = path.map(|p| p.into());
+      commands::redis_json::json_debug_memory(self, key, path)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Delete a value.
+  ///
+  /// <https://redis.io/commands/json.del/>
+  fn json_del<R, K, P>(&self, key: K, path: P) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(key, path);
+      commands::redis_json::json_del(self, key, path).await?.convert()
+    }
+  }
+
+  /// Return the value at path in JSON serialized form.
+  ///
+  /// <https://redis.io/commands/json.get/>
+  fn json_get<R, K, I, N, S, P>(
+    &self,
+    key: K,
+    indent: Option<I>,
+    newline: Option<N>,
+    space: Option<S>,
+    paths: P,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    I: Into<Str> + Send,
+    N: Into<Str> + Send,
+    S: Into<Str> + Send,
+    P: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(key, paths);
+      let indent = indent.map(|v| v.into());
+      let newline = newline.map(|v| v.into());
+      let space = space.map(|v| v.into());
+      commands::redis_json::json_get(self, key, indent, newline, space, paths)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Merge a given JSON value into matching paths.
+  ///
+  /// <https://redis.io/commands/json.merge/>
+  fn json_merge<R, K, P, V>(&self, key: K, path: P, value: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+    V: Into<Value> + Send,
+  {
+    async move {
+      into!(key, path, value);
+      commands::redis_json::json_merge(self, key, path, value)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Return the values at path from multiple key arguments.
+  ///
+  /// <https://redis.io/commands/json.mget/>
+  fn json_mget<R, K, P>(&self, keys: K, path: P) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(keys, path);
+      commands::redis_json::json_mget(self, keys, path).await?.convert()
+    }
+  }
+
+  /// Set or update one or more JSON values according to the specified key-path-value triplets.
+  ///
+  /// <https://redis.io/commands/json.mset/>
+  fn json_mset<R, K, P, V>(&self, values: Vec<(K, P, V)>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+    V: Into<Value> + Send,
+  {
+    async move {
+      let values = values
+        .into_iter()
+        .map(|(k, p, v)| (k.into(), p.into(), v.into()))
+        .collect();
+      commands::redis_json::json_mset(self, values).await?.convert()
+    }
+  }
+
+  /// Increment the number value stored at path by number
+  ///
+  /// <https://redis.io/commands/json.numincrby/>
+  fn json_numincrby<R, K, P, V>(&self, key: K, path: P, value: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+    V: Into<Value> + Send,
+  {
+    async move {
+      into!(key, path, value);
+      commands::redis_json::json_numincrby(self, key, path, value)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Return the keys in the object that's referenced by path.
+  ///
+  /// <https://redis.io/commands/json.objkeys/>
+  fn json_objkeys<R, K, P>(&self, key: K, path: Option<P>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(key);
+      let path = path.map(|p| p.into());
+      commands::redis_json::json_objkeys(self, key, path).await?.convert()
+    }
+  }
+
+  /// Report the number of keys in the JSON object at path in key.
+  ///
+  /// <https://redis.io/commands/json.objlen/>
+  fn json_objlen<R, K, P>(&self, key: K, path: Option<P>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(key);
+      let path = path.map(|p| p.into());
+      commands::redis_json::json_objlen(self, key, path).await?.convert()
+    }
+  }
+
+  /// Return the JSON in key in Redis serialization protocol specification form.
+  ///
+  /// <https://redis.io/commands/json.resp/>
+  fn json_resp<R, K, P>(&self, key: K, path: Option<P>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(key);
+      let path = path.map(|p| p.into());
+      commands::redis_json::json_resp(self, key, path).await?.convert()
+    }
+  }
+
+  /// Set the JSON value at path in key.
+  ///
+  /// <https://redis.io/commands/json.set/>
+  fn json_set<R, K, P, V>(
+    &self,
+    key: K,
+    path: P,
+    value: V,
+    options: Option<SetOptions>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+    V: Into<Value> + Send,
+  {
+    async move {
+      into!(key, path, value);
+      commands::redis_json::json_set(self, key, path, value, options)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Append the json-string values to the string at path.
+  ///
+  /// <https://redis.io/commands/json.strappend/>
+  fn json_strappend<R, K, P, V>(
+    &self,
+    key: K,
+    path: Option<P>,
+    value: V,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+    V: Into<Value> + Send,
+  {
+    async move {
+      into!(key, value);
+      let path = path.map(|p| p.into());
+      commands::redis_json::json_strappend(self, key, path, value)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Report the length of the JSON String at path in key.
+  ///
+  /// <https://redis.io/commands/json.strlen/>
+  fn json_strlen<R, K, P>(&self, key: K, path: Option<P>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(key);
+      let path = path.map(|p| p.into());
+      commands::redis_json::json_strlen(self, key, path).await?.convert()
+    }
+  }
+
+  /// Toggle a Boolean value stored at path.
+  ///
+  /// <https://redis.io/commands/json.toggle/>
+  fn json_toggle<R, K, P>(&self, key: K, path: P) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(key, path);
+      commands::redis_json::json_toggle(self, key, path).await?.convert()
+    }
+  }
+
+  /// Report the type of JSON value at path.
+  ///
+  /// <https://redis.io/commands/json.type/>
+  fn json_type<R, K, P>(&self, key: K, path: Option<P>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(key);
+      let path = path.map(|p| p.into());
+      commands::redis_json::json_type(self, key, path).await?.convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/redisearch.rs.html b/doc/src/fred/commands/interfaces/redisearch.rs.html new file mode 100644 index 00000000..9268cb62 --- /dev/null +++ b/doc/src/fred/commands/interfaces/redisearch.rs.html @@ -0,0 +1,983 @@ +redisearch.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+
use crate::{
+  commands,
+  interfaces::{ClientLike, RedisResult},
+  prelude::RedisError,
+  types::{
+    FromRedis,
+    FtAggregateOptions,
+    FtAlterOptions,
+    FtCreateOptions,
+    FtSearchOptions,
+    MultipleStrings,
+    RedisKey,
+    RedisValue,
+    SearchSchema,
+    SpellcheckTerms,
+  },
+};
+use bytes::Bytes;
+use bytes_utils::Str;
+use rm_send_macros::rm_send_if;
+use std::future::Future;
+
+/// A [RediSearch](https://github.com/RediSearch/RediSearch) interface.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))]
+#[rm_send_if(feature = "glommio")]
+pub trait RediSearchInterface: ClientLike + Sized {
+  /// Returns a list of all existing indexes.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft._list/>
+  fn ft_list<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::redisearch::ft_list(self).await?.convert() }
+  }
+
+  /// Run a search query on an index, and perform aggregate transformations on the results.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.aggregate/>
+  fn ft_aggregate<R, I, Q>(
+    &self,
+    index: I,
+    query: Q,
+    options: FtAggregateOptions,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    I: Into<Str> + Send,
+    Q: Into<Str> + Send,
+  {
+    async move {
+      into!(index, query);
+      commands::redisearch::ft_aggregate(self, index, query, options)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Search the index with a textual query, returning either documents or just ids.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.search/>
+  ///
+  /// Note: `FT.SEARCH` uses a different format in RESP3 mode.
+  fn ft_search<R, I, Q>(
+    &self,
+    index: I,
+    query: Q,
+    options: FtSearchOptions,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    I: Into<Str> + Send,
+    Q: Into<Str> + Send,
+  {
+    async move {
+      into!(index, query);
+      commands::redisearch::ft_search(self, index, query, options)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Create an index with the given specification.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.create/>
+  fn ft_create<R, I>(
+    &self,
+    index: I,
+    options: FtCreateOptions,
+    schema: Vec<SearchSchema>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    I: Into<Str> + Send,
+  {
+    async move {
+      into!(index);
+      commands::redisearch::ft_create(self, index, options, schema)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Add a new attribute to the index.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.alter/>
+  fn ft_alter<R, I>(&self, index: I, options: FtAlterOptions) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    I: Into<Str> + Send,
+  {
+    async move {
+      into!(index);
+      commands::redisearch::ft_alter(self, index, options).await?.convert()
+    }
+  }
+
+  /// Add an alias to an index.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.aliasadd/>
+  fn ft_aliasadd<R, A, I>(&self, alias: A, index: I) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    A: Into<Str> + Send,
+    I: Into<Str> + Send,
+  {
+    async move {
+      into!(alias, index);
+      commands::redisearch::ft_aliasadd(self, alias, index).await?.convert()
+    }
+  }
+
+  /// Remove an alias from an index.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.aliasdel/>
+  fn ft_aliasdel<R, A>(&self, alias: A) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    A: Into<Str> + Send,
+  {
+    async move {
+      into!(alias);
+      commands::redisearch::ft_aliasdel(self, alias).await?.convert()
+    }
+  }
+
+  /// Add an alias to an index. If the alias is already associated with another index, FT.ALIASUPDATE removes the
+  /// alias association with the previous index.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.aliasupdate/>
+  fn ft_aliasupdate<R, A, I>(&self, alias: A, index: I) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    A: Into<Str> + Send,
+    I: Into<Str> + Send,
+  {
+    async move {
+      into!(alias, index);
+      commands::redisearch::ft_aliasupdate(self, alias, index)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Retrieve configuration options.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.config-get/>
+  fn ft_config_get<R, S>(&self, option: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(option);
+      commands::redisearch::ft_config_get(self, option).await?.convert()
+    }
+  }
+
+  /// Set the value of a RediSearch configuration parameter.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.config-set/>
+  fn ft_config_set<R, S, V>(&self, option: S, value: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(option);
+      try_into!(value);
+      commands::redisearch::ft_config_set(self, option, value)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Delete a cursor.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.cursor-del/>
+  fn ft_cursor_del<R, I, C>(&self, index: I, cursor: C) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    I: Into<Str> + Send,
+    C: TryInto<RedisValue> + Send,
+    C::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(index);
+      try_into!(cursor);
+      commands::redisearch::ft_cursor_del(self, index, cursor)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Read next results from an existing cursor.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.cursor-read/>
+  fn ft_cursor_read<R, I, C>(
+    &self,
+    index: I,
+    cursor: C,
+    count: Option<u64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    I: Into<Str> + Send,
+    C: TryInto<RedisValue> + Send,
+    C::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(index);
+      try_into!(cursor);
+      commands::redisearch::ft_cursor_read(self, index, cursor, count)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Add terms to a dictionary.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.dictadd/>
+  fn ft_dictadd<R, D, S>(&self, dict: D, terms: S) -> impl Future<Output = RedisResult<R>>
+  where
+    R: FromRedis,
+    D: Into<Str> + Send,
+    S: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(dict, terms);
+      commands::redisearch::ft_dictadd(self, dict, terms).await?.convert()
+    }
+  }
+
+  /// Remove terms from a dictionary.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.dictdel/>
+  fn ft_dictdel<R, D, S>(&self, dict: D, terms: S) -> impl Future<Output = RedisResult<R>>
+  where
+    R: FromRedis,
+    D: Into<Str> + Send,
+    S: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(dict, terms);
+      commands::redisearch::ft_dictdel(self, dict, terms).await?.convert()
+    }
+  }
+
+  /// Dump all terms in the given dictionary.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.dictdump/>
+  fn ft_dictdump<R, D>(&self, dict: D) -> impl Future<Output = RedisResult<R>>
+  where
+    R: FromRedis,
+    D: Into<Str> + Send,
+  {
+    async move {
+      into!(dict);
+      commands::redisearch::ft_dictdump(self, dict).await?.convert()
+    }
+  }
+
+  /// Delete an index.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.dropindex/>
+  fn ft_dropindex<R, I>(&self, index: I, dd: bool) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    I: Into<Str> + Send,
+  {
+    async move {
+      into!(index);
+      commands::redisearch::ft_dropindex(self, index, dd).await?.convert()
+    }
+  }
+
+  /// Return the execution plan for a complex query.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.explain/>
+  fn ft_explain<R, I, Q>(
+    &self,
+    index: I,
+    query: Q,
+    dialect: Option<i64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    I: Into<Str> + Send,
+    Q: Into<Str> + Send,
+  {
+    async move {
+      into!(index, query);
+      commands::redisearch::ft_explain(self, index, query, dialect)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Return information and statistics on the index.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.info/>
+  fn ft_info<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    I: Into<Str> + Send,
+  {
+    async move {
+      into!(index);
+      commands::redisearch::ft_info(self, index).await?.convert()
+    }
+  }
+
+  /// Perform spelling correction on a query, returning suggestions for misspelled terms.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.spellcheck/>
+  fn ft_spellcheck<R, I, Q>(
+    &self,
+    index: I,
+    query: Q,
+    distance: Option<u8>,
+    terms: Option<SpellcheckTerms>,
+    dialect: Option<i64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    I: Into<Str> + Send,
+    Q: Into<Str> + Send,
+  {
+    async move {
+      into!(index, query);
+      commands::redisearch::ft_spellcheck(self, index, query, distance, terms, dialect)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Add a suggestion string to an auto-complete suggestion dictionary.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.sugadd/>
+  fn ft_sugadd<R, K, S>(
+    &self,
+    key: K,
+    string: S,
+    score: f64,
+    incr: bool,
+    payload: Option<Bytes>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(key, string);
+      commands::redisearch::ft_sugadd(self, key, string, score, incr, payload)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Delete a string from a suggestion index.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.sugdel/>
+  fn ft_sugdel<R, K, S>(&self, key: K, string: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(key, string);
+      commands::redisearch::ft_sugdel(self, key, string).await?.convert()
+    }
+  }
+
+  /// Get completion suggestions for a prefix.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.sugget/>
+  fn ft_sugget<R, K, P>(
+    &self,
+    key: K,
+    prefix: P,
+    fuzzy: bool,
+    withscores: bool,
+    withpayloads: bool,
+    max: Option<u64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(key, prefix);
+      commands::redisearch::ft_sugget(self, key, prefix, fuzzy, withscores, withpayloads, max)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Get the size of an auto-complete suggestion dictionary.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.suglen/>
+  fn ft_suglen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::redisearch::ft_suglen(self, key).await?.convert()
+    }
+  }
+
+  /// Dump the contents of a synonym group.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.syndump/>
+  fn ft_syndump<R, I>(&self, index: I) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    I: Into<Str> + Send,
+  {
+    async move {
+      into!(index);
+      commands::redisearch::ft_syndump(self, index).await?.convert()
+    }
+  }
+
+  /// Update a synonym group.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.synupdate/>
+  fn ft_synupdate<R, I, S, T>(
+    &self,
+    index: I,
+    synonym_group_id: S,
+    skipinitialscan: bool,
+    terms: T,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    I: Into<Str> + Send,
+    S: Into<Str> + Send,
+    T: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(index, synonym_group_id, terms);
+      commands::redisearch::ft_synupdate(self, index, synonym_group_id, skipinitialscan, terms)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Return a distinct set of values indexed in a Tag field.
+  ///
+  /// <https://redis.io/docs/latest/commands/ft.tagvals/>
+  fn ft_tagvals<R, I, F>(&self, index: I, field_name: F) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    I: Into<Str> + Send,
+    F: Into<Str> + Send,
+  {
+    async move {
+      into!(index, field_name);
+      commands::redisearch::ft_tagvals(self, index, field_name)
+        .await?
+        .convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/scan.rs.html b/doc/src/fred/commands/interfaces/scan.rs.html new file mode 100644 index 00000000..7fb3861b --- /dev/null +++ b/doc/src/fred/commands/interfaces/scan.rs.html @@ -0,0 +1,3 @@ +scan.rs - source
1
+
// `impl Trait` doesn't work inside traits, so we just put these functions directly on the RedisClient
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/sentinel.rs.html b/doc/src/fred/commands/interfaces/sentinel.rs.html new file mode 100644 index 00000000..d79c11b7 --- /dev/null +++ b/doc/src/fred/commands/interfaces/sentinel.rs.html @@ -0,0 +1,445 @@ +sentinel.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  types::{FromRedis, RedisMap, RedisValue, SentinelFailureKind},
+};
+use bytes_utils::Str;
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use std::{convert::TryInto, net::IpAddr};
+
+/// Functions that implement the [sentinel](https://redis.io/topics/sentinel#sentinel-commands) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait SentinelInterface: ClientLike + Sized {
+  /// Check if the current Sentinel configuration is able to reach the quorum needed to failover a master, and the
+  /// majority needed to authorize the failover.
+  fn ckquorum<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    N: Into<Str> + Send,
+  {
+    async move {
+      into!(name);
+      commands::sentinel::ckquorum(self, name).await?.convert()
+    }
+  }
+
+  /// Force Sentinel to rewrite its configuration on disk, including the current Sentinel state.
+  fn flushconfig<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::sentinel::flushconfig(self).await?.convert() }
+  }
+
+  /// Force a failover as if the master was not reachable, and without asking for agreement to other Sentinels.
+  fn failover<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    N: Into<Str> + Send,
+  {
+    async move {
+      into!(name);
+      commands::sentinel::failover(self, name).await?.convert()
+    }
+  }
+
+  /// Return the ip and port number of the master with that name.
+  fn get_master_addr_by_name<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    N: Into<Str> + Send,
+  {
+    async move {
+      into!(name);
+      commands::sentinel::get_master_addr_by_name(self, name).await?.convert()
+    }
+  }
+
+  /// Return cached INFO output from masters and replicas.
+  fn info_cache<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::sentinel::info_cache(self).await?.convert() }
+  }
+
+  /// Show the state and info of the specified master.
+  fn master<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    N: Into<Str> + Send,
+  {
+    async move {
+      into!(name);
+      commands::sentinel::master(self, name).await?.convert()
+    }
+  }
+
+  /// Show a list of monitored masters and their state.
+  fn masters<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::sentinel::masters(self).await?.convert() }
+  }
+
+  /// Start Sentinel's monitoring.
+  ///
+  /// <https://redis.io/topics/sentinel#reconfiguring-sentinel-at-runtime>
+  fn monitor<R, N>(&self, name: N, ip: IpAddr, port: u16, quorum: u32) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    N: Into<Str> + Send,
+  {
+    async move {
+      into!(name);
+      commands::sentinel::monitor(self, name, ip, port, quorum)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Return the ID of the Sentinel instance.
+  fn myid<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::sentinel::myid(self).await?.convert() }
+  }
+
+  /// This command returns information about pending scripts.
+  fn pending_scripts<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::sentinel::pending_scripts(self).await?.convert() }
+  }
+
+  /// Stop Sentinel's monitoring.
+  ///
+  /// <https://redis.io/topics/sentinel#reconfiguring-sentinel-at-runtime>
+  fn remove<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    N: Into<Str> + Send,
+  {
+    async move {
+      into!(name);
+      commands::sentinel::remove(self, name).await?.convert()
+    }
+  }
+
+  /// Show a list of replicas for this master, and their state.
+  fn replicas<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    N: Into<Str> + Send,
+  {
+    async move {
+      into!(name);
+      commands::sentinel::replicas(self, name).await?.convert()
+    }
+  }
+
+  /// Show a list of sentinel instances for this master, and their state.
+  fn sentinels<R, N>(&self, name: N) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    N: Into<Str> + Send,
+  {
+    async move {
+      into!(name);
+      commands::sentinel::sentinels(self, name).await?.convert()
+    }
+  }
+
+  /// Set Sentinel's monitoring configuration.
+  ///
+  /// <https://redis.io/topics/sentinel#reconfiguring-sentinel-at-runtime>
+  fn set<R, N, V>(&self, name: N, args: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    N: Into<Str> + Send,
+    V: TryInto<RedisMap> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(name);
+      try_into!(args);
+      commands::sentinel::set(self, name, args).await?.convert()
+    }
+  }
+
+  /// This command simulates different Sentinel crash scenarios.
+  fn simulate_failure<R>(&self, kind: SentinelFailureKind) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::sentinel::simulate_failure(self, kind).await?.convert() }
+  }
+
+  /// This command will reset all the masters with matching name.
+  fn reset<R, P>(&self, pattern: P) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    P: Into<Str> + Send,
+  {
+    async move {
+      into!(pattern);
+      commands::sentinel::reset(self, pattern).await?.convert()
+    }
+  }
+
+  /// Get the current value of a global Sentinel configuration parameter. The specified name may be a wildcard,
+  /// similar to the Redis CONFIG GET command.
+  fn config_get<R, K>(&self, name: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<Str> + Send,
+  {
+    async move {
+      into!(name);
+      commands::sentinel::config_get(self, name).await?.convert()
+    }
+  }
+
+  /// Set the value of a global Sentinel configuration parameter.
+  fn config_set<R, K, V>(&self, name: K, value: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<Str> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(name);
+      try_into!(value);
+      commands::sentinel::config_set(self, name, value).await?.convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/server.rs.html b/doc/src/fred/commands/interfaces/server.rs.html new file mode 100644 index 00000000..d0ffd79d --- /dev/null +++ b/doc/src/fred/commands/interfaces/server.rs.html @@ -0,0 +1,193 @@ +server.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  types::{FromRedis, Server},
+};
+use futures::Future;
+use rm_send_macros::rm_send_if;
+
+/// Functions that implement the [server](https://redis.io/commands#server) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait ServerInterface: ClientLike {
+  /// Instruct Redis to start an Append Only File rewrite process.
+  ///
+  /// <https://redis.io/commands/bgrewriteaof>
+  fn bgrewriteaof<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::server::bgrewriteaof(self).await?.convert() }
+  }
+
+  /// Save the DB in background.
+  ///
+  /// <https://redis.io/commands/bgsave>
+  fn bgsave<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::server::bgsave(self).await?.convert() }
+  }
+
+  /// Return the number of keys in the selected database.
+  ///
+  /// <https://redis.io/commands/dbsize>
+  fn dbsize<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::server::dbsize(self).await?.convert() }
+  }
+
+  /// Select the database this client should use.
+  ///
+  /// <https://redis.io/commands/select>
+  fn select(&self, db: u8) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::server::select(self, db).await?.convert() }
+  }
+
+  /// This command will start a coordinated failover between the currently-connected-to master and one of its
+  /// replicas.
+  ///
+  /// <https://redis.io/commands/failover>
+  fn failover(
+    &self,
+    to: Option<(String, u16)>,
+    force: bool,
+    abort: bool,
+    timeout: Option<u32>,
+  ) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::server::failover(self, to, force, abort, timeout).await }
+  }
+
+  /// Return the UNIX TIME of the last DB save executed with success.
+  ///
+  /// <https://redis.io/commands/lastsave>
+  fn lastsave<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::server::lastsave(self).await?.convert() }
+  }
+
+  /// This command blocks the current client until all the previous write commands are successfully transferred and
+  /// acknowledged by at least the specified number of replicas. If the timeout, specified in milliseconds, is
+  /// reached, the command returns even if the specified number of replicas were not yet reached.
+  ///
+  /// <https://redis.io/commands/wait/>
+  fn wait<R>(&self, numreplicas: i64, timeout: i64) -> impl Future<Output = Result<R, RedisError>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::server::wait(self, numreplicas, timeout).await?.convert() }
+  }
+
+  /// Read the primary Redis server identifier returned from the sentinel nodes.
+  fn sentinel_primary(&self) -> Option<Server> {
+    self.inner().server_state.read().kind.sentinel_primary()
+  }
+
+  /// Read the set of known sentinel nodes.
+  fn sentinel_nodes(&self) -> Option<Vec<Server>> {
+    let inner = self.inner();
+    inner.server_state.read().kind.read_sentinel_nodes(&inner.config.server)
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/sets.rs.html b/doc/src/fred/commands/interfaces/sets.rs.html new file mode 100644 index 00000000..6daa3ed4 --- /dev/null +++ b/doc/src/fred/commands/interfaces/sets.rs.html @@ -0,0 +1,491 @@ +sets.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  types::{FromRedis, MultipleKeys, MultipleValues, RedisKey, RedisValue},
+};
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use std::convert::TryInto;
+
+/// Functions that implement the [sets](https://redis.io/commands#set) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait SetsInterface: ClientLike + Sized {
+  /// Add the specified members to the set stored at `key`.
+  ///
+  /// <https://redis.io/commands/sadd>
+  fn sadd<R, K, V>(&self, key: K, members: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(members);
+      commands::sets::sadd(self, key, members).await?.convert()
+    }
+  }
+
+  /// Returns the set cardinality (number of elements) of the set stored at `key`.
+  ///
+  /// <https://redis.io/commands/scard>
+  fn scard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::sets::scard(self, key).await?.convert()
+    }
+  }
+
+  /// Returns the members of the set resulting from the difference between the first set and all the successive sets.
+  ///
+  /// <https://redis.io/commands/sdiff>
+  fn sdiff<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::sets::sdiff(self, keys).await?.convert()
+    }
+  }
+
+  /// This command is equal to SDIFF, but instead of returning the resulting set, it is stored in `destination`.
+  ///
+  /// <https://redis.io/commands/sdiffstore>
+  fn sdiffstore<R, D, K>(&self, dest: D, keys: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    D: Into<RedisKey> + Send,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(dest, keys);
+      commands::sets::sdiffstore(self, dest, keys).await?.convert()
+    }
+  }
+
+  /// Returns the members of the set resulting from the intersection of all the given sets.
+  ///
+  /// <https://redis.io/commands/sinter>
+  fn sinter<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::sets::sinter(self, keys).await?.convert()
+    }
+  }
+
+  /// This command is equal to SINTER, but instead of returning the resulting set, it is stored in `destination`.
+  ///
+  /// <https://redis.io/commands/sinterstore>
+  fn sinterstore<R, D, K>(&self, dest: D, keys: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    D: Into<RedisKey> + Send,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(dest, keys);
+      commands::sets::sinterstore(self, dest, keys).await?.convert()
+    }
+  }
+
+  /// Returns if `member` is a member of the set stored at `key`.
+  ///
+  /// <https://redis.io/commands/sismember>
+  fn sismember<R, K, V>(&self, key: K, member: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(member);
+      commands::sets::sismember(self, key, member).await?.convert()
+    }
+  }
+
+  /// Returns whether each member is a member of the set stored at `key`.
+  ///
+  /// <https://redis.io/commands/smismember>
+  fn smismember<R, K, V>(&self, key: K, members: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(members);
+      commands::sets::smismember(self, key, members).await?.convert()
+    }
+  }
+
+  /// Returns all the members of the set value stored at `key`.
+  ///
+  /// <https://redis.io/commands/smembers>
+  fn smembers<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::sets::smembers(self, key).await?.convert()
+    }
+  }
+
+  /// Move `member` from the set at `source` to the set at `destination`.
+  ///
+  /// <https://redis.io/commands/smove>
+  fn smove<R, S, D, V>(&self, source: S, dest: D, member: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<RedisKey> + Send,
+    D: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(source, dest);
+      try_into!(member);
+      commands::sets::smove(self, source, dest, member).await?.convert()
+    }
+  }
+
+  /// Removes and returns one or more random members from the set value store at `key`.
+  ///
+  /// <https://redis.io/commands/spop>
+  fn spop<R, K>(&self, key: K, count: Option<usize>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::sets::spop(self, key, count).await?.convert()
+    }
+  }
+
+  /// When called with just the key argument, return a random element from the set value stored at `key`.
+  ///
+  /// If the provided `count` argument is positive, return an array of distinct elements. The array's length is either
+  /// count or the set's cardinality (SCARD), whichever is lower.
+  ///
+  /// <https://redis.io/commands/srandmember>
+  fn srandmember<R, K>(&self, key: K, count: Option<usize>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::sets::srandmember(self, key, count).await?.convert()
+    }
+  }
+
+  /// Remove the specified members from the set stored at `key`.
+  ///
+  /// <https://redis.io/commands/srem>
+  fn srem<R, K, V>(&self, key: K, members: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(members);
+      commands::sets::srem(self, key, members).await?.convert()
+    }
+  }
+
+  /// Returns the members of the set resulting from the union of all the given sets.
+  ///
+  /// <https://redis.io/commands/sunion>
+  fn sunion<R, K>(&self, keys: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::sets::sunion(self, keys).await?.convert()
+    }
+  }
+
+  /// This command is equal to SUNION, but instead of returning the resulting set, it is stored in `destination`.
+  ///
+  /// <https://redis.io/commands/sunionstore>
+  fn sunionstore<R, D, K>(&self, dest: D, keys: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    D: Into<RedisKey> + Send,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(dest, keys);
+      commands::sets::sunionstore(self, dest, keys).await?.convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/slowlog.rs.html b/doc/src/fred/commands/interfaces/slowlog.rs.html new file mode 100644 index 00000000..484a44ea --- /dev/null +++ b/doc/src/fred/commands/interfaces/slowlog.rs.html @@ -0,0 +1,77 @@ +slowlog.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+
use crate::{
+  commands,
+  interfaces::{ClientLike, RedisResult},
+  types::FromRedis,
+};
+use futures::Future;
+use rm_send_macros::rm_send_if;
+
+/// Functions that implement the [slowlog](https://redis.io/commands#server) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait SlowlogInterface: ClientLike + Sized {
+  /// This command is used to read the slow queries log.
+  ///
+  /// <https://redis.io/commands/slowlog#reading-the-slow-log>
+  fn slowlog_get<R>(&self, count: Option<i64>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::slowlog::slowlog_get(self, count).await?.convert() }
+  }
+
+  /// This command is used to read length of the slow queries log.
+  ///
+  /// <https://redis.io/commands/slowlog#obtaining-the-current-length-of-the-slow-log>
+  fn slowlog_length<R>(&self) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+  {
+    async move { commands::slowlog::slowlog_length(self).await?.convert() }
+  }
+
+  /// This command is used to reset the slow queries log.
+  ///
+  /// <https://redis.io/commands/slowlog#resetting-the-slow-log>
+  fn slowlog_reset(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::slowlog::slowlog_reset(self).await }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/sorted_sets.rs.html b/doc/src/fred/commands/interfaces/sorted_sets.rs.html new file mode 100644 index 00000000..98ccec04 --- /dev/null +++ b/doc/src/fred/commands/interfaces/sorted_sets.rs.html @@ -0,0 +1,1401 @@ +sorted_sets.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+
use crate::{
+  commands,
+  error::RedisError,
+  interfaces::{ClientLike, RedisResult},
+  types::{
+    AggregateOptions,
+    FromRedis,
+    Limit,
+    MultipleKeys,
+    MultipleValues,
+    MultipleWeights,
+    MultipleZaddValues,
+    Ordering,
+    RedisKey,
+    RedisValue,
+    SetOptions,
+    ZCmp,
+    ZRange,
+    ZSort,
+  },
+};
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use std::convert::TryInto;
+
+/// Functions that implement the [sorted sets](https://redis.io/commands#sorted_set) interface.
+#[rm_send_if(feature = "glommio")]
+pub trait SortedSetsInterface: ClientLike + Sized {
+  /// The blocking variant of [Self::zmpop].
+  ///
+  /// <https://redis.io/commands/bzmpop/>
+  fn bzmpop<R, K>(
+    &self,
+    timeout: f64,
+    keys: K,
+    sort: ZCmp,
+    count: Option<i64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::sorted_sets::bzmpop(self, timeout, keys, sort, count)
+        .await?
+        .convert()
+    }
+  }
+
+  /// The blocking variant of [Self::zpopmin].
+  ///
+  /// <https://redis.io/commands/bzpopmin>
+  fn bzpopmin<R, K>(&self, keys: K, timeout: f64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::sorted_sets::bzpopmin(self, keys, timeout).await?.convert()
+    }
+  }
+
+  /// The blocking variant of [Self::zpopmax].
+  ///
+  /// <https://redis.io/commands/bzpopmax>
+  fn bzpopmax<R, K>(&self, keys: K, timeout: f64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::sorted_sets::bzpopmax(self, keys, timeout).await?.convert()
+    }
+  }
+
+  /// Adds all the specified members with the specified scores to the sorted set stored at `key`.
+  ///
+  /// <https://redis.io/commands/zadd>
+  fn zadd<R, K, V>(
+    &self,
+    key: K,
+    options: Option<SetOptions>,
+    ordering: Option<Ordering>,
+    changed: bool,
+    incr: bool,
+    values: V,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<MultipleZaddValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(values);
+      commands::sorted_sets::zadd(self, key, options, ordering, changed, incr, values)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns the sorted set cardinality (number of elements) of the sorted set stored at `key`.
+  ///
+  /// <https://redis.io/commands/zcard>
+  fn zcard<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::sorted_sets::zcard(self, key).await?.convert()
+    }
+  }
+
+  /// Returns the number of elements in the sorted set at `key` with a score between `min` and `max`.
+  ///
+  /// <https://redis.io/commands/zcount>
+  fn zcount<R, K>(&self, key: K, min: f64, max: f64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::sorted_sets::zcount(self, key, min, max).await?.convert()
+    }
+  }
+
+  /// This command is similar to ZDIFFSTORE, but instead of storing the resulting sorted set, it is returned to the
+  /// client.
+  ///
+  /// <https://redis.io/commands/zdiff>
+  fn zdiff<R, K>(&self, keys: K, withscores: bool) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::sorted_sets::zdiff(self, keys, withscores).await?.convert()
+    }
+  }
+
+  /// Computes the difference between the first and all successive input sorted sets and stores the result in
+  /// `destination`.
+  ///
+  /// <https://redis.io/commands/zdiffstore>
+  fn zdiffstore<R, D, K>(&self, dest: D, keys: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    D: Into<RedisKey> + Send,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(dest, keys);
+      commands::sorted_sets::zdiffstore(self, dest, keys).await?.convert()
+    }
+  }
+
+  /// Increments the score of `member` in the sorted set stored at `key` by `increment`.
+  ///
+  /// <https://redis.io/commands/zincrby>
+  fn zincrby<R, K, V>(&self, key: K, increment: f64, member: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(member);
+      commands::sorted_sets::zincrby(self, key, increment, member)
+        .await?
+        .convert()
+    }
+  }
+
+  /// This command is similar to ZINTERSTORE, but instead of storing the resulting sorted set, it is returned to the
+  /// client.
+  ///
+  /// <https://redis.io/commands/zinter>
+  fn zinter<R, K, W>(
+    &self,
+    keys: K,
+    weights: W,
+    aggregate: Option<AggregateOptions>,
+    withscores: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+    W: Into<MultipleWeights> + Send,
+  {
+    async move {
+      into!(keys, weights);
+      commands::sorted_sets::zinter(self, keys, weights, aggregate, withscores)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Computes the intersection of the sorted sets given by the specified keys, and stores the result in
+  /// `destination`.
+  ///
+  /// <https://redis.io/commands/zinterstore>
+  fn zinterstore<R, D, K, W>(
+    &self,
+    dest: D,
+    keys: K,
+    weights: W,
+    aggregate: Option<AggregateOptions>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    D: Into<RedisKey> + Send,
+    K: Into<MultipleKeys> + Send,
+    W: Into<MultipleWeights> + Send,
+  {
+    async move {
+      into!(dest, keys, weights);
+      commands::sorted_sets::zinterstore(self, dest, keys, weights, aggregate)
+        .await?
+        .convert()
+    }
+  }
+
+  /// When all the elements in a sorted set are inserted with the same score, in order to force lexicographical
+  /// ordering, this command returns the number of elements in the sorted set at key with a value between min and
+  /// max.
+  ///
+  /// <https://redis.io/commands/zlexcount>
+  fn zlexcount<R, K, M, N>(&self, key: K, min: M, max: N) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    M: TryInto<ZRange> + Send,
+    M::Error: Into<RedisError> + Send,
+    N: TryInto<ZRange> + Send,
+    N::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(min, max);
+      commands::sorted_sets::zlexcount(self, key, min, max).await?.convert()
+    }
+  }
+
+  /// Removes and returns up to count members with the highest scores in the sorted set stored at `key`.
+  ///
+  /// <https://redis.io/commands/zpopmax>
+  fn zpopmax<R, K>(&self, key: K, count: Option<usize>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::sorted_sets::zpopmax(self, key, count).await?.convert()
+    }
+  }
+
+  /// Removes and returns up to count members with the lowest scores in the sorted set stored at `key`.
+  ///
+  /// <https://redis.io/commands/zpopmin>
+  fn zpopmin<R, K>(&self, key: K, count: Option<usize>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::sorted_sets::zpopmin(self, key, count).await?.convert()
+    }
+  }
+
+  /// Pops one or more elements, that are member-score pairs, from the first non-empty sorted set in the provided list
+  /// of key names.
+  ///
+  /// <https://redis.io/commands/zmpop/>
+  fn zmpop<R, K>(&self, keys: K, sort: ZCmp, count: Option<i64>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+  {
+    async move {
+      into!(keys);
+      commands::sorted_sets::zmpop(self, keys, sort, count).await?.convert()
+    }
+  }
+
+  /// When called with just the key argument, return a random element from the sorted set value stored at `key`.
+  ///
+  /// <https://redis.io/commands/zrandmember>
+  fn zrandmember<R, K>(&self, key: K, count: Option<(i64, bool)>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::sorted_sets::zrandmember(self, key, count).await?.convert()
+    }
+  }
+
+  /// This command is like ZRANGE, but stores the result in the `destination` key.
+  ///
+  /// <https://redis.io/commands/zrangestore>
+  fn zrangestore<R, D, S, M, N>(
+    &self,
+    dest: D,
+    source: S,
+    min: M,
+    max: N,
+    sort: Option<ZSort>,
+    rev: bool,
+    limit: Option<Limit>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    D: Into<RedisKey> + Send,
+    S: Into<RedisKey> + Send,
+    M: TryInto<ZRange> + Send,
+    M::Error: Into<RedisError> + Send,
+    N: TryInto<ZRange> + Send,
+    N::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(dest, source);
+      try_into!(min, max);
+      commands::sorted_sets::zrangestore(self, dest, source, min, max, sort, rev, limit)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns the specified range of elements in the sorted set stored at `key`.
+  ///
+  /// <https://redis.io/commands/zrange>
+  fn zrange<R, K, M, N>(
+    &self,
+    key: K,
+    min: M,
+    max: N,
+    sort: Option<ZSort>,
+    rev: bool,
+    limit: Option<Limit>,
+    withscores: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    M: TryInto<ZRange> + Send,
+    M::Error: Into<RedisError> + Send,
+    N: TryInto<ZRange> + Send,
+    N::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(min, max);
+      commands::sorted_sets::zrange(self, key, min, max, sort, rev, limit, withscores)
+        .await?
+        .convert()
+    }
+  }
+
+  /// When all the elements in a sorted set are inserted with the same score, in order to force lexicographical
+  /// ordering, this command returns all the elements in the sorted set at `key` with a value between `min` and `max`.
+  ///
+  /// <https://redis.io/commands/zrangebylex>
+  fn zrangebylex<R, K, M, N>(
+    &self,
+    key: K,
+    min: M,
+    max: N,
+    limit: Option<Limit>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    M: TryInto<ZRange> + Send,
+    M::Error: Into<RedisError> + Send,
+    N: TryInto<ZRange> + Send,
+    N::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(min, max);
+      commands::sorted_sets::zrangebylex(self, key, min, max, limit)
+        .await?
+        .convert()
+    }
+  }
+
+  /// When all the elements in a sorted set are inserted with the same score, in order to force lexicographical
+  /// ordering, this command returns all the elements in the sorted set at `key` with a value between `max` and `min`.
+  ///
+  /// <https://redis.io/commands/zrevrangebylex>
+  fn zrevrangebylex<R, K, M, N>(
+    &self,
+    key: K,
+    max: M,
+    min: N,
+    limit: Option<Limit>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    M: TryInto<ZRange> + Send,
+    M::Error: Into<RedisError> + Send,
+    N: TryInto<ZRange> + Send,
+    N::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(max, min);
+      commands::sorted_sets::zrevrangebylex(self, key, max, min, limit)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns all the elements in the sorted set at key with a score between `min` and `max` (including elements
+  /// with score equal to `min` or `max`).
+  ///
+  /// <https://redis.io/commands/zrangebyscore>
+  fn zrangebyscore<R, K, M, N>(
+    &self,
+    key: K,
+    min: M,
+    max: N,
+    withscores: bool,
+    limit: Option<Limit>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    M: TryInto<ZRange> + Send,
+    M::Error: Into<RedisError> + Send,
+    N: TryInto<ZRange> + Send,
+    N::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(min, max);
+      commands::sorted_sets::zrangebyscore(self, key, min, max, withscores, limit)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns all the elements in the sorted set at `key` with a score between `max` and `min` (including
+  /// elements with score equal to `max` or `min`).
+  ///
+  /// <https://redis.io/commands/zrevrangebyscore>
+  fn zrevrangebyscore<R, K, M, N>(
+    &self,
+    key: K,
+    max: M,
+    min: N,
+    withscores: bool,
+    limit: Option<Limit>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    M: TryInto<ZRange> + Send,
+    M::Error: Into<RedisError> + Send,
+    N: TryInto<ZRange> + Send,
+    N::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(max, min);
+      commands::sorted_sets::zrevrangebyscore(self, key, max, min, withscores, limit)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns the rank of member in the sorted set stored at `key`, with the scores ordered from low to high.
+  ///
+  /// <https://redis.io/commands/zrank>
+  fn zrank<R, K, V>(&self, key: K, member: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(member);
+      commands::sorted_sets::zrank(self, key, member).await?.convert()
+    }
+  }
+
+  /// Removes the specified members from the sorted set stored at `key`. Non existing members are ignored.
+  ///
+  /// <https://redis.io/commands/zrem>
+  fn zrem<R, K, V>(&self, key: K, members: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(members);
+      commands::sorted_sets::zrem(self, key, members).await?.convert()
+    }
+  }
+
+  /// When all the elements in a sorted set are inserted with the same score, in order to force lexicographical
+  /// ordering, this command removes all elements in the sorted set stored at `key` between the lexicographical range
+  /// specified by `min` and `max`.
+  ///
+  /// <https://redis.io/commands/zremrangebylex>
+  fn zremrangebylex<R, K, M, N>(&self, key: K, min: M, max: N) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    M: TryInto<ZRange> + Send,
+    M::Error: Into<RedisError> + Send,
+    N: TryInto<ZRange> + Send,
+    N::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(min, max);
+      commands::sorted_sets::zremrangebylex(self, key, min, max)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Removes all elements in the sorted set stored at `key` with rank between `start` and `stop`.
+  ///
+  /// <https://redis.io/commands/zremrangebyrank>
+  fn zremrangebyrank<R, K>(&self, key: K, start: i64, stop: i64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::sorted_sets::zremrangebyrank(self, key, start, stop)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Removes all elements in the sorted set stored at `key` with a score between `min` and `max`.
+  ///
+  /// <https://redis.io/commands/zremrangebyscore>
+  fn zremrangebyscore<R, K, M, N>(&self, key: K, min: M, max: N) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    M: TryInto<ZRange> + Send,
+    M::Error: Into<RedisError> + Send,
+    N: TryInto<ZRange> + Send,
+    N::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(min, max);
+      commands::sorted_sets::zremrangebyscore(self, key, min, max)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns the specified range of elements in the sorted set stored at `key`.
+  ///
+  /// <https://redis.io/commands/zrevrange>
+  fn zrevrange<R, K>(
+    &self,
+    key: K,
+    start: i64,
+    stop: i64,
+    withscores: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::sorted_sets::zrevrange(self, key, start, stop, withscores)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns the rank of `member` in the sorted set stored at `key`, with the scores ordered from high to low.
+  ///
+  /// <https://redis.io/commands/zrevrank>
+  fn zrevrank<R, K, V>(&self, key: K, member: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(member);
+      commands::sorted_sets::zrevrank(self, key, member).await?.convert()
+    }
+  }
+
+  /// Returns the score of `member` in the sorted set at `key`.
+  ///
+  /// <https://redis.io/commands/zscore>
+  fn zscore<R, K, V>(&self, key: K, member: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<RedisValue> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(member);
+      commands::sorted_sets::zscore(self, key, member).await?.convert()
+    }
+  }
+
+  /// This command is similar to ZUNIONSTORE, but instead of storing the resulting sorted set, it is returned to the
+  /// client.
+  ///
+  /// <https://redis.io/commands/zunion>
+  fn zunion<R, K, W>(
+    &self,
+    keys: K,
+    weights: W,
+    aggregate: Option<AggregateOptions>,
+    withscores: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+    W: Into<MultipleWeights> + Send,
+  {
+    async move {
+      into!(keys, weights);
+      commands::sorted_sets::zunion(self, keys, weights, aggregate, withscores)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Computes the union of the sorted sets given by the specified keys, and stores the result in `destination`.
+  ///
+  /// <https://redis.io/commands/zunionstore>
+  fn zunionstore<R, D, K, W>(
+    &self,
+    dest: D,
+    keys: K,
+    weights: W,
+    aggregate: Option<AggregateOptions>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    D: Into<RedisKey> + Send,
+    K: Into<MultipleKeys> + Send,
+    W: Into<MultipleWeights> + Send,
+  {
+    async move {
+      into!(dest, keys, weights);
+      commands::sorted_sets::zunionstore(self, dest, keys, weights, aggregate)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns the scores associated with the specified members in the sorted set stored at `key`.
+  ///
+  /// <https://redis.io/commands/zmscore>
+  fn zmscore<R, K, V>(&self, key: K, members: V) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(members);
+      commands::sorted_sets::zmscore(self, key, members).await?.convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/streams.rs.html b/doc/src/fred/commands/interfaces/streams.rs.html new file mode 100644 index 00000000..4c407ca8 --- /dev/null +++ b/doc/src/fred/commands/interfaces/streams.rs.html @@ -0,0 +1,1571 @@ +streams.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+
use crate::{
+  commands,
+  interfaces::{ClientLike, RedisResult},
+  prelude::RedisError,
+  types::{
+    FromRedis,
+    FromRedisKey,
+    MultipleIDs,
+    MultipleKeys,
+    MultipleOrderedPairs,
+    MultipleStrings,
+    RedisKey,
+    RedisValue,
+    XCap,
+    XPendingArgs,
+    XReadResponse,
+    XReadValue,
+    XID,
+  },
+};
+use bytes_utils::Str;
+use futures::Future;
+use rm_send_macros::rm_send_if;
+use std::{convert::TryInto, hash::Hash};
+
+/// Functions that implement the [streams](https://redis.io/commands#stream) interface.
+///
+/// **Note:** Several of the stream commands can return types with verbose type declarations. Additionally, certain
+/// commands can be parsed differently in RESP2 and RESP3 modes. Functions such as [xread_map](Self::xread_map),
+/// [xreadgroup_map](Self::xreadgroup_map), [xrange_values](Self::xrange_values), etc exist to make this easier for
+/// callers. These functions apply an additional layer of parsing logic that can make declaring response types easier,
+/// as well as automatically handling any differences between RESP2 and RESP3 return value types.
+#[rm_send_if(feature = "glommio")]
+pub trait StreamsInterface: ClientLike + Sized {
+  /// This command returns the list of consumers that belong to the `groupname` consumer group of the stream stored at
+  /// `key`.
+  ///
+  /// <https://redis.io/commands/xinfo-consumers>
+  fn xinfo_consumers<R, K, S>(&self, key: K, groupname: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(key, groupname);
+      commands::streams::xinfo_consumers(self, key, groupname)
+        .await?
+        .convert()
+    }
+  }
+
+  /// This command returns the list of all consumers groups of the stream stored at `key`.
+  ///
+  /// <https://redis.io/commands/xinfo-groups>
+  fn xinfo_groups<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::streams::xinfo_groups(self, key).await?.convert()
+    }
+  }
+
+  /// This command returns information about the stream stored at `key`.
+  ///
+  /// <https://redis.io/commands/xinfo-stream>
+  fn xinfo_stream<R, K>(&self, key: K, full: bool, count: Option<u64>) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::streams::xinfo_stream(self, key, full, count).await?.convert()
+    }
+  }
+
+  /// Appends the specified stream entry to the stream at the specified key. If the key does not exist, as a side
+  /// effect of running this command the key is created with a stream value. The creation of stream's key can be
+  /// disabled with the NOMKSTREAM option.
+  ///
+  /// <https://redis.io/commands/xadd>
+  fn xadd<R, K, C, I, F>(
+    &self,
+    key: K,
+    nomkstream: bool,
+    cap: C,
+    id: I,
+    fields: F,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    I: Into<XID> + Send,
+    F: TryInto<MultipleOrderedPairs> + Send,
+    F::Error: Into<RedisError> + Send,
+    C: TryInto<XCap> + Send,
+    C::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key, id);
+      try_into!(fields, cap);
+      commands::streams::xadd(self, key, nomkstream, cap, id, fields)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Trims the stream by evicting older entries (entries with lower IDs) if needed.
+  ///
+  /// <https://redis.io/commands/xtrim>
+  fn xtrim<R, K, C>(&self, key: K, cap: C) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    C: TryInto<XCap> + Send,
+    C::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(cap);
+      commands::streams::xtrim(self, key, cap).await?.convert()
+    }
+  }
+
+  /// Removes the specified entries from a stream, and returns the number of entries deleted.
+  ///
+  /// <https://redis.io/commands/xdel>
+  fn xdel<R, K, S>(&self, key: K, ids: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(key, ids);
+      commands::streams::xdel(self, key, ids).await?.convert()
+    }
+  }
+
+  /// Return the stream entries matching the provided range of IDs, automatically converting to a less verbose type
+  /// definition.
+  ///
+  /// <https://redis.io/commands/xrange>
+  fn xrange_values<Ri, Rk, Rv, K, S, E>(
+    &self,
+    key: K,
+    start: S,
+    end: E,
+    count: Option<u64>,
+  ) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>> + Send
+  where
+    Ri: FromRedis,
+    Rk: FromRedisKey + Hash + Eq,
+    Rv: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: TryInto<RedisValue> + Send,
+    S::Error: Into<RedisError> + Send,
+    E: TryInto<RedisValue> + Send,
+    E::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(start, end);
+      commands::streams::xrange(self, key, start, end, count)
+        .await?
+        .into_xread_value()
+    }
+  }
+
+  /// The command returns the stream entries matching a given range of IDs. The range is specified by a minimum
+  /// and maximum ID. All the entries having an ID between the two specified or exactly one of the two IDs specified
+  /// (closed interval) are returned.
+  ///
+  /// <https://redis.io/commands/xrange>
+  ///
+  /// **See [xrange_values](Self::xrange_values) for a variation of this function that may be more useful.**
+  fn xrange<R, K, S, E>(
+    &self,
+    key: K,
+    start: S,
+    end: E,
+    count: Option<u64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: TryInto<RedisValue> + Send,
+    S::Error: Into<RedisError> + Send,
+    E: TryInto<RedisValue> + Send,
+    E::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(start, end);
+      commands::streams::xrange(self, key, start, end, count).await?.convert()
+    }
+  }
+
+  /// Similar to `XRANGE`, but with the results returned in reverse order. The results will be automatically converted
+  /// to a less verbose type definition.
+  ///
+  /// <https://redis.io/commands/xrevrange>
+  fn xrevrange_values<Ri, Rk, Rv, K, E, S>(
+    &self,
+    key: K,
+    end: E,
+    start: S,
+    count: Option<u64>,
+  ) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>> + Send
+  where
+    Ri: FromRedis,
+    Rk: FromRedisKey + Hash + Eq,
+    Rv: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: TryInto<RedisValue> + Send,
+    S::Error: Into<RedisError> + Send,
+    E: TryInto<RedisValue> + Send,
+    E::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(start, end);
+      commands::streams::xrevrange(self, key, end, start, count)
+        .await?
+        .into_xread_value()
+    }
+  }
+
+  /// Similar to `XRANGE`, but with the results returned in reverse order.
+  ///
+  /// <https://redis.io/commands/xrevrange>
+  ///
+  /// **See the [xrevrange_values](Self::xrevrange_values) for a variation of this function that may be more useful.**
+  fn xrevrange<R, K, S, E>(
+    &self,
+    key: K,
+    end: E,
+    start: S,
+    count: Option<u64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: TryInto<RedisValue> + Send,
+    S::Error: Into<RedisError> + Send,
+    E: TryInto<RedisValue> + Send,
+    E::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(start, end);
+      commands::streams::xrevrange(self, key, end, start, count)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Returns the number of entries inside a stream.
+  ///
+  /// <https://redis.io/commands/xlen>
+  fn xlen<R, K>(&self, key: K) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::streams::xlen(self, key).await?.convert()
+    }
+  }
+
+  /// Read data from one or multiple streams, only returning entries with an ID greater than the last received ID
+  /// reported by the caller.
+  ///
+  /// <https://redis.io/commands/xread>
+  ///
+  /// The `XREAD` and `XREADGROUP` commands return values that can be interpreted differently in RESP2 and RESP3 mode.
+  /// In many cases it is also easier to operate on the return values of these functions as a `HashMap`, but
+  /// manually declaring this type can be very verbose. This function will automatically convert the response to the
+  /// [most common](crate::types::XReadResponse) map representation while also handling the encoding differences
+  /// between RESP2 and RESP3.
+  ///
+  /// ```rust no_run
+  /// # use fred::{prelude::*, types::XReadResponse};
+  /// async fn example(client: RedisClient) -> Result<(), RedisError> {
+  ///   // borrowed from the tests. XREAD and XREADGROUP are very similar.
+  ///   let result: XReadResponse<String, String, String, usize> = client  
+  ///     .xreadgroup_map("group1", "consumer1", None, None, false, "foo", ">")
+  ///     .await?;
+  ///   println!("Result: {:?}", result);    
+  ///   // Result: {"foo": [("1646240801081-0", {"count": 0}), ("1646240801082-0", {"count": 1}), ("1646240801082-1", {"count": 2})]}
+  ///
+  ///   assert_eq!(result.len(), 1);
+  ///   for (idx, (id, record)) in result.get("foo").unwrap().into_iter().enumerate() {
+  ///     let value = record.get("count").expect("Failed to read count");
+  ///     assert_eq!(idx, *value);
+  ///   }
+  ///
+  ///   Ok(())
+  /// }
+  /// ```
+  // The underlying issue here isn't so much a semantic difference between RESP2 and RESP3, but rather an assumption
+  // that went into the logic behind the `FromRedis` trait.
+  //
+  // In all other Redis commands that return "maps" in RESP2 (or responses that should be interpreted as maps) a map
+  // is encoded as an array with an even number of elements representing `(key, value)` pairs.
+  //
+  // As a result the `FromRedis` implementation for `HashMap`, `BTreeMap`, etc, took a dependency on this behavior. For example: https://redis.io/commands/hgetall#return-value
+  //
+  // ```
+  // 127.0.0.1:6379> hset foo bar 0
+  // (integer) 1
+  // 127.0.0.1:6379> hset foo baz 1
+  // (integer) 1
+  // 127.0.0.1:6379> hgetall foo
+  // 1) "bar"
+  // 2) "0"
+  // 3) "baz"
+  // 4) "1"
+  // // now switch to RESP3 which has a specific type for maps on the wire
+  // 127.0.0.1:6379> hello 3
+  // ...
+  // 127.0.0.1:6379> hgetall foo
+  // 1# "bar" => "0"
+  // 2# "baz" => "1"
+  // ```
+  //
+  // However, with XREAD/XREADGROUP there's an extra array wrapper in RESP2 around both the "outer" map and "inner"
+  // map(s):
+  //
+  // ```
+  // // RESP3
+  // 127.0.0.1:6379> xread count 2 streams foo bar 1643479648480-0 1643479834990-0
+  // 1# "foo" => 1) 1) "1643479650336-0"
+  //       2) 1) "count"
+  //          2) "3"
+  // 2# "bar" => 1) 1) "1643479837746-0"
+  //       2) 1) "count"
+  //          2) "5"
+  //    2) 1) "1643479925582-0"
+  //       2) 1) "count"
+  //          2) "6"
+  //
+  // // RESP2
+  // 127.0.0.1:6379> xread count 2 streams foo bar 1643479648480-0 1643479834990-0
+  // 1) 1) "foo"
+  //    2) 1) 1) "1643479650336-0"
+  //          2) 1) "count"
+  //             2) "3"
+  // 2) 1) "bar"
+  //    2) 1) 1) "1643479837746-0"
+  //          2) 1) "count"
+  //             2) "5"
+  //       2) 1) "1643479925582-0"
+  //          2) 1) "count"
+  //             2) "6"
+  // ```
+  //
+  // The underlying functions that do the RESP2 vs RESP3 conversion are public for callers as well, so one could use a
+  // `BTreeMap` instead of a `HashMap` like so:
+  //
+  // ```
+  // let value: BTreeMap<String, Vec<(String, BTreeMap<String, usize>)>> = client
+  //   .xread::<RedisValue, _, _>(None, None, "foo", "0")
+  //   .await?
+  //   .flatten_array_values(2)
+  //   .convert()?;
+  // ```
+  fn xread_map<Rk1, Rk2, Rk3, Rv, K, I>(
+    &self,
+    count: Option<u64>,
+    block: Option<u64>,
+    keys: K,
+    ids: I,
+  ) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>> + Send
+  where
+    Rk1: FromRedisKey + Hash + Eq,
+    Rk2: FromRedis,
+    Rk3: FromRedisKey + Hash + Eq,
+    Rv: FromRedis,
+    K: Into<MultipleKeys> + Send,
+    I: Into<MultipleIDs> + Send,
+  {
+    async move {
+      into!(keys, ids);
+      commands::streams::xread(self, count, block, keys, ids)
+        .await?
+        .into_xread_response()
+    }
+  }
+
+  /// Read data from one or multiple streams, only returning entries with an ID greater than the last received ID
+  /// reported by the caller.
+  ///
+  /// <https://redis.io/commands/xread>
+  ///
+  /// **See [xread_map](Self::xread_map) for more information on a variation of this function that might be more
+  /// useful.**
+  fn xread<R, K, I>(
+    &self,
+    count: Option<u64>,
+    block: Option<u64>,
+    keys: K,
+    ids: I,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+    I: Into<MultipleIDs> + Send,
+  {
+    async move {
+      into!(keys, ids);
+      commands::streams::xread(self, count, block, keys, ids).await?.convert()
+    }
+  }
+
+  /// This command creates a new consumer group uniquely identified by `groupname` for the stream stored at `key`.
+  ///
+  /// <https://redis.io/commands/xgroup-create>
+  fn xgroup_create<R, K, S, I>(
+    &self,
+    key: K,
+    groupname: S,
+    id: I,
+    mkstream: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: Into<Str> + Send,
+    I: Into<XID> + Send,
+  {
+    async move {
+      into!(key, groupname, id);
+      commands::streams::xgroup_create(self, key, groupname, id, mkstream)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Create a consumer named `consumername` in the consumer group `groupname` of the stream that's stored at `key`.
+  ///
+  /// <https://redis.io/commands/xgroup-createconsumer>
+  fn xgroup_createconsumer<R, K, G, C>(
+    &self,
+    key: K,
+    groupname: G,
+    consumername: C,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    G: Into<Str> + Send,
+    C: Into<Str> + Send,
+  {
+    async move {
+      into!(key, groupname, consumername);
+      commands::streams::xgroup_createconsumer(self, key, groupname, consumername)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Delete a consumer named `consumername` in the consumer group `groupname` of the stream that's stored at `key`.
+  ///
+  /// <https://redis.io/commands/xgroup-delconsumer>
+  fn xgroup_delconsumer<R, K, G, C>(
+    &self,
+    key: K,
+    groupname: G,
+    consumername: C,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    G: Into<Str> + Send,
+    C: Into<Str> + Send,
+  {
+    async move {
+      into!(key, groupname, consumername);
+      commands::streams::xgroup_delconsumer(self, key, groupname, consumername)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Completely destroy a consumer group.
+  ///
+  /// <https://redis.io/commands/xgroup-destroy>
+  fn xgroup_destroy<R, K, S>(&self, key: K, groupname: S) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(key, groupname);
+      commands::streams::xgroup_destroy(self, key, groupname).await?.convert()
+    }
+  }
+
+  /// Set the last delivered ID for a consumer group.
+  ///
+  /// <https://redis.io/commands/xgroup-setid>
+  fn xgroup_setid<R, K, S, I>(&self, key: K, groupname: S, id: I) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    S: Into<Str> + Send,
+    I: Into<XID> + Send,
+  {
+    async move {
+      into!(key, groupname, id);
+      commands::streams::xgroup_setid(self, key, groupname, id)
+        .await?
+        .convert()
+    }
+  }
+
+  /// A special version of the `XREAD` command with support for consumer groups.
+  ///
+  /// Declaring proper type declarations for this command can be complicated due to the complex nature of the response
+  /// values and the differences between RESP2 and RESP3. See the [xread](Self::xread) documentation for more
+  /// information.
+  ///
+  /// <https://redis.io/commands/xreadgroup>
+  ///
+  /// The `XREAD` and `XREADGROUP` commands return values that can be interpreted differently in RESP2 and RESP3 mode.
+  /// In many cases it is also easier to operate on the return values of these functions as a `HashMap`, but
+  /// manually declaring this type can be very verbose. This function will automatically convert the response to the
+  /// [most common](crate::types::XReadResponse) map representation while also handling the encoding differences
+  /// between RESP2 and RESP3.
+  ///
+  /// See the [xread_map](Self::xread_map) documentation for more information.
+  // See the `xread_map` source docs for more information.
+  fn xreadgroup_map<Rk1, Rk2, Rk3, Rv, G, C, K, I>(
+    &self,
+    group: G,
+    consumer: C,
+    count: Option<u64>,
+    block: Option<u64>,
+    noack: bool,
+    keys: K,
+    ids: I,
+  ) -> impl Future<Output = RedisResult<XReadResponse<Rk1, Rk2, Rk3, Rv>>> + Send
+  where
+    Rk1: FromRedisKey + Hash + Eq,
+    Rk2: FromRedis,
+    Rk3: FromRedisKey + Hash + Eq,
+    Rv: FromRedis,
+    G: Into<Str> + Send,
+    C: Into<Str> + Send,
+    K: Into<MultipleKeys> + Send,
+    I: Into<MultipleIDs> + Send,
+  {
+    async move {
+      into!(group, consumer, keys, ids);
+      commands::streams::xreadgroup(self, group, consumer, count, block, noack, keys, ids)
+        .await?
+        .into_xread_response()
+    }
+  }
+
+  /// A special version of the `XREAD` command with support for consumer groups.
+  ///
+  /// Declaring proper type declarations for this command can be complicated due to the complex nature of the response
+  /// values and the differences between RESP2 and RESP3. See the [xread](Self::xread) documentation for more
+  /// information.
+  ///
+  /// <https://redis.io/commands/xreadgroup>
+  ///
+  /// **See [xreadgroup_map](Self::xreadgroup_map) for a variation of this function that might be more useful.**
+  fn xreadgroup<R, G, C, K, I>(
+    &self,
+    group: G,
+    consumer: C,
+    count: Option<u64>,
+    block: Option<u64>,
+    noack: bool,
+    keys: K,
+    ids: I,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    G: Into<Str> + Send,
+    C: Into<Str> + Send,
+    K: Into<MultipleKeys> + Send,
+    I: Into<MultipleIDs> + Send,
+  {
+    async move {
+      into!(group, consumer, keys, ids);
+      commands::streams::xreadgroup(self, group, consumer, count, block, noack, keys, ids)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Remove one or more messages from the Pending Entries List (PEL) of a stream consumer group.
+  ///
+  /// <https://redis.io/commands/xack>
+  fn xack<R, K, G, I>(&self, key: K, group: G, ids: I) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    G: Into<Str> + Send,
+    I: Into<MultipleIDs> + Send,
+  {
+    async move {
+      into!(key, group, ids);
+      commands::streams::xack(self, key, group, ids).await?.convert()
+    }
+  }
+
+  /// A variation of [xclaim](Self::xclaim) with a less verbose return type.
+  fn xclaim_values<Ri, Rk, Rv, K, G, C, I>(
+    &self,
+    key: K,
+    group: G,
+    consumer: C,
+    min_idle_time: u64,
+    ids: I,
+    idle: Option<u64>,
+    time: Option<u64>,
+    retry_count: Option<u64>,
+    force: bool,
+    justid: bool,
+  ) -> impl Future<Output = RedisResult<Vec<XReadValue<Ri, Rk, Rv>>>> + Send
+  where
+    Ri: FromRedis,
+    Rk: FromRedisKey + Hash + Eq,
+    Rv: FromRedis,
+    K: Into<RedisKey> + Send,
+    G: Into<Str> + Send,
+    C: Into<Str> + Send,
+    I: Into<MultipleIDs> + Send,
+  {
+    async move {
+      into!(key, group, consumer, ids);
+      commands::streams::xclaim(
+        self,
+        key,
+        group,
+        consumer,
+        min_idle_time,
+        ids,
+        idle,
+        time,
+        retry_count,
+        force,
+        justid,
+      )
+      .await?
+      .into_xread_value()
+    }
+  }
+
+  /// In the context of a stream consumer group, this command changes the ownership of a pending message,
+  /// so that the new owner is the consumer specified as the command argument.
+  ///
+  /// <https://redis.io/commands/xclaim>
+  ///
+  /// **See [xclaim_values](Self::xclaim_values) for a variation of this function that might be more useful.**
+  fn xclaim<R, K, G, C, I>(
+    &self,
+    key: K,
+    group: G,
+    consumer: C,
+    min_idle_time: u64,
+    ids: I,
+    idle: Option<u64>,
+    time: Option<u64>,
+    retry_count: Option<u64>,
+    force: bool,
+    justid: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    G: Into<Str> + Send,
+    C: Into<Str> + Send,
+    I: Into<MultipleIDs> + Send,
+  {
+    async move {
+      into!(key, group, consumer, ids);
+      commands::streams::xclaim(
+        self,
+        key,
+        group,
+        consumer,
+        min_idle_time,
+        ids,
+        idle,
+        time,
+        retry_count,
+        force,
+        justid,
+      )
+      .await?
+      .convert()
+    }
+  }
+
+  /// This command transfers ownership of pending stream entries that match the specified criteria. It also converts
+  /// the response type to a less verbose type declaration and handles potential differences between RESP2 and RESP3.
+  ///
+  /// <https://redis.io/commands/xautoclaim>
+  // FIXME: this type declaration wont work for Redis v7. Probably need a new FF for this...
+  fn xautoclaim_values<Ri, Rk, Rv, K, G, C, I>(
+    &self,
+    key: K,
+    group: G,
+    consumer: C,
+    min_idle_time: u64,
+    start: I,
+    count: Option<u64>,
+    justid: bool,
+  ) -> impl Future<Output = RedisResult<(String, Vec<XReadValue<Ri, Rk, Rv>>)>> + Send
+  where
+    Ri: FromRedis,
+    Rk: FromRedisKey + Hash + Eq,
+    Rv: FromRedis,
+    K: Into<RedisKey> + Send,
+    G: Into<Str> + Send,
+    C: Into<Str> + Send,
+    I: Into<XID> + Send,
+  {
+    async move {
+      into!(key, group, consumer, start);
+      commands::streams::xautoclaim(self, key, group, consumer, min_idle_time, start, count, justid)
+        .await?
+        .into_xautoclaim_values()
+    }
+  }
+
+  /// This command transfers ownership of pending stream entries that match the specified criteria.
+  ///
+  /// <https://redis.io/commands/xautoclaim>
+  ///
+  /// **Note: See [xautoclaim_values](Self::xautoclaim_values) for a variation of this function that may be more
+  /// useful.**
+  fn xautoclaim<R, K, G, C, I>(
+    &self,
+    key: K,
+    group: G,
+    consumer: C,
+    min_idle_time: u64,
+    start: I,
+    count: Option<u64>,
+    justid: bool,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    G: Into<Str> + Send,
+    C: Into<Str> + Send,
+    I: Into<XID> + Send,
+  {
+    async move {
+      into!(key, group, consumer, start);
+      commands::streams::xautoclaim(self, key, group, consumer, min_idle_time, start, count, justid)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Inspect the list of pending messages in a consumer group.
+  ///
+  /// <https://redis.io/commands/xpending>
+  fn xpending<R, K, G, A>(&self, key: K, group: G, args: A) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    G: Into<Str> + Send,
+    A: Into<XPendingArgs> + Send,
+  {
+    async move {
+      into!(key, group, args);
+      commands::streams::xpending(self, key, group, args).await?.convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/strings.rs.html b/doc/src/fred/commands/interfaces/strings.rs.html new file mode 100644 index 00000000..82523b5c --- /dev/null +++ b/doc/src/fred/commands/interfaces/strings.rs.html @@ -0,0 +1,3 @@ +strings.rs - source
1
+

+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/timeseries.rs.html b/doc/src/fred/commands/interfaces/timeseries.rs.html new file mode 100644 index 00000000..fc3888ea --- /dev/null +++ b/doc/src/fred/commands/interfaces/timeseries.rs.html @@ -0,0 +1,1059 @@ +timeseries.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+
use crate::{
+  commands,
+  interfaces::ClientLike,
+  prelude::{RedisError, RedisKey, RedisResult},
+  types::{
+    Aggregator,
+    DuplicatePolicy,
+    Encoding,
+    FromRedis,
+    GetLabels,
+    GetTimestamp,
+    GroupBy,
+    RangeAggregation,
+    RedisMap,
+    Timestamp,
+  },
+};
+use bytes_utils::Str;
+use futures::Future;
+use rm_send_macros::rm_send_if;
+
+/// A [Redis Timeseries](https://github.com/RedisTimeSeries/RedisTimeSeries/) interface.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+#[rm_send_if(feature = "glommio")]
+pub trait TimeSeriesInterface: ClientLike {
+  /// Append a sample to a time series.
+  ///
+  /// <https://redis.io/commands/ts.add/>
+  fn ts_add<R, K, T, L>(
+    &self,
+    key: K,
+    timestamp: T,
+    value: f64,
+    retention: Option<u64>,
+    encoding: Option<Encoding>,
+    chunk_size: Option<u64>,
+    on_duplicate: Option<DuplicatePolicy>,
+    labels: L,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    T: TryInto<Timestamp> + Send,
+    T::Error: Into<RedisError> + Send,
+    L: TryInto<RedisMap> + Send,
+    L::Error: Into<RedisError>,
+  {
+    async move {
+      into!(key);
+      try_into!(timestamp, labels);
+      commands::timeseries::ts_add(
+        self,
+        key,
+        timestamp,
+        value,
+        retention,
+        encoding,
+        chunk_size,
+        on_duplicate,
+        labels,
+      )
+      .await?
+      .convert()
+    }
+  }
+
+  /// Update the retention, chunk size, duplicate policy, and labels of an existing time series.
+  ///
+  /// <https://redis.io/commands/ts.alter/>
+  fn ts_alter<R, K, L>(
+    &self,
+    key: K,
+    retention: Option<u64>,
+    chunk_size: Option<u64>,
+    duplicate_policy: Option<DuplicatePolicy>,
+    labels: L,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    L: TryInto<RedisMap> + Send,
+    L::Error: Into<RedisError>,
+  {
+    async move {
+      into!(key);
+      try_into!(labels);
+      commands::timeseries::ts_alter(self, key, retention, chunk_size, duplicate_policy, labels)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Create a new time series.
+  ///
+  /// <https://redis.io/commands/ts.create/>
+  fn ts_create<R, K, L>(
+    &self,
+    key: K,
+    retention: Option<u64>,
+    encoding: Option<Encoding>,
+    chunk_size: Option<u64>,
+    duplicate_policy: Option<DuplicatePolicy>,
+    labels: L,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    L: TryInto<RedisMap> + Send,
+    L::Error: Into<RedisError>,
+  {
+    async move {
+      into!(key);
+      try_into!(labels);
+      commands::timeseries::ts_create(self, key, retention, encoding, chunk_size, duplicate_policy, labels)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Create a compaction rule.
+  ///
+  /// <https://redis.io/commands/ts.createrule/>
+  fn ts_createrule<R, S, D>(
+    &self,
+    src: S,
+    dest: D,
+    aggregation: (Aggregator, u64),
+    align_timestamp: Option<u64>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<RedisKey> + Send,
+    D: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(src, dest);
+      commands::timeseries::ts_createrule(self, src, dest, aggregation, align_timestamp)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal
+  /// to the value of the sample with the maximum existing timestamp with a given decrement.
+  ///
+  /// <https://redis.io/commands/ts.decrby/>
+  fn ts_decrby<R, K, L>(
+    &self,
+    key: K,
+    subtrahend: f64,
+    timestamp: Option<Timestamp>,
+    retention: Option<u64>,
+    uncompressed: bool,
+    chunk_size: Option<u64>,
+    labels: L,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    L: TryInto<RedisMap> + Send,
+    L::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(labels);
+      commands::timeseries::ts_decrby(
+        self,
+        key,
+        subtrahend,
+        timestamp,
+        retention,
+        uncompressed,
+        chunk_size,
+        labels,
+      )
+      .await?
+      .convert()
+    }
+  }
+
+  /// Delete all samples between two timestamps for a given time series.
+  ///
+  /// <https://redis.io/commands/ts.del/>
+  fn ts_del<R, K>(&self, key: K, from: i64, to: i64) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::timeseries::ts_del(self, key, from, to).await?.convert()
+    }
+  }
+
+  /// Delete a compaction rule.
+  ///
+  /// <https://redis.io/commands/ts.deleterule/>
+  fn ts_deleterule<R, S, D>(&self, src: S, dest: D) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<RedisKey> + Send,
+    D: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(src, dest);
+      commands::timeseries::ts_deleterule(self, src, dest).await?.convert()
+    }
+  }
+
+  /// Get the sample with the highest timestamp from a given time series.
+  ///
+  /// <https://redis.io/commands/ts.get/>
+  fn ts_get<R, K>(&self, key: K, latest: bool) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::timeseries::ts_get(self, key, latest).await?.convert()
+    }
+  }
+
+  /// Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal
+  /// to the value of the sample with the maximum existing timestamp with a given increment.
+  ///
+  /// <https://redis.io/commands/ts.incrby/>
+  fn ts_incrby<R, K, L>(
+    &self,
+    key: K,
+    addend: f64,
+    timestamp: Option<Timestamp>,
+    retention: Option<u64>,
+    uncompressed: bool,
+    chunk_size: Option<u64>,
+    labels: L,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    L: TryInto<RedisMap> + Send,
+    L::Error: Into<RedisError> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(labels);
+      commands::timeseries::ts_incrby(
+        self,
+        key,
+        addend,
+        timestamp,
+        retention,
+        uncompressed,
+        chunk_size,
+        labels,
+      )
+      .await?
+      .convert()
+    }
+  }
+
+  /// Return information and statistics for a time series.
+  ///
+  /// <https://redis.io/commands/ts.info/>
+  fn ts_info<R, K>(&self, key: K, debug: bool) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+  {
+    async move {
+      into!(key);
+      commands::timeseries::ts_info(self, key, debug).await?.convert()
+    }
+  }
+
+  /// Append new samples to one or more time series.
+  ///
+  /// <https://redis.io/commands/ts.madd/>
+  fn ts_madd<R, K, I>(&self, samples: I) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    I: IntoIterator<Item = (K, Timestamp, f64)> + Send,
+  {
+    async move {
+      let samples: Vec<_> = samples
+        .into_iter()
+        .map(|(key, ts, val)| (key.into(), ts, val))
+        .collect();
+
+      commands::timeseries::ts_madd(self, samples).await?.convert()
+    }
+  }
+
+  /// Get the sample with the highest timestamp from each time series matching a specific filter.
+  ///
+  /// See [Resp2TimeSeriesValues](crate::types::Resp2TimeSeriesValues) and
+  /// [Resp3TimeSeriesValues](crate::types::Resp3TimeSeriesValues) for more information.
+  ///
+  /// <https://redis.io/commands/ts.mget/>
+  fn ts_mget<R, L, S, I>(
+    &self,
+    latest: bool,
+    labels: Option<L>,
+    filters: I,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    L: Into<GetLabels> + Send,
+    S: Into<Str> + Send,
+    I: IntoIterator<Item = S> + Send,
+  {
+    async move {
+      let labels = labels.map(|l| l.into());
+      let filters = filters.into_iter().map(|s| s.into()).collect();
+
+      commands::timeseries::ts_mget(self, latest, labels, filters)
+        .await?
+        .convert()
+    }
+  }
+
+  /// Query a range across multiple time series by filters in the forward direction.
+  ///
+  /// See [Resp2TimeSeriesValues](crate::types::Resp2TimeSeriesValues) and
+  /// [Resp3TimeSeriesValues](crate::types::Resp3TimeSeriesValues) for more information.
+  ///
+  /// <https://redis.io/commands/ts.mrange/>
+  fn ts_mrange<R, F, T, I, S, J>(
+    &self,
+    from: F,
+    to: T,
+    latest: bool,
+    filter_by_ts: I,
+    filter_by_value: Option<(i64, i64)>,
+    labels: Option<GetLabels>,
+    count: Option<u64>,
+    aggregation: Option<RangeAggregation>,
+    filters: J,
+    group_by: Option<GroupBy>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    F: TryInto<GetTimestamp> + Send,
+    F::Error: Into<RedisError> + Send,
+    T: TryInto<GetTimestamp> + Send,
+    T::Error: Into<RedisError> + Send,
+    S: Into<Str> + Send,
+    I: IntoIterator<Item = i64> + Send,
+    J: IntoIterator<Item = S> + Send,
+  {
+    async move {
+      try_into!(from, to);
+      let filters = filters.into_iter().map(|s| s.into()).collect();
+      let filter_by_ts = filter_by_ts.into_iter().collect();
+
+      commands::timeseries::ts_mrange(
+        self,
+        from,
+        to,
+        latest,
+        filter_by_ts,
+        filter_by_value,
+        labels,
+        count,
+        aggregation,
+        filters,
+        group_by,
+      )
+      .await?
+      .convert()
+    }
+  }
+
+  /// Query a range across multiple time series by filters in the reverse direction.
+  ///
+  /// See [Resp2TimeSeriesValues](crate::types::Resp2TimeSeriesValues) and
+  /// [Resp3TimeSeriesValues](crate::types::Resp3TimeSeriesValues) for more information.
+  ///
+  /// <https://redis.io/commands/ts.mrevrange/>
+  fn ts_mrevrange<R, F, T, I, S, J>(
+    &self,
+    from: F,
+    to: T,
+    latest: bool,
+    filter_by_ts: I,
+    filter_by_value: Option<(i64, i64)>,
+    labels: Option<GetLabels>,
+    count: Option<u64>,
+    aggregation: Option<RangeAggregation>,
+    filters: J,
+    group_by: Option<GroupBy>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    F: TryInto<GetTimestamp> + Send,
+    F::Error: Into<RedisError> + Send,
+    T: TryInto<GetTimestamp> + Send,
+    T::Error: Into<RedisError> + Send,
+    S: Into<Str> + Send,
+    I: IntoIterator<Item = i64> + Send,
+    J: IntoIterator<Item = S> + Send,
+  {
+    async move {
+      try_into!(from, to);
+      let filters = filters.into_iter().map(|s| s.into()).collect();
+      let filter_by_ts = filter_by_ts.into_iter().collect();
+
+      commands::timeseries::ts_mrevrange(
+        self,
+        from,
+        to,
+        latest,
+        filter_by_ts,
+        filter_by_value,
+        labels,
+        count,
+        aggregation,
+        filters,
+        group_by,
+      )
+      .await?
+      .convert()
+    }
+  }
+
+  /// Get all time series keys matching a filter list.
+  ///
+  /// <https://redis.io/commands/ts.queryindex/>
+  fn ts_queryindex<R, S, I>(&self, filters: I) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    S: Into<Str> + Send,
+    I: IntoIterator<Item = S> + Send,
+  {
+    async move {
+      let filters = filters.into_iter().map(|s| s.into()).collect();
+      commands::timeseries::ts_queryindex(self, filters).await?.convert()
+    }
+  }
+
+  /// Query a range in forward direction.
+  ///
+  /// <https://redis.io/commands/ts.range/>
+  fn ts_range<R, K, F, T, I>(
+    &self,
+    key: K,
+    from: F,
+    to: T,
+    latest: bool,
+    filter_by_ts: I,
+    filter_by_value: Option<(i64, i64)>,
+    count: Option<u64>,
+    aggregation: Option<RangeAggregation>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    F: TryInto<GetTimestamp> + Send,
+    F::Error: Into<RedisError> + Send,
+    T: TryInto<GetTimestamp> + Send,
+    T::Error: Into<RedisError> + Send,
+    I: IntoIterator<Item = i64> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(from, to);
+      let filter_by_ts = filter_by_ts.into_iter().collect();
+
+      commands::timeseries::ts_range(
+        self,
+        key,
+        from,
+        to,
+        latest,
+        filter_by_ts,
+        filter_by_value,
+        count,
+        aggregation,
+      )
+      .await?
+      .convert()
+    }
+  }
+
+  /// Query a range in reverse direction.
+  ///
+  /// <https://redis.io/commands/ts.revrange/>
+  fn ts_revrange<R, K, F, T, I>(
+    &self,
+    key: K,
+    from: F,
+    to: T,
+    latest: bool,
+    filter_by_ts: I,
+    filter_by_value: Option<(i64, i64)>,
+    count: Option<u64>,
+    aggregation: Option<RangeAggregation>,
+  ) -> impl Future<Output = RedisResult<R>> + Send
+  where
+    R: FromRedis,
+    K: Into<RedisKey> + Send,
+    F: TryInto<GetTimestamp> + Send,
+    F::Error: Into<RedisError> + Send,
+    T: TryInto<GetTimestamp> + Send,
+    T::Error: Into<RedisError> + Send,
+    I: IntoIterator<Item = i64> + Send,
+  {
+    async move {
+      into!(key);
+      try_into!(from, to);
+      let filter_by_ts = filter_by_ts.into_iter().collect();
+
+      commands::timeseries::ts_revrange(
+        self,
+        key,
+        from,
+        to,
+        latest,
+        filter_by_ts,
+        filter_by_value,
+        count,
+        aggregation,
+      )
+      .await?
+      .convert()
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/tracking.rs.html b/doc/src/fred/commands/interfaces/tracking.rs.html new file mode 100644 index 00000000..b62d82dc --- /dev/null +++ b/doc/src/fred/commands/interfaces/tracking.rs.html @@ -0,0 +1,141 @@ +tracking.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+
use crate::{
+  commands,
+  interfaces::ClientLike,
+  prelude::RedisResult,
+  runtime::{spawn, BroadcastReceiver, JoinHandle},
+  types::{Invalidation, MultipleStrings},
+};
+use futures::Future;
+use rm_send_macros::rm_send_if;
+
+/// A high level interface that supports [client side caching](https://redis.io/docs/manual/client-side-caching/) via the [client tracking](https://redis.io/commands/client-tracking/) interface.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+#[rm_send_if(feature = "glommio")]
+pub trait TrackingInterface: ClientLike + Sized {
+  /// Send the [CLIENT TRACKING](https://redis.io/commands/client-tracking/) command to all connected servers, subscribing to [invalidation messages](Self::on_invalidation) on the same connection.
+  ///
+  /// This interface requires the RESP3 protocol mode and supports all server deployment types (centralized,
+  /// clustered, and sentinel).
+  ///
+  /// See the basic [client tracking](crate::interfaces::ClientInterface::client_tracking) function for more
+  /// information on the underlying commands.
+  fn start_tracking<P>(
+    &self,
+    prefixes: P,
+    bcast: bool,
+    optin: bool,
+    optout: bool,
+    noloop: bool,
+  ) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    P: Into<MultipleStrings> + Send,
+  {
+    async move {
+      into!(prefixes);
+      commands::tracking::start_tracking(self, prefixes, bcast, optin, optout, noloop).await
+    }
+  }
+
+  /// Disable client tracking on all connections.
+  fn stop_tracking(&self) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::tracking::stop_tracking(self).await }
+  }
+
+  /// Spawn a task that processes invalidation messages from the server.
+  ///
+  /// See [invalidation_rx](Self::invalidation_rx) for a more flexible variation of this function.
+  fn on_invalidation<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
+  where
+    F: Fn(Invalidation) -> RedisResult<()> + Send + 'static,
+  {
+    let mut invalidation_rx = self.invalidation_rx();
+
+    spawn(async move {
+      let mut result = Ok(());
+
+      while let Ok(invalidation) = invalidation_rx.recv().await {
+        if let Err(err) = func(invalidation) {
+          result = Err(err);
+          break;
+        }
+      }
+      result
+    })
+  }
+
+  /// Subscribe to invalidation messages from the server(s).
+  fn invalidation_rx(&self) -> BroadcastReceiver<Invalidation> {
+    self.inner().notifications.invalidations.load().subscribe()
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/interfaces/transactions.rs.html b/doc/src/fred/commands/interfaces/transactions.rs.html new file mode 100644 index 00000000..5baa359f --- /dev/null +++ b/doc/src/fred/commands/interfaces/transactions.rs.html @@ -0,0 +1,31 @@ +transactions.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
use crate::{clients::Transaction, interfaces::ClientLike};
+
+/// Functions that implement the [transactions](https://redis.io/commands#transactions) interface.
+///
+/// See the [Transaction](crate::clients::Transaction) client for more information;
+#[cfg(feature = "transactions")]
+#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))]
+pub trait TransactionInterface: ClientLike + Sized {
+  /// Enter a MULTI block, executing subsequent commands as a transaction.
+  ///
+  /// <https://redis.io/commands/multi>
+  fn multi(&self) -> Transaction {
+    Transaction::from_inner(self.inner())
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/commands/mod.rs.html b/doc/src/fred/commands/mod.rs.html new file mode 100644 index 00000000..3692098f --- /dev/null +++ b/doc/src/fred/commands/mod.rs.html @@ -0,0 +1,7 @@ +mod.rs - source
1
+2
+3
+
mod impls;
+pub mod interfaces;
+pub use impls::*;
+
\ No newline at end of file diff --git a/doc/src/fred/error.rs.html b/doc/src/fred/error.rs.html new file mode 100644 index 00000000..da10bdfd --- /dev/null +++ b/doc/src/fred/error.rs.html @@ -0,0 +1,853 @@ +error.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+
use bytes_utils::string::Utf8Error as BytesUtf8Error;
+use futures::channel::oneshot::Canceled;
+use redis_protocol::{error::RedisProtocolError, resp2::types::BytesFrame as Resp2Frame};
+use semver::Error as SemverError;
+use std::{
+  borrow::{Borrow, Cow},
+  convert::Infallible,
+  error::Error,
+  fmt,
+  io::Error as IoError,
+  num::{ParseFloatError, ParseIntError},
+  str,
+  str::Utf8Error,
+  string::FromUtf8Error,
+};
+use url::ParseError;
+
+/// An enum representing the type of error from Redis.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum RedisErrorKind {
+  /// A fatal client configuration error. These errors will shutdown a client and break out of any reconnection
+  /// attempts.
+  Config,
+  /// An authentication error.
+  Auth,
+  /// An IO error with the underlying connection.
+  IO,
+  /// An invalid command, such as trying to perform a `set` command on a client after calling `subscribe`.
+  InvalidCommand,
+  /// An invalid argument or set of arguments to a command.
+  InvalidArgument,
+  /// An invalid URL error.
+  Url,
+  /// A protocol error such as an invalid or unexpected frame from the server.
+  Protocol,
+  /// A TLS error.
+  #[cfg(any(
+    feature = "enable-native-tls",
+    feature = "enable-rustls",
+    feature = "enable-rustls-ring"
+  ))]
+  #[cfg_attr(
+    docsrs,
+    doc(cfg(any(
+      feature = "enable-native-tls",
+      feature = "enable-rustls",
+      feature = "enable-rustls-ring"
+    )))
+  )]
+  Tls,
+  /// An error indicating the request was canceled.
+  Canceled,
+  /// An unknown error.
+  Unknown,
+  /// A timeout error.
+  Timeout,
+  /// An error used to indicate that the cluster's state has changed. These errors will show up on the `on_error`
+  /// error stream even though the client will automatically attempt to recover.
+  Cluster,
+  /// A parser error.
+  Parse,
+  /// An error communicating with redis sentinel.
+  Sentinel,
+  /// An error indicating a value was not found, often used when trying to cast a `nil` response from the server to a
+  /// non-nullable type.
+  NotFound,
+  /// An error indicating that the caller should apply backpressure and retry the command.
+  Backpressure,
+  /// An error associated with a replica node.
+  #[cfg(feature = "replicas")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+  Replica,
+}
+
+impl RedisErrorKind {
+  pub fn to_str(&self) -> &'static str {
+    match *self {
+      RedisErrorKind::Auth => "Authentication Error",
+      RedisErrorKind::IO => "IO Error",
+      RedisErrorKind::InvalidArgument => "Invalid Argument",
+      RedisErrorKind::InvalidCommand => "Invalid Command",
+      RedisErrorKind::Url => "Url Error",
+      RedisErrorKind::Protocol => "Protocol Error",
+      RedisErrorKind::Unknown => "Unknown Error",
+      RedisErrorKind::Canceled => "Canceled",
+      RedisErrorKind::Cluster => "Cluster Error",
+      RedisErrorKind::Timeout => "Timeout Error",
+      #[cfg(any(
+        feature = "enable-native-tls",
+        feature = "enable-rustls",
+        feature = "enable-rustls-ring"
+      ))]
+      RedisErrorKind::Tls => "TLS Error",
+      RedisErrorKind::Config => "Config Error",
+      RedisErrorKind::Parse => "Parse Error",
+      RedisErrorKind::Sentinel => "Sentinel Error",
+      RedisErrorKind::NotFound => "Not Found",
+      RedisErrorKind::Backpressure => "Backpressure",
+      #[cfg(feature = "replicas")]
+      RedisErrorKind::Replica => "Replica",
+    }
+  }
+}
+
+/// An error from Redis.
+pub struct RedisError {
+  /// Details about the specific error condition.
+  details: Cow<'static, str>,
+  /// The kind of error.
+  kind:    RedisErrorKind,
+}
+
+impl Clone for RedisError {
+  fn clone(&self) -> Self {
+    RedisError::new(self.kind.clone(), self.details.clone())
+  }
+}
+
+impl PartialEq for RedisError {
+  fn eq(&self, other: &Self) -> bool {
+    self.kind == other.kind && self.details == other.details
+  }
+}
+
+impl Eq for RedisError {}
+
+impl fmt::Debug for RedisError {
+  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    write!(f, "Redis Error - kind: {:?}, details: {}", self.kind, self.details)
+  }
+}
+
+impl fmt::Display for RedisError {
+  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    write!(f, "{}: {}", self.kind.to_str(), self.details)
+  }
+}
+
+#[doc(hidden)]
+impl From<RedisProtocolError> for RedisError {
+  fn from(e: RedisProtocolError) -> Self {
+    RedisError::new(RedisErrorKind::Protocol, format!("{}", e))
+  }
+}
+
+#[doc(hidden)]
+impl From<()> for RedisError {
+  fn from(_: ()) -> Self {
+    RedisError::new(RedisErrorKind::Canceled, "Empty error.")
+  }
+}
+
+#[doc(hidden)]
+impl From<futures::channel::mpsc::SendError> for RedisError {
+  fn from(e: futures::channel::mpsc::SendError) -> Self {
+    RedisError::new(RedisErrorKind::Unknown, format!("{}", e))
+  }
+}
+
+#[doc(hidden)]
+impl From<tokio::sync::oneshot::error::RecvError> for RedisError {
+  fn from(e: tokio::sync::oneshot::error::RecvError) -> Self {
+    RedisError::new(RedisErrorKind::Unknown, format!("{}", e))
+  }
+}
+
+#[doc(hidden)]
+impl From<tokio::sync::broadcast::error::RecvError> for RedisError {
+  fn from(e: tokio::sync::broadcast::error::RecvError) -> Self {
+    RedisError::new(RedisErrorKind::Unknown, format!("{}", e))
+  }
+}
+
+#[doc(hidden)]
+impl<T: fmt::Display> From<tokio::sync::broadcast::error::SendError<T>> for RedisError {
+  fn from(e: tokio::sync::broadcast::error::SendError<T>) -> Self {
+    RedisError::new(RedisErrorKind::Unknown, format!("{}", e))
+  }
+}
+
+#[doc(hidden)]
+impl From<IoError> for RedisError {
+  fn from(e: IoError) -> Self {
+    RedisError::new(RedisErrorKind::IO, format!("{:?}", e))
+  }
+}
+
+#[doc(hidden)]
+impl From<ParseError> for RedisError {
+  fn from(e: ParseError) -> Self {
+    RedisError::new(RedisErrorKind::Url, format!("{:?}", e))
+  }
+}
+
+#[doc(hidden)]
+impl From<ParseFloatError> for RedisError {
+  fn from(_: ParseFloatError) -> Self {
+    RedisError::new(RedisErrorKind::Parse, "Invalid floating point number.")
+  }
+}
+
+#[doc(hidden)]
+impl From<ParseIntError> for RedisError {
+  fn from(_: ParseIntError) -> Self {
+    RedisError::new(RedisErrorKind::Parse, "Invalid integer string.")
+  }
+}
+
+#[doc(hidden)]
+impl From<FromUtf8Error> for RedisError {
+  fn from(_: FromUtf8Error) -> Self {
+    RedisError::new(RedisErrorKind::Parse, "Invalid UTF-8 string.")
+  }
+}
+
+#[doc(hidden)]
+impl From<Utf8Error> for RedisError {
+  fn from(_: Utf8Error) -> Self {
+    RedisError::new(RedisErrorKind::Parse, "Invalid UTF-8 string.")
+  }
+}
+
+#[doc(hidden)]
+impl<S> From<BytesUtf8Error<S>> for RedisError {
+  fn from(e: BytesUtf8Error<S>) -> Self {
+    e.utf8_error().into()
+  }
+}
+
+#[doc(hidden)]
+impl From<fmt::Error> for RedisError {
+  fn from(e: fmt::Error) -> Self {
+    RedisError::new(RedisErrorKind::Unknown, format!("{:?}", e))
+  }
+}
+
+#[doc(hidden)]
+impl From<Canceled> for RedisError {
+  fn from(e: Canceled) -> Self {
+    RedisError::new(RedisErrorKind::Canceled, format!("{}", e))
+  }
+}
+
+#[doc(hidden)]
+#[cfg(not(feature = "glommio"))]
+impl From<tokio::task::JoinError> for RedisError {
+  fn from(e: tokio::task::JoinError) -> Self {
+    RedisError::new(RedisErrorKind::Unknown, format!("Spawn Error: {:?}", e))
+  }
+}
+
+#[doc(hidden)]
+#[cfg(feature = "glommio")]
+impl<T: fmt::Debug> From<glommio::GlommioError<T>> for RedisError {
+  fn from(e: glommio::GlommioError<T>) -> Self {
+    RedisError::new(RedisErrorKind::Unknown, format!("{:?}", e))
+  }
+}
+
+#[doc(hidden)]
+#[cfg(feature = "glommio")]
+impl From<oneshot::RecvError> for RedisError {
+  fn from(_: oneshot::RecvError) -> Self {
+    RedisError::new_canceled()
+  }
+}
+
+#[doc(hidden)]
+impl From<SemverError> for RedisError {
+  fn from(e: SemverError) -> Self {
+    RedisError::new(RedisErrorKind::Protocol, format!("Invalid Redis version: {:?}", e))
+  }
+}
+
+#[doc(hidden)]
+impl From<Infallible> for RedisError {
+  fn from(e: Infallible) -> Self {
+    warn!("Infallible error: {:?}", e);
+    RedisError::new(RedisErrorKind::Unknown, "Unknown error.")
+  }
+}
+
+#[doc(hidden)]
+impl From<Resp2Frame> for RedisError {
+  fn from(e: Resp2Frame) -> Self {
+    match e {
+      Resp2Frame::SimpleString(s) => match str::from_utf8(&s).ok() {
+        Some("Canceled") => RedisError::new_canceled(),
+        _ => RedisError::new(RedisErrorKind::Unknown, "Unknown frame error."),
+      },
+      _ => RedisError::new(RedisErrorKind::Unknown, "Unknown frame error."),
+    }
+  }
+}
+
+#[doc(hidden)]
+#[cfg(feature = "enable-native-tls")]
+#[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))]
+impl From<native_tls::Error> for RedisError {
+  fn from(e: native_tls::Error) -> Self {
+    RedisError::new(RedisErrorKind::Tls, format!("{:?}", e))
+  }
+}
+
+#[doc(hidden)]
+#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))]
+impl From<rustls::pki_types::InvalidDnsNameError> for RedisError {
+  fn from(e: rustls::pki_types::InvalidDnsNameError) -> Self {
+    RedisError::new(RedisErrorKind::Tls, format!("{:?}", e))
+  }
+}
+
+#[doc(hidden)]
+#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))]
+impl From<rustls::Error> for RedisError {
+  fn from(e: rustls::Error) -> Self {
+    RedisError::new(RedisErrorKind::Tls, format!("{:?}", e))
+  }
+}
+
+#[doc(hidden)]
+#[cfg(feature = "trust-dns-resolver")]
+#[cfg_attr(docsrs, doc(cfg(feature = "trust-dns-resolver")))]
+impl From<trust_dns_resolver::error::ResolveError> for RedisError {
+  fn from(e: trust_dns_resolver::error::ResolveError) -> Self {
+    RedisError::new(RedisErrorKind::IO, format!("{:?}", e))
+  }
+}
+
+#[doc(hidden)]
+#[cfg(feature = "dns")]
+#[cfg_attr(docsrs, doc(cfg(feature = "dns")))]
+impl From<hickory_resolver::error::ResolveError> for RedisError {
+  fn from(e: hickory_resolver::error::ResolveError) -> Self {
+    RedisError::new(RedisErrorKind::IO, format!("{:?}", e))
+  }
+}
+
+#[cfg(feature = "serde-json")]
+#[cfg_attr(docsrs, doc(cfg(feature = "serde-json")))]
+impl From<serde_json::Error> for RedisError {
+  fn from(e: serde_json::Error) -> Self {
+    RedisError::new(RedisErrorKind::Parse, format!("{:?}", e))
+  }
+}
+
+impl RedisError {
+  /// Create a new Redis error with the provided details.
+  pub fn new<T>(kind: RedisErrorKind, details: T) -> RedisError
+  where
+    T: Into<Cow<'static, str>>,
+  {
+    RedisError {
+      kind,
+      details: details.into(),
+    }
+  }
+
+  /// Read the type of error without any associated data.
+  pub fn kind(&self) -> &RedisErrorKind {
+    &self.kind
+  }
+
+  /// Change the kind of the error.
+  pub fn change_kind(&mut self, kind: RedisErrorKind) {
+    self.kind = kind;
+  }
+
+  /// Read details about the error.
+  pub fn details(&self) -> &str {
+    self.details.borrow()
+  }
+
+  /// Create a new empty Canceled error.
+  pub fn new_canceled() -> Self {
+    RedisError::new(RedisErrorKind::Canceled, "Canceled.")
+  }
+
+  /// Create a new parse error with the provided details.
+  pub(crate) fn new_parse<T>(details: T) -> Self
+  where
+    T: Into<Cow<'static, str>>,
+  {
+    RedisError::new(RedisErrorKind::Parse, details)
+  }
+
+  /// Create a new default backpressure error.
+  pub(crate) fn new_backpressure() -> Self {
+    RedisError::new(RedisErrorKind::Backpressure, "Max in-flight commands reached.")
+  }
+
+  /// Whether reconnection logic should be skipped in all cases.
+  pub(crate) fn should_not_reconnect(&self) -> bool {
+    matches!(self.kind, RedisErrorKind::Config | RedisErrorKind::Url)
+  }
+
+  /// Whether the error is a `Cluster` error.
+  pub fn is_cluster(&self) -> bool {
+    matches!(self.kind, RedisErrorKind::Cluster)
+  }
+
+  /// Whether the error is a `Canceled` error.
+  pub fn is_canceled(&self) -> bool {
+    matches!(self.kind, RedisErrorKind::Canceled)
+  }
+
+  /// Whether the error is a `Replica` error.
+  #[cfg(feature = "replicas")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+  pub fn is_replica(&self) -> bool {
+    matches!(self.kind, RedisErrorKind::Replica)
+  }
+
+  /// Whether the error is a `NotFound` error.
+  pub fn is_not_found(&self) -> bool {
+    matches!(self.kind, RedisErrorKind::NotFound)
+  }
+}
+
+impl Error for RedisError {
+  fn source(&self) -> Option<&(dyn Error + 'static)> {
+    None
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/glommio/broadcast.rs.html b/doc/src/fred/glommio/broadcast.rs.html new file mode 100644 index 00000000..f4234f68 --- /dev/null +++ b/doc/src/fred/glommio/broadcast.rs.html @@ -0,0 +1,187 @@ +broadcast.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+
use crate::error::RedisError;
+use glommio::{
+  channels::local_channel::{new_unbounded, LocalReceiver, LocalSender},
+  GlommioError,
+  ResourceType,
+};
+use std::{cell::RefCell, collections::BTreeMap, rc::Rc};
+
+struct Inner<T: Clone> {
+  pub counter: u64,
+  pub senders: BTreeMap<u64, LocalSender<T>>,
+}
+
+/// A multi-producer multi-consumer channel receiver.
+///
+/// See [LocalReceiver](glommio::channels::local_channel::LocalReceiver) for more information.
+pub struct BroadcastReceiver<T: Clone> {
+  id:    u64,
+  inner: Rc<RefCell<Inner<T>>>,
+  rx:    LocalReceiver<T>,
+}
+
+impl<T: Clone> BroadcastReceiver<T> {
+  /// Receives data from this channel.
+  ///
+  /// See [recv](glommio::channels::local_channel::LocalReceiver::recv) for more information.
+  pub async fn recv(&self) -> Result<T, RedisError> {
+    match self.rx.recv().await {
+      Some(v) => Ok(v),
+      None => Err(RedisError::new_canceled()),
+    }
+  }
+}
+
+impl<T: Clone> Drop for BroadcastReceiver<T> {
+  fn drop(&mut self) {
+    self.inner.as_ref().borrow_mut().senders.remove(&self.id);
+  }
+}
+
+#[derive(Clone)]
+pub struct BroadcastSender<T: Clone> {
+  inner: Rc<RefCell<Inner<T>>>,
+}
+
+impl<T: Clone> BroadcastSender<T> {
+  pub fn new() -> Self {
+    BroadcastSender {
+      inner: Rc::new(RefCell::new(Inner {
+        counter: 0,
+        senders: BTreeMap::new(),
+      })),
+    }
+  }
+
+  pub fn subscribe(&self) -> BroadcastReceiver<T> {
+    let (tx, rx) = new_unbounded();
+    let id = {
+      let mut guard = self.inner.as_ref().borrow_mut();
+      let count = guard.counter.wrapping_add(1);
+      guard.counter = count;
+      guard.senders.insert(count, tx);
+      guard.counter
+    };
+
+    BroadcastReceiver {
+      id,
+      rx,
+      inner: self.inner.clone(),
+    }
+  }
+
+  pub fn send<F: Fn(&T)>(&self, msg: &T, func: F) {
+    let mut guard = self.inner.as_ref().borrow_mut();
+
+    let to_remove: Vec<u64> = guard
+      .senders
+      .iter()
+      .filter_map(|(id, tx)| {
+        if let Err(GlommioError::Closed(ResourceType::Channel(val))) = tx.try_send(msg.clone()) {
+          func(&val);
+          Some(*id)
+        } else {
+          None
+        }
+      })
+      .collect();
+
+    for id in to_remove.into_iter() {
+      guard.senders.remove(&id);
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/glommio/interfaces.rs.html b/doc/src/fred/glommio/interfaces.rs.html new file mode 100644 index 00000000..3b824fa2 --- /dev/null +++ b/doc/src/fred/glommio/interfaces.rs.html @@ -0,0 +1,709 @@ +interfaces.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+
use crate::{
+  clients::WithOptions,
+  commands,
+  error::RedisError,
+  interfaces::{RedisResult, Resp3Frame},
+  modules::inner::RedisClientInner,
+  prelude::default_send_command,
+  protocol::command::RedisCommand,
+  router::commands as router_commands,
+  runtime::{spawn, BroadcastReceiver, JoinHandle, RefCount},
+  types::{
+    ClientState,
+    ConnectHandle,
+    ConnectionConfig,
+    CustomCommand,
+    FromRedis,
+    InfoKind,
+    Options,
+    PerformanceConfig,
+    ReconnectPolicy,
+    RedisConfig,
+    RedisValue,
+    Server,
+  },
+  utils,
+};
+use redis_protocol::resp3::types::RespVersion;
+use semver::Version;
+use std::future::Future;
+
+#[cfg(feature = "i-server")]
+use crate::types::ShutdownFlags;
+
+#[cfg(any(feature = "dns", feature = "trust-dns-resolver"))]
+use crate::protocol::types::Resolve;
+
+pub trait ClientLike: Clone + Sized {
+  #[doc(hidden)]
+  fn inner(&self) -> &RefCount<RedisClientInner>;
+
+  /// Helper function to intercept and modify a command without affecting how it is sent to the connection layer.
+  #[doc(hidden)]
+  fn change_command(&self, _: &mut RedisCommand) {}
+
+  /// Helper function to intercept and customize how a command is sent to the connection layer.
+  #[doc(hidden)]
+  fn send_command<C>(&self, command: C) -> Result<(), RedisError>
+  where
+    C: Into<RedisCommand>,
+  {
+    let mut command: RedisCommand = command.into();
+    self.change_command(&mut command);
+    default_send_command(self.inner(), command)
+  }
+
+  /// The unique ID identifying this client and underlying connections.
+  fn id(&self) -> &str {
+    &self.inner().id
+  }
+
+  /// Read the config used to initialize the client.
+  fn client_config(&self) -> RedisConfig {
+    self.inner().config.as_ref().clone()
+  }
+
+  /// Read the reconnect policy used to initialize the client.
+  fn client_reconnect_policy(&self) -> Option<ReconnectPolicy> {
+    self.inner().policy.read().clone()
+  }
+
+  /// Read the connection config used to initialize the client.
+  fn connection_config(&self) -> &ConnectionConfig {
+    self.inner().connection.as_ref()
+  }
+
+  /// Read the RESP version used by the client when communicating with the server.
+  fn protocol_version(&self) -> RespVersion {
+    if self.inner().is_resp3() {
+      RespVersion::RESP3
+    } else {
+      RespVersion::RESP2
+    }
+  }
+
+  /// Whether the client has a reconnection policy.
+  fn has_reconnect_policy(&self) -> bool {
+    self.inner().policy.read().is_some()
+  }
+
+  /// Whether the client will automatically pipeline commands.
+  fn is_pipelined(&self) -> bool {
+    self.inner().is_pipelined()
+  }
+
+  /// Whether the client is connected to a cluster.
+  fn is_clustered(&self) -> bool {
+    self.inner().config.server.is_clustered()
+  }
+
+  /// Whether the client uses the sentinel interface.
+  fn uses_sentinels(&self) -> bool {
+    self.inner().config.server.is_sentinel()
+  }
+
+  /// Update the internal [PerformanceConfig](crate::types::PerformanceConfig) in place with new values.
+  fn update_perf_config(&self, config: PerformanceConfig) {
+    self.inner().update_performance_config(config);
+  }
+
+  /// Read the [PerformanceConfig](crate::types::PerformanceConfig) associated with this client.
+  fn perf_config(&self) -> PerformanceConfig {
+    self.inner().performance_config()
+  }
+
+  /// Read the state of the underlying connection(s).
+  ///
+  /// If running against a cluster the underlying state will reflect the state of the least healthy connection.
+  fn state(&self) -> ClientState {
+    self.inner().state.read().clone()
+  }
+
+  /// Whether all underlying connections are healthy.
+  fn is_connected(&self) -> bool {
+    *self.inner().state.read() == ClientState::Connected
+  }
+
+  /// Read the set of active connections managed by the client.
+  fn active_connections(&self) -> impl Future<Output = Result<Vec<Server>, RedisError>> {
+    commands::server::active_connections(self)
+  }
+
+  /// Read the server version, if known.
+  fn server_version(&self) -> Option<Version> {
+    self.inner().server_state.read().kind.server_version()
+  }
+
+  /// Override the DNS resolution logic for the client.
+  #[cfg(feature = "dns")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "dns")))]
+  fn set_resolver(&self, resolver: RefCount<dyn Resolve>) -> impl Future {
+    async move { self.inner().set_resolver(resolver).await }
+  }
+
+  /// Connect to the server.
+  ///
+  /// This function returns a `JoinHandle` to a task that drives the connection. It will not resolve until the
+  /// connection closes, or if a reconnection policy with unlimited attempts is provided then it will
+  /// run until `QUIT` is called. Callers should avoid calling [abort](tokio::task::JoinHandle::abort) on the returned
+  /// `JoinHandle` unless the client will no longer be used.
+  ///
+  /// **Calling this function more than once will drop all state associated with the previous connection(s).** Any
+  /// pending commands on the old connection(s) will either finish or timeout, but they will not be retried on the
+  /// new connection(s).
+  ///
+  /// See [init](Self::init) for an alternative shorthand.
+  fn connect(&self) -> ConnectHandle {
+    let inner = self.inner().clone();
+    utils::reset_router_task(&inner);
+
+    spawn(async move {
+      utils::clear_backchannel_state(&inner).await;
+      let result = router_commands::start(&inner).await;
+      // a canceled error means we intentionally closed the client
+      _trace!(inner, "Ending connection task with {:?}", result);
+
+      if let Err(ref error) = result {
+        if !error.is_canceled() {
+          inner.notifications.broadcast_connect(Err(error.clone()));
+        }
+      }
+
+      utils::check_and_set_client_state(&inner.state, ClientState::Disconnecting, ClientState::Disconnected);
+      result
+    })
+  }
+
+  /// Force a reconnection to the server(s).
+  ///
+  /// When running against a cluster this function will also refresh the cached cluster routing table.
+  fn force_reconnection(&self) -> impl Future<Output = RedisResult<()>> {
+    async move { commands::server::force_reconnection(self.inner()).await }
+  }
+
+  /// Wait for the result of the next connection attempt.
+  ///
+  /// This can be used with `on_reconnect` to separate initialization logic that needs to occur only on the next
+  /// connection attempt vs all subsequent attempts.
+  fn wait_for_connect(&self) -> impl Future<Output = RedisResult<()>> {
+    async move {
+      if { utils::read_locked(&self.inner().state) } == ClientState::Connected {
+        debug!("{}: Client is already connected.", self.inner().id);
+        Ok(())
+      } else {
+        let rx = { self.inner().notifications.connect.load().subscribe() };
+        rx.recv().await?
+      }
+    }
+  }
+
+  /// Initialize a new routing and connection task and wait for it to connect successfully.
+  ///
+  /// The returned [ConnectHandle](crate::types::ConnectHandle) refers to the task that drives the routing and
+  /// connection layer. It will not finish until the max reconnection count is reached. Callers should avoid calling
+  /// [abort](tokio::task::JoinHandle::abort) on the returned `JoinHandle` unless the client will no longer be used.
+  ///
+  /// Callers can also use [connect](Self::connect) and [wait_for_connect](Self::wait_for_connect) separately if
+  /// needed.
+  ///
+  /// ```rust
+  /// use fred::prelude::*;
+  ///
+  /// #[tokio::main]
+  /// async fn main() -> Result<(), RedisError> {
+  ///   let client = RedisClient::default();
+  ///   let connection_task = client.init().await?;
+  ///
+  ///   // ...
+  ///
+  ///   client.quit().await?;
+  ///   connection_task.await?
+  /// }
+  /// ```
+  fn init(&self) -> impl Future<Output = RedisResult<ConnectHandle>> {
+    async move {
+      let rx = { self.inner().notifications.connect.load().subscribe() };
+      let task = self.connect();
+      let error = rx.recv().await.map_err(RedisError::from).and_then(|r| r).err();
+
+      if let Some(error) = error {
+        // the initial connection failed, so we should gracefully close the routing task
+        utils::reset_router_task(self.inner());
+        Err(error)
+      } else {
+        Ok(task)
+      }
+    }
+  }
+
+  /// Close the connection to the Redis server. The returned future resolves when the command has been written to the
+  /// socket, not when the connection has been fully closed. Some time after this future resolves the future
+  /// returned by [connect](Self::connect) will resolve which indicates that the connection has been fully closed.
+  ///
+  /// This function will also close all error, pubsub message, and reconnection event streams.
+  fn quit(&self) -> impl Future<Output = RedisResult<()>> {
+    async move { commands::server::quit(self).await }
+  }
+
+  /// Shut down the server and quit the client.
+  ///
+  /// <https://redis.io/commands/shutdown>
+  #[cfg(feature = "i-server")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+  fn shutdown(&self, flags: Option<ShutdownFlags>) -> impl Future<Output = RedisResult<()>> {
+    async move { commands::server::shutdown(self, flags).await }
+  }
+
+  /// Delete the keys in all databases.
+  ///
+  /// <https://redis.io/commands/flushall>
+  fn flushall<R>(&self, r#async: bool) -> impl Future<Output = RedisResult<R>>
+  where
+    R: FromRedis,
+  {
+    async move { commands::server::flushall(self, r#async).await?.convert() }
+  }
+
+  /// Delete the keys on all nodes in the cluster. This is a special function that does not map directly to the Redis
+  /// interface.
+  fn flushall_cluster(&self) -> impl Future<Output = RedisResult<()>> {
+    async move { commands::server::flushall_cluster(self).await }
+  }
+
+  /// Ping the Redis server.
+  ///
+  /// <https://redis.io/commands/ping>
+  fn ping<R>(&self) -> impl Future<Output = RedisResult<R>>
+  where
+    R: FromRedis,
+  {
+    async move { commands::server::ping(self).await?.convert() }
+  }
+
+  /// Read info about the server.
+  ///
+  /// <https://redis.io/commands/info>
+  fn info<R>(&self, section: Option<InfoKind>) -> impl Future<Output = RedisResult<R>>
+  where
+    R: FromRedis,
+  {
+    async move { commands::server::info(self, section).await?.convert() }
+  }
+
+  /// Run a custom command that is not yet supported via another interface on this client. This is most useful when
+  /// interacting with third party modules or extensions.
+  ///
+  /// Callers should use the re-exported [redis_keyslot](crate::util::redis_keyslot) function to hash the command's
+  /// key, if necessary.
+  ///
+  /// This interface should be used with caution as it may break the automatic pipeline features in the client if
+  /// command flags are not properly configured.
+  fn custom<R, T>(&self, cmd: CustomCommand, args: Vec<T>) -> impl Future<Output = RedisResult<R>>
+  where
+    R: FromRedis,
+    T: TryInto<RedisValue>,
+    T::Error: Into<RedisError>,
+  {
+    async move {
+      let args = utils::try_into_vec(args)?;
+      commands::server::custom(self, cmd, args).await?.convert()
+    }
+  }
+
+  /// Run a custom command similar to [custom](Self::custom), but return the response frame directly without any
+  /// parsing.
+  ///
+  /// Note: RESP2 frames from the server are automatically converted to the RESP3 format when parsed by the client.
+  fn custom_raw<T>(&self, cmd: CustomCommand, args: Vec<T>) -> impl Future<Output = RedisResult<Resp3Frame>>
+  where
+    T: TryInto<RedisValue>,
+    T::Error: Into<RedisError>,
+  {
+    async move {
+      let args = utils::try_into_vec(args)?;
+      commands::server::custom_raw(self, cmd, args).await
+    }
+  }
+
+  /// Customize various configuration options on commands.
+  fn with_options(&self, options: &Options) -> WithOptions<Self> {
+    WithOptions {
+      client:  self.clone(),
+      options: options.clone(),
+    }
+  }
+}
+
+pub fn spawn_event_listener<T, F>(rx: BroadcastReceiver<T>, func: F) -> JoinHandle<RedisResult<()>>
+where
+  T: Clone + 'static,
+  F: Fn(T) -> RedisResult<()> + 'static,
+{
+  spawn(async move {
+    let mut result = Ok(());
+
+    while let Ok(val) = rx.recv().await {
+      if let Err(err) = func(val) {
+        result = Err(err);
+        break;
+      }
+    }
+
+    result
+  })
+}
+
\ No newline at end of file diff --git a/doc/src/fred/glommio/io_compat.rs.html b/doc/src/fred/glommio/io_compat.rs.html new file mode 100644 index 00000000..bf911480 --- /dev/null +++ b/doc/src/fred/glommio/io_compat.rs.html @@ -0,0 +1,137 @@ +io_compat.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+
/// Reuse the same approach used by gmf (https://github.com/EtaCassiopeia/gmf/blob/591037476e6a17f83954a20558ff0e1920d94301/gmf/src/server/tokio_interop.rs#L1).
+///
+/// The `Framed<T, U>` codec interface used by the `Connection` struct requires that `T: AsyncRead+AsyncWrite`.
+/// These traits are defined in the tokio and futures_io/futures_lite crates, but the tokio_util::codec interface
+/// uses the versions re-implemented in tokio. However, glommio's network interfaces implement
+/// `AsyncRead+AsyncWrite` from the futures_io crate. There are several ways to work around this, including
+/// either a re-implementation of the codec traits `Encoder+Decoder`, or a compatibility layer for the different
+/// versions of `AsyncRead+AsyncWrite`. The `gmf` project used the second approach, which seems much easier than
+/// re-implementing the `Framed` traits (https://github.com/tokio-rs/tokio/blob/1ac8dff213937088616dc84de9adc92b4b68c49a/tokio-util/src/codec/framed_impl.rs#L125).
+
+// ------------------- https://github.com/EtaCassiopeia/gmf/blob/591037476e6a17f83954a20558ff0e1920d94301/gmf/src/server/tokio_interop.rs
+
+/// This module provides interoperability with the Tokio async runtime.
+/// It contains utilities to bridge between futures_lite and Tokio.
+use std::io::{self};
+use std::{
+  pin::Pin,
+  task::{Context, Poll},
+};
+
+use futures_io::{AsyncRead, AsyncWrite};
+use tokio::io::ReadBuf;
+
+/// A wrapper type for AsyncRead + AsyncWrite + Unpin types, providing
+/// interoperability with Tokio's AsyncRead and AsyncWrite traits.
+#[pin_project::pin_project] // This generates a projection for the inner type.
+pub struct TokioIO<T>(#[pin] pub T)
+where
+  T: AsyncRead + AsyncWrite + Unpin;
+
+impl<T> tokio::io::AsyncWrite for TokioIO<T>
+where
+  T: AsyncRead + AsyncWrite + Unpin,
+{
+  /// Write some data into the inner type, returning how many bytes were written.
+  fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll<io::Result<usize>> {
+    // This is the same as  Pin::new(&mut self.0).poll_write(cx, buf) with the source type of `mut self`
+    // using projection makes it easier to read.
+    let this = self.project();
+    this.0.poll_write(cx, buf)
+  }
+
+  /// Flushes the inner type.
+  fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
+    self.project().0.poll_flush(cx)
+  }
+
+  /// Shuts down the inner type, flushing any buffered data.
+  fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
+    self.project().0.poll_close(cx)
+  }
+}
+
+impl<T> tokio::io::AsyncRead for TokioIO<T>
+where
+  T: AsyncRead + AsyncWrite + Unpin,
+{
+  /// Reads some data from the inner type, returning how many bytes were read.
+  fn poll_read(self: Pin<&mut Self>, cx: &mut Context, buf: &mut ReadBuf<'_>) -> Poll<io::Result<()>> {
+    self.project().0.poll_read(cx, buf.initialize_unfilled()).map(|n| {
+      if let Ok(n) = n {
+        buf.advance(n);
+      }
+
+      Ok(())
+    })
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/glommio/mod.rs.html b/doc/src/fred/glommio/mod.rs.html new file mode 100644 index 00000000..538cec77 --- /dev/null +++ b/doc/src/fred/glommio/mod.rs.html @@ -0,0 +1,245 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+
#[cfg(all(feature = "glommio", feature = "unix-sockets"))]
+compile_error!("Cannot use glommio and unix-sockets features together.");
+
+pub(crate) mod broadcast;
+pub(crate) mod interfaces;
+pub(crate) mod io_compat;
+pub(crate) mod mpsc;
+pub(crate) mod sync;
+
+pub(crate) mod compat {
+  pub use super::{
+    broadcast::{BroadcastReceiver, BroadcastSender},
+    mpsc::{rx_stream, UnboundedReceiver, UnboundedSender},
+    sync::*,
+  };
+  use crate::error::RedisError;
+  use futures::Future;
+  use glommio::TaskQueueHandle;
+  pub use glommio::{
+    channels::local_channel::new_unbounded as unbounded_channel,
+    task::JoinHandle as GlommioJoinHandle,
+    timer::sleep,
+  };
+  pub use oneshot::{channel as oneshot_channel, Receiver as OneshotReceiver, Sender as OneshotSender};
+  use std::{
+    cell::RefCell,
+    pin::Pin,
+    rc::Rc,
+    task::{Context, Poll},
+  };
+
+  /// The reference counting container type.
+  ///
+  /// This type may change based on the runtime feature flags used.
+  pub type RefCount<T> = Rc<T>;
+
+  pub fn broadcast_send<T: Clone, F: Fn(&T)>(tx: &BroadcastSender<T>, msg: &T, func: F) {
+    tx.send(msg, func);
+  }
+
+  pub fn broadcast_channel<T: Clone>(_: usize) -> (BroadcastSender<T>, BroadcastReceiver<T>) {
+    let tx = BroadcastSender::new();
+    let rx = tx.subscribe();
+    (tx, rx)
+  }
+
+  /// A wrapper type around [JoinHandle](glommio::task::JoinHandle) with an interface similar to Tokio's
+  /// [JoinHandle](tokio::task::JoinHandle)
+  pub struct JoinHandle<T> {
+    pub(crate) inner:    GlommioJoinHandle<T>,
+    pub(crate) finished: Rc<RefCell<bool>>,
+  }
+
+  pub fn spawn<T: 'static>(ft: impl Future<Output = T> + 'static) -> JoinHandle<T> {
+    let finished = Rc::new(RefCell::new(false));
+    let _finished = finished.clone();
+    let inner = glommio::spawn_local(async move {
+      let result = ft.await;
+      _finished.replace(true);
+      result
+    })
+    .detach();
+
+    JoinHandle { inner, finished }
+  }
+
+  // TODO use with connection config task queues
+  #[allow(dead_code)]
+  pub fn spawn_into<T: 'static>(
+    ft: impl Future<Output = T> + 'static,
+    tq: TaskQueueHandle,
+  ) -> Result<JoinHandle<T>, RedisError> {
+    let finished = Rc::new(RefCell::new(false));
+    let _finished = finished.clone();
+    let inner = glommio::spawn_local_into(
+      async move {
+        let result = ft.await;
+        _finished.replace(true);
+        result
+      },
+      tq,
+    )?
+    .detach();
+
+    Ok(JoinHandle { inner, finished })
+  }
+
+  impl<T> Future for JoinHandle<T> {
+    type Output = Result<T, RedisError>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+      use futures_lite::FutureExt;
+
+      let finished = self.finished.clone();
+      let result = self
+        .get_mut()
+        .inner
+        .poll(cx)
+        .map(|result| result.ok_or(RedisError::new_canceled()));
+
+      if let Poll::Ready(_) = result {
+        finished.replace(true);
+      }
+      result
+    }
+  }
+
+  impl<T> JoinHandle<T> {
+    pub(crate) fn set_finished(&self) {
+      self.finished.replace(true);
+    }
+
+    pub fn is_finished(&self) -> bool {
+      *self.finished.as_ref().borrow()
+    }
+
+    pub fn abort(&self) {
+      self.inner.cancel();
+      self.set_finished();
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/glommio/mpsc.rs.html b/doc/src/fred/glommio/mpsc.rs.html new file mode 100644 index 00000000..57d17e93 --- /dev/null +++ b/doc/src/fred/glommio/mpsc.rs.html @@ -0,0 +1,167 @@ +mpsc.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+
use crate::error::{RedisError, RedisErrorKind};
+use futures::Stream;
+use glommio::{
+  channels::local_channel::{LocalReceiver, LocalSender},
+  GlommioError,
+};
+use std::{
+  ops::Deref,
+  pin::Pin,
+  rc::Rc,
+  task::{Context, Poll},
+};
+
+pub type UnboundedReceiver<T> = LocalReceiver<T>;
+
+pub struct UnboundedReceiverStream<T> {
+  rx: LocalReceiver<T>,
+}
+
+impl<T> From<LocalReceiver<T>> for UnboundedReceiverStream<T> {
+  fn from(rx: LocalReceiver<T>) -> Self {
+    UnboundedReceiverStream { rx }
+  }
+}
+
+impl<T> UnboundedReceiverStream<T> {
+  #[allow(dead_code)]
+  pub async fn recv(&mut self) -> Option<T> {
+    self.rx.recv().await
+  }
+}
+
+impl<T> Stream for UnboundedReceiverStream<T> {
+  type Item = T;
+
+  fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+    use futures_lite::stream::StreamExt;
+
+    // TODO make sure this is cancellation-safe. it's a bit unclear why the internal impl of ChannelStream does what
+    // it does.
+    self.rx.stream().poll_next(cx)
+  }
+}
+
+pub struct UnboundedSender<T> {
+  tx: Rc<LocalSender<T>>,
+}
+
+// https://github.com/rust-lang/rust/issues/26925
+impl<T> Clone for UnboundedSender<T> {
+  fn clone(&self) -> Self {
+    UnboundedSender { tx: self.tx.clone() }
+  }
+}
+
+impl<T> From<LocalSender<T>> for UnboundedSender<T> {
+  fn from(tx: LocalSender<T>) -> Self {
+    UnboundedSender { tx: Rc::new(tx) }
+  }
+}
+
+impl<T> UnboundedSender<T> {
+  pub fn try_send(&self, msg: T) -> Result<(), GlommioError<T>> {
+    self.tx.try_send(msg)
+  }
+
+  pub fn send(&self, msg: T) -> Result<(), RedisError> {
+    if let Err(_e) = self.tx.deref().try_send(msg) {
+      // shouldn't happen since we use unbounded channels
+      Err(RedisError::new(
+        RedisErrorKind::Canceled,
+        "Failed to send message on channel.",
+      ))
+    } else {
+      Ok(())
+    }
+  }
+}
+
+pub fn rx_stream<T: 'static>(rx: LocalReceiver<T>) -> impl Stream<Item = T> + 'static {
+  // what happens if we `join` the futures from `recv()` and `rx.stream().next()`?
+  UnboundedReceiverStream::from(rx)
+}
+
\ No newline at end of file diff --git a/doc/src/fred/glommio/sync.rs.html b/doc/src/fred/glommio/sync.rs.html new file mode 100644 index 00000000..b9e73094 --- /dev/null +++ b/doc/src/fred/glommio/sync.rs.html @@ -0,0 +1,327 @@ +sync.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+
use std::{
+  cell::{Ref, RefCell, RefMut},
+  fmt,
+  mem,
+  sync::atomic::Ordering,
+};
+
+pub struct RefSwap<T> {
+  inner: RefCell<T>,
+}
+
+impl<T> RefSwap<T> {
+  pub fn new(val: T) -> Self {
+    RefSwap {
+      inner: RefCell::new(val),
+    }
+  }
+
+  pub fn swap(&self, other: T) -> T {
+    mem::replace(&mut self.inner.borrow_mut(), other)
+  }
+
+  pub fn store(&self, other: T) {
+    self.swap(other);
+  }
+
+  pub fn load(&self) -> Ref<'_, T> {
+    self.inner.borrow()
+  }
+}
+
+pub struct AsyncRwLock<T> {
+  inner: glommio::sync::RwLock<T>,
+}
+
+impl<T> AsyncRwLock<T> {
+  pub fn new(val: T) -> Self {
+    AsyncRwLock {
+      inner: glommio::sync::RwLock::new(val),
+    }
+  }
+
+  pub async fn write(&self) -> glommio::sync::RwLockWriteGuard<T> {
+    self.inner.write().await.unwrap()
+  }
+}
+
+#[derive(Debug)]
+pub struct AtomicUsize {
+  inner: RefCell<usize>,
+}
+
+impl AtomicUsize {
+  pub fn new(val: usize) -> Self {
+    AtomicUsize {
+      inner: RefCell::new(val),
+    }
+  }
+
+  pub fn fetch_add(&self, val: usize, _: Ordering) -> usize {
+    let mut guard = self.inner.borrow_mut();
+
+    let new = guard.saturating_add(val);
+    *guard = new;
+    new
+  }
+
+  pub fn fetch_sub(&self, val: usize, _: Ordering) -> usize {
+    let mut guard = self.inner.borrow_mut();
+
+    let new = guard.saturating_sub(val);
+    *guard = new;
+    new
+  }
+
+  pub fn load(&self, _: Ordering) -> usize {
+    *self.inner.borrow()
+  }
+
+  pub fn swap(&self, val: usize, _: Ordering) -> usize {
+    let mut guard = self.inner.borrow_mut();
+    let old = *guard;
+    *guard = val;
+    old
+  }
+}
+
+#[derive(Debug)]
+pub struct AtomicBool {
+  inner: RefCell<bool>,
+}
+
+impl AtomicBool {
+  pub fn new(val: bool) -> Self {
+    AtomicBool {
+      inner: RefCell::new(val),
+    }
+  }
+
+  pub fn load(&self, _: Ordering) -> bool {
+    *self.inner.borrow()
+  }
+
+  pub fn swap(&self, val: bool, _: Ordering) -> bool {
+    let mut guard = self.inner.borrow_mut();
+    let old = *guard;
+    *guard = val;
+    old
+  }
+}
+
+pub type MutexGuard<'a, T> = RefMut<'a, T>;
+
+pub struct Mutex<T> {
+  inner: RefCell<T>,
+}
+
+impl<T: fmt::Debug> fmt::Debug for Mutex<T> {
+  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    write!(f, "{:?}", self.inner)
+  }
+}
+
+impl<T> Mutex<T> {
+  pub fn new(val: T) -> Self {
+    Mutex {
+      inner: RefCell::new(val),
+    }
+  }
+
+  pub fn lock(&self) -> MutexGuard<T> {
+    self.inner.borrow_mut()
+  }
+}
+
+pub type RwLockReadGuard<'a, T> = Ref<'a, T>;
+pub type RwLockWriteGuard<'a, T> = RefMut<'a, T>;
+
+pub struct RwLock<T> {
+  inner: RefCell<T>,
+}
+
+impl<T: fmt::Debug> fmt::Debug for RwLock<T> {
+  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    write!(f, "{:?}", self.inner)
+  }
+}
+
+impl<T> RwLock<T> {
+  pub fn new(val: T) -> Self {
+    RwLock {
+      inner: RefCell::new(val),
+    }
+  }
+
+  pub fn read(&self) -> RwLockReadGuard<T> {
+    self.inner.borrow()
+  }
+
+  pub fn write(&self) -> RwLockWriteGuard<T> {
+    self.inner.borrow_mut()
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/interfaces.rs.html b/doc/src/fred/interfaces.rs.html new file mode 100644 index 00000000..7ecfcbeb --- /dev/null +++ b/doc/src/fred/interfaces.rs.html @@ -0,0 +1,807 @@ +interfaces.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+
use crate::{
+  commands,
+  error::{RedisError, RedisErrorKind},
+  modules::inner::RedisClientInner,
+  protocol::command::{RedisCommand, RouterCommand},
+  runtime::{sleep, spawn, BroadcastReceiver, JoinHandle, RefCount},
+  types::{ClientState, ClusterStateChange, KeyspaceEvent, Message, RespVersion, Server},
+  utils,
+};
+use bytes_utils::Str;
+use futures::Future;
+pub use redis_protocol::resp3::types::BytesFrame as Resp3Frame;
+use rm_send_macros::rm_send_if;
+use std::time::Duration;
+
+/// Type alias for `Result<T, RedisError>`.
+pub type RedisResult<T> = Result<T, RedisError>;
+
+/// Send a single `RedisCommand` to the router.
+pub(crate) fn default_send_command<C>(inner: &RefCount<RedisClientInner>, command: C) -> Result<(), RedisError>
+where
+  C: Into<RedisCommand>,
+{
+  let mut command: RedisCommand = command.into();
+  _trace!(
+    inner,
+    "Sending command {} ({}) to router.",
+    command.kind.to_str_debug(),
+    command.debug_id()
+  );
+  command.inherit_options(inner);
+
+  send_to_router(inner, command.into())
+}
+
+/// Send a `RouterCommand` to the router.
+pub(crate) fn send_to_router(inner: &RefCount<RedisClientInner>, command: RouterCommand) -> Result<(), RedisError> {
+  #[allow(clippy::collapsible_if)]
+  if command.should_check_fail_fast() {
+    if utils::read_locked(&inner.state) != ClientState::Connected {
+      _debug!(inner, "Responding early after fail fast check.");
+      command.finish_with_error(RedisError::new(
+        RedisErrorKind::Canceled,
+        "Connection closed unexpectedly.",
+      ));
+      return Ok(());
+    }
+  }
+
+  let new_len = inner.counters.incr_cmd_buffer_len();
+  let should_apply_backpressure = inner.connection.max_command_buffer_len > 0
+    && new_len > inner.connection.max_command_buffer_len
+    && !command.should_skip_backpressure();
+
+  if should_apply_backpressure {
+    inner.counters.decr_cmd_buffer_len();
+    command.finish_with_error(RedisError::new(
+      RedisErrorKind::Backpressure,
+      "Max command queue length exceeded.",
+    ));
+    return Ok(());
+  }
+
+  if let Err(e) = inner.send_command(command) {
+    // usually happens if the caller tries to send a command before calling `connect` or after calling `quit`
+    inner.counters.decr_cmd_buffer_len();
+
+    if let RouterCommand::Command(mut command) = e {
+      _warn!(
+        inner,
+        "Fatal error sending {} command to router. Client may be stopped or not yet initialized.",
+        command.kind.to_str_debug()
+      );
+
+      command.respond_to_caller(Err(RedisError::new(
+        RedisErrorKind::Unknown,
+        "Client is not initialized.",
+      )));
+    } else {
+      _warn!(
+        inner,
+        "Fatal error sending command to router. Client may be stopped or not yet initialized."
+      );
+    }
+
+    Err(RedisError::new(
+      RedisErrorKind::Unknown,
+      "Failed to send command to router.",
+    ))
+  } else {
+    Ok(())
+  }
+}
+
+#[cfg(not(feature = "glommio"))]
+pub use crate::_tokio::ClientLike;
+#[cfg(feature = "glommio")]
+pub use crate::glommio::interfaces::ClientLike;
+
+#[cfg(not(feature = "glommio"))]
+pub use crate::_tokio::spawn_event_listener;
+#[cfg(feature = "glommio")]
+pub use crate::glommio::interfaces::spawn_event_listener;
+
+/// Functions that provide a connection heartbeat interface.
+#[rm_send_if(feature = "glommio")]
+pub trait HeartbeatInterface: ClientLike {
+  /// Return a future that will ping the server on an interval.
+  #[allow(unreachable_code)]
+  fn enable_heartbeat(
+    &self,
+    interval: Duration,
+    break_on_error: bool,
+  ) -> impl Future<Output = RedisResult<()>> + Send {
+    async move {
+      let _self = self.clone();
+
+      loop {
+        sleep(interval).await;
+
+        if break_on_error {
+          let _: () = _self.ping().await?;
+        } else if let Err(e) = _self.ping::<()>().await {
+          warn!("{}: Heartbeat ping failed with error: {:?}", _self.inner().id, e);
+        }
+      }
+
+      Ok(())
+    }
+  }
+}
+
+/// Functions for authenticating clients.
+#[rm_send_if(feature = "glommio")]
+pub trait AuthInterface: ClientLike {
+  /// Request for authentication in a password-protected Redis server. Returns ok if successful.
+  ///
+  /// The client will automatically authenticate with the default user if a password is provided in the associated
+  /// `RedisConfig` when calling [connect](crate::interfaces::ClientLike::connect).
+  ///
+  /// If running against clustered servers this function will authenticate all connections.
+  ///
+  /// <https://redis.io/commands/auth>
+  fn auth<S>(&self, username: Option<String>, password: S) -> impl Future<Output = RedisResult<()>> + Send
+  where
+    S: Into<Str> + Send,
+  {
+    async move {
+      into!(password);
+      commands::server::auth(self, username, password).await
+    }
+  }
+
+  /// Switch to a different protocol, optionally authenticating in the process.
+  ///
+  /// If running against clustered servers this function will issue the HELLO command to each server concurrently.
+  ///
+  /// <https://redis.io/commands/hello>
+  fn hello(
+    &self,
+    version: RespVersion,
+    auth: Option<(Str, Str)>,
+    setname: Option<Str>,
+  ) -> impl Future<Output = RedisResult<()>> + Send {
+    async move { commands::server::hello(self, version, auth, setname).await }
+  }
+}
+
+/// An interface that exposes various client and connection events.
+///
+/// Calling [quit](crate::interfaces::ClientLike::quit) will close all event streams.
+#[rm_send_if(feature = "glommio")]
+pub trait EventInterface: ClientLike {
+  /// Spawn a task that runs the provided function on each publish-subscribe message.
+  ///
+  /// See [message_rx](Self::message_rx) for more information.
+  fn on_message<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
+  where
+    F: Fn(Message) -> RedisResult<()> + Send + 'static,
+  {
+    let rx = self.message_rx();
+    spawn_event_listener(rx, func)
+  }
+
+  /// Spawn a task that runs the provided function on each keyspace event.
+  ///
+  /// <https://redis.io/topics/notifications>
+  fn on_keyspace_event<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
+  where
+    F: Fn(KeyspaceEvent) -> RedisResult<()> + Send + 'static,
+  {
+    let rx = self.keyspace_event_rx();
+    spawn_event_listener(rx, func)
+  }
+
+  /// Spawn a task that runs the provided function on each reconnection event.
+  ///
+  /// Errors returned by `func` will exit the task.
+  fn on_reconnect<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
+  where
+    F: Fn(Server) -> RedisResult<()> + Send + 'static,
+  {
+    let rx = self.reconnect_rx();
+    spawn_event_listener(rx, func)
+  }
+
+  /// Spawn a task that runs the provided function on each cluster change event.
+  ///
+  /// Errors returned by `func` will exit the task.
+  fn on_cluster_change<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
+  where
+    F: Fn(Vec<ClusterStateChange>) -> RedisResult<()> + Send + 'static,
+  {
+    let rx = self.cluster_change_rx();
+    spawn_event_listener(rx, func)
+  }
+
+  /// Spawn a task that runs the provided function on each connection error event.
+  ///
+  /// Errors returned by `func` will exit the task.
+  fn on_error<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
+  where
+    F: Fn(RedisError) -> RedisResult<()> + Send + 'static,
+  {
+    let rx = self.error_rx();
+    spawn_event_listener(rx, func)
+  }
+
+  /// Spawn a task that runs the provided function whenever the client detects an unresponsive connection.
+  fn on_unresponsive<F>(&self, func: F) -> JoinHandle<RedisResult<()>>
+  where
+    F: Fn(Server) -> RedisResult<()> + Send + 'static,
+  {
+    let rx = self.unresponsive_rx();
+    spawn_event_listener(rx, func)
+  }
+
+  /// Spawn one task that listens for all connection management event types.
+  ///
+  /// Errors in any of the provided functions will exit the task.
+  fn on_any<Fe, Fr, Fc>(&self, error_fn: Fe, reconnect_fn: Fr, cluster_change_fn: Fc) -> JoinHandle<RedisResult<()>>
+  where
+    Fe: Fn(RedisError) -> RedisResult<()> + Send + 'static,
+    Fr: Fn(Server) -> RedisResult<()> + Send + 'static,
+    Fc: Fn(Vec<ClusterStateChange>) -> RedisResult<()> + Send + 'static,
+  {
+    let mut error_rx = self.error_rx();
+    let mut reconnect_rx = self.reconnect_rx();
+    let mut cluster_rx = self.cluster_change_rx();
+
+    spawn(async move {
+      #[allow(unused_assignments)]
+      let mut result = Ok(());
+
+      loop {
+        tokio::select! {
+          Ok(error) = error_rx.recv() => {
+            if let Err(err) = error_fn(error) {
+              result = Err(err);
+              break;
+            }
+          }
+          Ok(server) = reconnect_rx.recv() => {
+            if let Err(err) = reconnect_fn(server) {
+              result = Err(err);
+              break;
+            }
+          }
+          Ok(changes) = cluster_rx.recv() => {
+            if let Err(err) = cluster_change_fn(changes) {
+              result = Err(err);
+              break;
+            }
+          }
+        }
+      }
+
+      result
+    })
+  }
+
+  /// Listen for messages on the publish-subscribe interface.
+  ///
+  /// **Keyspace events are not sent on this interface.**
+  ///
+  /// If the connection to the Redis server closes for any reason this function does not need to be called again.
+  /// Messages will start appearing on the original stream after
+  /// [subscribe](crate::interfaces::PubsubInterface::subscribe) is called again.
+  fn message_rx(&self) -> BroadcastReceiver<Message> {
+    self.inner().notifications.pubsub.load().subscribe()
+  }
+
+  /// Listen for keyspace and keyevent notifications on the publish-subscribe interface.
+  ///
+  /// Callers still need to configure the server and subscribe to the relevant channels, but this interface will
+  /// parse and format the messages automatically.
+  ///
+  /// <https://redis.io/topics/notifications>
+  fn keyspace_event_rx(&self) -> BroadcastReceiver<KeyspaceEvent> {
+    self.inner().notifications.keyspace.load().subscribe()
+  }
+
+  /// Listen for reconnection notifications.
+  ///
+  /// This function can be used to receive notifications whenever the client reconnects in order to
+  /// re-subscribe to channels, etc.
+  ///
+  /// A reconnection event is also triggered upon first connecting to the server.
+  fn reconnect_rx(&self) -> BroadcastReceiver<Server> {
+    self.inner().notifications.reconnect.load().subscribe()
+  }
+
+  /// Listen for notifications whenever the cluster state changes.
+  ///
+  /// This is usually triggered in response to a `MOVED` error, but can also happen when connections close
+  /// unexpectedly.
+  fn cluster_change_rx(&self) -> BroadcastReceiver<Vec<ClusterStateChange>> {
+    self.inner().notifications.cluster_change.load().subscribe()
+  }
+
+  /// Listen for protocol and connection errors. This stream can be used to more intelligently handle errors that may
+  /// not appear in the request-response cycle, and so cannot be handled by response futures.
+  fn error_rx(&self) -> BroadcastReceiver<RedisError> {
+    self.inner().notifications.errors.load().subscribe()
+  }
+
+  /// Receive a message when the client initiates a reconnection after detecting an unresponsive connection.
+  fn unresponsive_rx(&self) -> BroadcastReceiver<Server> {
+    self.inner().notifications.unresponsive.load().subscribe()
+  }
+}
+
+#[cfg(feature = "i-acl")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))]
+pub use crate::commands::interfaces::acl::*;
+#[cfg(feature = "i-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))]
+pub use crate::commands::interfaces::client::*;
+#[cfg(feature = "i-cluster")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))]
+pub use crate::commands::interfaces::cluster::*;
+#[cfg(feature = "i-config")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))]
+pub use crate::commands::interfaces::config::*;
+#[cfg(feature = "i-geo")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))]
+pub use crate::commands::interfaces::geo::*;
+#[cfg(feature = "i-hashes")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))]
+pub use crate::commands::interfaces::hashes::*;
+#[cfg(feature = "i-hyperloglog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))]
+pub use crate::commands::interfaces::hyperloglog::*;
+#[cfg(feature = "i-keys")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))]
+pub use crate::commands::interfaces::keys::*;
+#[cfg(feature = "i-lists")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))]
+pub use crate::commands::interfaces::lists::*;
+#[cfg(feature = "i-scripts")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+pub use crate::commands::interfaces::lua::*;
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+pub use crate::commands::interfaces::memory::*;
+#[cfg(feature = "i-pubsub")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))]
+pub use crate::commands::interfaces::pubsub::*;
+#[cfg(feature = "i-redis-json")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))]
+pub use crate::commands::interfaces::redis_json::RedisJsonInterface;
+#[cfg(feature = "i-redisearch")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))]
+pub use crate::commands::interfaces::redisearch::*;
+#[cfg(feature = "sentinel-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))]
+pub use crate::commands::interfaces::sentinel::SentinelInterface;
+#[cfg(feature = "i-server")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
+pub use crate::commands::interfaces::server::*;
+#[cfg(feature = "i-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))]
+pub use crate::commands::interfaces::sets::*;
+#[cfg(feature = "i-slowlog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))]
+pub use crate::commands::interfaces::slowlog::*;
+#[cfg(feature = "i-sorted-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))]
+pub use crate::commands::interfaces::sorted_sets::*;
+#[cfg(feature = "i-streams")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))]
+pub use crate::commands::interfaces::streams::*;
+#[cfg(feature = "i-time-series")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+pub use crate::commands::interfaces::timeseries::*;
+#[cfg(feature = "i-tracking")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+pub use crate::commands::interfaces::tracking::*;
+#[cfg(feature = "transactions")]
+#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))]
+pub use crate::commands::interfaces::transactions::*;
+
+pub use crate::commands::interfaces::metrics::MetricsInterface;
+
\ No newline at end of file diff --git a/doc/src/fred/lib.rs.html b/doc/src/fred/lib.rs.html new file mode 100644 index 00000000..17293714 --- /dev/null +++ b/doc/src/fred/lib.rs.html @@ -0,0 +1,387 @@ +lib.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+
#![allow(clippy::unnecessary_fallible_conversions)]
+#![allow(clippy::redundant_pattern_matching)]
+#![allow(clippy::mutable_key_type)]
+#![allow(clippy::derivable_impls)]
+#![allow(clippy::enum_variant_names)]
+#![allow(clippy::iter_kv_map)]
+#![allow(clippy::len_without_is_empty)]
+#![allow(clippy::vec_init_then_push)]
+#![allow(clippy::while_let_on_iterator)]
+#![allow(clippy::type_complexity)]
+#![allow(clippy::too_many_arguments)]
+#![allow(clippy::new_without_default)]
+#![allow(clippy::assigning_clones)]
+#![allow(clippy::manual_async_fn)]
+#![warn(clippy::large_types_passed_by_value)]
+#![warn(clippy::large_stack_frames)]
+#![warn(clippy::large_futures)]
+#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
+#![cfg_attr(docsrs, feature(doc_cfg))]
+#![cfg_attr(docsrs, allow(unused_attributes))]
+#![doc = include_str!("../README.md")]
+
+#[cfg(any(feature = "dns", feature = "replicas"))]
+#[macro_use]
+extern crate async_trait;
+
+#[macro_use]
+extern crate log;
+
+pub extern crate bytes;
+pub extern crate bytes_utils;
+#[cfg(feature = "enable-native-tls")]
+#[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))]
+pub extern crate native_tls;
+#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))]
+pub extern crate rustls;
+#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))]
+pub extern crate rustls_native_certs;
+#[cfg(feature = "serde-json")]
+pub extern crate serde_json;
+pub extern crate socket2;
+#[cfg(feature = "partial-tracing")]
+#[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))]
+pub extern crate tracing;
+#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))]
+extern crate tracing_futures;
+#[macro_use]
+mod macros;
+
+mod commands;
+mod modules;
+mod protocol;
+mod router;
+mod trace;
+mod utils;
+
+/// Redis client implementations.
+pub mod clients;
+/// Error structs returned by Redis commands.
+pub mod error;
+/// Traits that implement portions of the Redis interface.
+pub mod interfaces;
+#[cfg(feature = "mocks")]
+#[cfg_attr(docsrs, doc(cfg(feature = "mocks")))]
+pub use modules::mocks;
+/// An interface to run the `MONITOR` command.
+#[cfg(feature = "monitor")]
+#[cfg_attr(docsrs, doc(cfg(feature = "monitor")))]
+pub mod monitor;
+/// The structs and enums used by the Redis client.
+pub mod types;
+
+#[cfg(feature = "glommio")]
+mod glommio;
+#[cfg(feature = "glommio")]
+pub(crate) use glommio::compat as runtime;
+
+#[cfg(not(feature = "glommio"))]
+mod _tokio;
+#[cfg(not(feature = "glommio"))]
+pub(crate) use _tokio as runtime;
+
+/// Various client utility functions.
+pub mod util {
+  pub use crate::utils::{f64_to_redis_string, redis_string_to_f64, static_bytes, static_str};
+  use crate::{error::RedisError, types::RedisKey};
+  pub use redis_protocol::redis_keyslot;
+  use std::collections::{BTreeMap, VecDeque};
+
+  /// A convenience constant for `None` values used as generic arguments.
+  ///
+  /// Functions that take `Option<T>` as an argument often require the caller to use a turbofish when the
+  /// variant is `None`. In many cases this constant can be used instead.
+  // pretty much everything in this crate supports From<String>
+  pub const NONE: Option<String> = None;
+
+  /// Calculate the SHA1 hash output as a hex string. This is provided for clients that use the Lua interface to
+  /// manage their own script caches.
+  #[cfg(feature = "sha-1")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "sha-1")))]
+  pub fn sha1_hash(input: &str) -> String {
+    use sha1::Digest;
+
+    let mut hasher = sha1::Sha1::new();
+    hasher.update(input.as_bytes());
+    format!("{:x}", hasher.finalize())
+  }
+
+  /// Group the provided arguments by their cluster hash slot.
+  ///
+  /// This can be useful with commands that require all keys map to the same hash slot, such as `SSUBSCRIBE`,
+  /// `MGET`, etc.
+  ///
+  /// ```rust
+  /// # use fred::prelude::*;
+  /// async fn example(client: impl KeysInterface) -> Result<(), RedisError> {
+  ///   let keys = vec!["foo", "bar", "baz", "a{1}", "b{1}", "c{1}"];
+  ///   let groups = fred::util::group_by_hash_slot(keys)?;
+  ///
+  ///   for (slot, keys) in groups.into_iter() {
+  ///     // `MGET` requires that all arguments map to the same hash slot
+  ///     println!("{:?}", client.mget::<Vec<String>, _>(keys).await?);
+  ///   }
+  ///   Ok(())
+  /// }
+  /// ```
+  pub fn group_by_hash_slot<T>(
+    args: impl IntoIterator<Item = T>,
+  ) -> Result<BTreeMap<u16, VecDeque<RedisKey>>, RedisError>
+  where
+    T: TryInto<RedisKey>,
+    T::Error: Into<RedisError>,
+  {
+    let mut out = BTreeMap::new();
+
+    for arg in args.into_iter() {
+      let arg: RedisKey = to!(arg)?;
+      let slot = redis_keyslot(arg.as_bytes());
+
+      out.entry(slot).or_insert(VecDeque::new()).push_back(arg);
+    }
+    Ok(out)
+  }
+}
+
+/// Convenience module to import a `RedisClient`, all possible interfaces, error types, and common argument types or
+/// return value types.
+pub mod prelude {
+  #[cfg(feature = "partial-tracing")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))]
+  pub use crate::types::TracingConfig;
+
+  pub use crate::{
+    clients::{RedisClient, RedisPool},
+    error::{RedisError, RedisErrorKind},
+    interfaces::*,
+    types::{
+      Blocking,
+      Builder,
+      ConnectionConfig,
+      Expiration,
+      FromRedis,
+      Options,
+      PerformanceConfig,
+      ReconnectPolicy,
+      RedisConfig,
+      RedisKey,
+      RedisValue,
+      RedisValueKind,
+      Server,
+      ServerConfig,
+      SetOptions,
+      TcpConfig,
+    },
+  };
+
+  #[cfg(any(
+    feature = "enable-native-tls",
+    feature = "enable-rustls",
+    feature = "enable-rustls-ring"
+  ))]
+  #[cfg_attr(
+    docsrs,
+    doc(cfg(any(
+      feature = "enable-rustls",
+      feature = "enable-native-tls",
+      feature = "enable-rustls-ring"
+    )))
+  )]
+  pub use crate::types::{TlsConfig, TlsConnector};
+}
+
\ No newline at end of file diff --git a/doc/src/fred/macros.rs.html b/doc/src/fred/macros.rs.html new file mode 100644 index 00000000..1dde83fe --- /dev/null +++ b/doc/src/fred/macros.rs.html @@ -0,0 +1,331 @@ +macros.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+
#![allow(unused_macros)]
+
+macro_rules! to(
+  ($val:ident) => {
+    crate::utils::try_into($val)
+  }
+);
+
+macro_rules! _trace(
+  ($inner:tt, $($arg:tt)*) => { {
+    if log::log_enabled!(log::Level::Trace) {
+      log::trace!("{}: {}", $inner.id, format!($($arg)*))
+    }
+   } }
+);
+
+macro_rules! _debug(
+  ($inner:tt, $($arg:tt)*) => { {
+    if log::log_enabled!(log::Level::Debug) {
+      log::debug!("{}: {}", $inner.id, format!($($arg)*))
+    }
+   } }
+);
+
+macro_rules! _error(
+  ($inner:tt, $($arg:tt)*) => { {
+    if log::log_enabled!(log::Level::Error) {
+      log::error!("{}: {}", $inner.id, format!($($arg)*))
+    }
+   } }
+);
+
+macro_rules! _warn(
+  ($inner:tt, $($arg:tt)*) => { {
+    if log::log_enabled!(log::Level::Warn) {
+      log::warn!("{}: {}", $inner.id, format!($($arg)*))
+    }
+   } }
+);
+
+macro_rules! _info(
+  ($inner:tt, $($arg:tt)*) => { {
+    if log::log_enabled!(log::Level::Info) {
+      log::info!("{}: {}", $inner.id, format!($($arg)*))
+    }
+   } }
+);
+
+/// Span used within the client that uses the command's span ID as the parent.
+#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))]
+macro_rules! fspan (
+  ($cmd:ident, $lvl:expr, $($arg:tt)*) => { {
+    let _id = $cmd.traces.cmd.as_ref().and_then(|c| c.id());
+    span_lvl!($lvl, parent: _id, $($arg)*)
+  } }
+);
+
+macro_rules! span_lvl {
+    ($lvl:expr, $($args:tt)*) => {{
+        match $lvl {
+            tracing::Level::ERROR => tracing::error_span!($($args)*),
+            tracing::Level::WARN => tracing::warn_span!($($args)*),
+            tracing::Level::INFO => tracing::info_span!($($args)*),
+            tracing::Level::DEBUG => tracing::debug_span!($($args)*),
+            tracing::Level::TRACE => tracing::trace_span!($($args)*),
+        }
+    }};
+}
+
+/// Fake span used within the client that uses the command's span ID as the parent.
+#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))]
+macro_rules! fspan (
+  ($cmd:ident, $($arg:tt)*) => {
+    crate::trace::Span {}
+  }
+);
+
+/// Similar to `try`/`?`, but `continue` instead of breaking out with an error.  
+macro_rules! try_or_continue (
+  ($expr:expr) => {
+    match $expr {
+      Ok(val) => val,
+      Err(_) => continue
+    }
+  }
+);
+
+/// A helper macro to wrap a string value in quotes via the [json](serde_json::json) macro.
+///
+/// See the [RedisJSON interface](crate::interfaces::RedisJsonInterface) for more information.
+#[cfg(feature = "i-redis-json")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))]
+#[macro_export]
+macro_rules! json_quote(
+  ($($json:tt)+) => {
+    serde_json::json!($($json)+).to_string()
+  }
+);
+
+/// Shorthand to create a [CustomCommand](crate::types::CustomCommand).
+///
+/// ```rust no_run
+/// # use fred::{cmd, types::{CustomCommand, ClusterHash}};
+/// let _cmd = cmd!("FOO.BAR");
+/// let _cmd = cmd!("FOO.BAR", blocking: true);
+/// let _cmd = cmd!("FOO.BAR", hash: ClusterHash::FirstKey);
+/// let _cmd = cmd!("FOO.BAR", hash: ClusterHash::FirstKey, blocking: true);
+/// // which is shorthand for
+/// let _cmd = CustomCommand::new("FOO.BAR", ClusterHash::FirstKey, true);
+/// ```
+#[macro_export]
+macro_rules! cmd(
+  ($name:expr) => {
+    fred::types::CustomCommand::new($name, fred::types::ClusterHash::FirstKey, false)
+  };
+  ($name:expr, blocking: $blk:expr) => {
+    fred::types::CustomCommand::new($name, fred::types::ClusterHash::FirstKey, $blk)
+  };
+  ($name:expr, hash: $hash:expr) => {
+    fred::types::CustomCommand::new($name, $hash, false)
+  };
+  ($name:expr, hash: $hash:expr, blocking: $blk:expr) => {
+    fred::types::CustomCommand::new($name, $hash, $blk)
+  };
+);
+
+macro_rules! static_val(
+  ($val:expr) => {
+    RedisValue::from_static_str($val)
+  }
+);
+
+macro_rules! into (
+  ($val:ident) => (let $val = $val.into(););
+  ($v1:ident, $v2:ident) => (
+    let ($v1, $v2) = ($v1.into(), $v2.into());
+  );
+  ($v1:ident, $v2:ident, $v3:ident) => (
+    let ($v1, $v2, $v3) = ($v1.into(), $v2.into(), $v3.into());
+  );
+  ($v1:ident, $v2:ident, $v3:ident, $v4:ident) => (
+    let ($v1, $v2, $v3, $v4) = ($v1.into(), $v2.into(), $v3.into(), $v4.into());
+  );
+  ($v1:ident, $v2:ident, $v3:ident, $v4:ident, $v5:ident) => (
+    let ($v1, $v2, $v3, $v4, $v5) = ($v1.into(), $v2.into(), $v3.into(), $v4.into(), $v5.into());
+  );
+  // add to this as needed
+);
+
+macro_rules! try_into (
+  ($val:ident) => (let $val = to!($val)?;);
+  ($v1:ident, $v2:ident) => (
+    let ($v1, $v2) = (to!($v1)?, to!($v2)?);
+  );
+  ($v1:ident, $v2:ident, $v3:ident) => (
+    let ($v1, $v2, $v3) = (to!($v1)?, to!($v2)?, to!($v3)?);
+  );
+  ($v1:ident, $v2:ident, $v3:ident, $v4:ident) => (
+    let ($v1, $v2, $v3, $v4) = (to!($v1)?, to!($v2)?, to!($v3)?, to!($v4)?);
+  );
+  ($v1:ident, $v2:ident, $v3:ident, $v4:ident, $v5:ident) => (
+    let ($v1, $v2, $v3, $v4, $v5) = (to!($v1)?, to!($v2)?, to!($v3)?, to!($v4)?, to!($v5)?);
+  );
+  // add to this as needed
+);
+
\ No newline at end of file diff --git a/doc/src/fred/modules/backchannel.rs.html b/doc/src/fred/modules/backchannel.rs.html new file mode 100644 index 00000000..d2cb3f2c --- /dev/null +++ b/doc/src/fred/modules/backchannel.rs.html @@ -0,0 +1,463 @@ +backchannel.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  modules::inner::RedisClientInner,
+  protocol::{command::RedisCommand, connection, connection::RedisTransport, types::Server},
+  router::Connections,
+  runtime::RefCount,
+  utils,
+};
+use redis_protocol::resp3::types::BytesFrame as Resp3Frame;
+use std::collections::HashMap;
+
+/// Check if an existing connection can be used to the provided `server`, otherwise create a new one.
+///
+/// Returns whether a new connection was created.
+async fn check_and_create_transport(
+  backchannel: &mut Backchannel,
+  inner: &RefCount<RedisClientInner>,
+  server: &Server,
+) -> Result<bool, RedisError> {
+  if let Some(ref mut transport) = backchannel.transport {
+    if &transport.server == server && transport.ping(inner).await.is_ok() {
+      _debug!(inner, "Using existing backchannel connection to {}", server);
+      return Ok(false);
+    }
+  }
+  backchannel.transport = None;
+
+  let mut transport = connection::create(inner, server, None).await?;
+  transport.setup(inner, None).await?;
+  backchannel.transport = Some(transport);
+
+  Ok(true)
+}
+
+/// A struct wrapping a separate connection to the server or cluster for client or cluster management commands.
+#[derive(Default)]
+pub struct Backchannel {
+  /// A connection to any of the servers.
+  pub transport:      Option<RedisTransport>,
+  /// An identifier for the blocked connection, if any.
+  pub blocked:        Option<Server>,
+  /// A map of server IDs to connection IDs, as managed by the router.
+  pub connection_ids: HashMap<Server, i64>,
+}
+
+impl Backchannel {
+  /// Check if the current server matches the provided server, and disconnect.
+  // TODO does this need to disconnect whenever the caller manually changes the RESP protocol mode?
+  pub async fn check_and_disconnect(&mut self, inner: &RefCount<RedisClientInner>, server: Option<&Server>) {
+    let should_close = self
+      .current_server()
+      .map(|current| server.map(|server| *server == current).unwrap_or(true))
+      .unwrap_or(false);
+
+    if should_close {
+      if let Some(ref mut transport) = self.transport {
+        let _ = transport.disconnect(inner).await;
+      }
+      self.transport = None;
+    }
+  }
+
+  /// Clear all local state that depends on the associated `Router` instance.
+  pub async fn clear_router_state(&mut self, inner: &RefCount<RedisClientInner>) {
+    self.connection_ids.clear();
+    self.blocked = None;
+
+    if let Some(ref mut transport) = self.transport {
+      let _ = transport.disconnect(inner).await;
+    }
+    self.transport = None;
+  }
+
+  /// Set the connection IDs from the router.
+  pub fn update_connection_ids(&mut self, connections: &Connections) {
+    self.connection_ids = connections.connection_ids();
+  }
+
+  /// Read the connection ID for the provided server.
+  pub fn connection_id(&self, server: &Server) -> Option<i64> {
+    self.connection_ids.get(server).cloned()
+  }
+
+  /// Set the blocked flag to the provided server.
+  pub fn set_blocked(&mut self, server: &Server) {
+    self.blocked = Some(server.clone());
+  }
+
+  /// Remove the blocked flag.
+  pub fn set_unblocked(&mut self) {
+    self.blocked = None;
+  }
+
+  /// Remove the blocked flag only if the server matches the blocked server.
+  pub fn check_and_set_unblocked(&mut self, server: &Server) {
+    let should_remove = self.blocked.as_ref().map(|blocked| blocked == server).unwrap_or(false);
+    if should_remove {
+      self.set_unblocked();
+    }
+  }
+
+  /// Whether the client is blocked on a command.
+  pub fn is_blocked(&self) -> bool {
+    self.blocked.is_some()
+  }
+
+  /// Whether an open connection exists to the blocked server.
+  pub fn has_blocked_transport(&self) -> bool {
+    match self.blocked {
+      Some(ref server) => match self.transport {
+        Some(ref transport) => &transport.server == server,
+        None => false,
+      },
+      None => false,
+    }
+  }
+
+  /// Return the server ID of the blocked client connection, if found.
+  pub fn blocked_server(&self) -> Option<Server> {
+    self.blocked.clone()
+  }
+
+  /// Return the server ID of the existing backchannel connection, if found.
+  pub fn current_server(&self) -> Option<Server> {
+    self.transport.as_ref().map(|t| t.server.clone())
+  }
+
+  /// Return a server ID, with the following preferences:
+  ///
+  /// 1. The server ID of the existing connection, if any.
+  /// 2. The blocked server ID, if any.
+  /// 3. A random server ID from the router's connection map.
+  pub fn any_server(&self) -> Option<Server> {
+    self
+      .current_server()
+      .or(self.blocked_server())
+      .or(self.connection_ids.keys().next().cloned())
+  }
+
+  /// Whether the existing connection is to the currently blocked server.
+  pub fn current_server_is_blocked(&self) -> bool {
+    self
+      .current_server()
+      .and_then(|server| self.blocked_server().map(|blocked| server == blocked))
+      .unwrap_or(false)
+  }
+
+  /// Send the provided command to the provided server, creating a new connection if needed.
+  ///
+  /// If a new connection is created this function also sets it on `self` before returning.
+  pub async fn request_response(
+    &mut self,
+    inner: &RefCount<RedisClientInner>,
+    server: &Server,
+    command: RedisCommand,
+  ) -> Result<Resp3Frame, RedisError> {
+    let _ = check_and_create_transport(self, inner, server).await?;
+
+    if let Some(ref mut transport) = self.transport {
+      _debug!(
+        inner,
+        "Sending {} ({}) on backchannel to {}",
+        command.kind.to_str_debug(),
+        command.debug_id(),
+        server
+      );
+
+      utils::timeout(
+        transport.request_response(command, inner.is_resp3()),
+        inner.connection_timeout(),
+      )
+      .await
+    } else {
+      Err(RedisError::new(
+        RedisErrorKind::Unknown,
+        "Failed to create backchannel connection.",
+      ))
+    }
+  }
+
+  /// Find the server identifier that should receive the provided command.
+  ///
+  /// Servers are chosen with the following preference order:
+  ///
+  /// * If `use_blocked` is true and a connection is blocked then that server will be used.
+  /// * If the client is clustered and the command uses a hashing policy that specifies a specific server then that
+  ///   will be used.
+  /// * If a backchannel connection already exists then that will be used.
+  /// * Failing all of the above a random server will be used.
+  pub fn find_server(
+    &self,
+    inner: &RefCount<RedisClientInner>,
+    command: &RedisCommand,
+    use_blocked: bool,
+  ) -> Result<Server, RedisError> {
+    if use_blocked {
+      if let Some(server) = self.blocked.as_ref() {
+        Ok(server.clone())
+      } else {
+        // should this be more relaxed?
+        Err(RedisError::new(RedisErrorKind::Unknown, "No connections are blocked."))
+      }
+    } else if inner.config.server.is_clustered() {
+      if command.kind.use_random_cluster_node() {
+        self
+          .any_server()
+          .ok_or_else(|| RedisError::new(RedisErrorKind::Unknown, "Failed to find backchannel server."))
+      } else {
+        inner.with_cluster_state(|state| {
+          let slot = match command.cluster_hash() {
+            Some(slot) => slot,
+            None => {
+              return Err(RedisError::new(
+                RedisErrorKind::Cluster,
+                "Failed to find cluster hash slot.",
+              ))
+            },
+          };
+          state
+            .get_server(slot)
+            .cloned()
+            .ok_or_else(|| RedisError::new(RedisErrorKind::Cluster, "Failed to find cluster owner."))
+        })
+      }
+    } else {
+      self
+        .any_server()
+        .ok_or_else(|| RedisError::new(RedisErrorKind::Unknown, "Failed to find backchannel server."))
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/modules/inner.rs.html b/doc/src/fred/modules/inner.rs.html new file mode 100644 index 00000000..24e63b24 --- /dev/null +++ b/doc/src/fred/modules/inner.rs.html @@ -0,0 +1,1503 @@ +inner.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+
use crate::{
+  error::*,
+  interfaces,
+  modules::backchannel::Backchannel,
+  protocol::{
+    command::{ResponseSender, RouterCommand},
+    connection::RedisTransport,
+    types::{ClusterRouting, DefaultResolver, Resolve, Server},
+  },
+  runtime::{
+    broadcast_channel,
+    broadcast_send,
+    sleep,
+    unbounded_channel,
+    AsyncRwLock,
+    AtomicBool,
+    AtomicUsize,
+    BroadcastSender,
+    Mutex,
+    RefCount,
+    RefSwap,
+    RwLock,
+    UnboundedReceiver,
+    UnboundedSender,
+  },
+  types::*,
+  utils,
+};
+use bytes_utils::Str;
+use futures::future::{select, Either};
+use semver::Version;
+use std::{ops::DerefMut, time::Duration};
+
+#[cfg(feature = "metrics")]
+use crate::modules::metrics::MovingStats;
+#[cfg(feature = "replicas")]
+use std::collections::HashMap;
+
+pub type CommandSender = UnboundedSender<RouterCommand>;
+pub type CommandReceiver = UnboundedReceiver<RouterCommand>;
+
+#[cfg(feature = "i-tracking")]
+use crate::types::Invalidation;
+
+pub struct Notifications {
+  /// The client ID.
+  pub id:             Str,
+  /// A broadcast channel for the `on_error` interface.
+  pub errors:         RefSwap<RefCount<BroadcastSender<RedisError>>>,
+  /// A broadcast channel for the `on_message` interface.
+  pub pubsub:         RefSwap<RefCount<BroadcastSender<Message>>>,
+  /// A broadcast channel for the `on_keyspace_event` interface.
+  pub keyspace:       RefSwap<RefCount<BroadcastSender<KeyspaceEvent>>>,
+  /// A broadcast channel for the `on_reconnect` interface.
+  pub reconnect:      RefSwap<RefCount<BroadcastSender<Server>>>,
+  /// A broadcast channel for the `on_cluster_change` interface.
+  pub cluster_change: RefSwap<RefCount<BroadcastSender<Vec<ClusterStateChange>>>>,
+  /// A broadcast channel for the `on_connect` interface.
+  pub connect:        RefSwap<RefCount<BroadcastSender<Result<(), RedisError>>>>,
+  /// A channel for events that should close all client tasks with `Canceled` errors.
+  ///
+  /// Emitted when QUIT, SHUTDOWN, etc are called.
+  pub close:          BroadcastSender<()>,
+  /// A broadcast channel for the `on_invalidation` interface.
+  #[cfg(feature = "i-tracking")]
+  pub invalidations:  RefSwap<RefCount<BroadcastSender<Invalidation>>>,
+  /// A broadcast channel for notifying callers when servers go unresponsive.
+  pub unresponsive:   RefSwap<RefCount<BroadcastSender<Server>>>,
+}
+
+impl Notifications {
+  pub fn new(id: &Str, capacity: usize) -> Self {
+    Notifications {
+      id:                                           id.clone(),
+      close:                                        broadcast_channel(capacity).0,
+      errors:                                       RefSwap::new(RefCount::new(broadcast_channel(capacity).0)),
+      pubsub:                                       RefSwap::new(RefCount::new(broadcast_channel(capacity).0)),
+      keyspace:                                     RefSwap::new(RefCount::new(broadcast_channel(capacity).0)),
+      reconnect:                                    RefSwap::new(RefCount::new(broadcast_channel(capacity).0)),
+      cluster_change:                               RefSwap::new(RefCount::new(broadcast_channel(capacity).0)),
+      connect:                                      RefSwap::new(RefCount::new(broadcast_channel(capacity).0)),
+      #[cfg(feature = "i-tracking")]
+      invalidations:                                RefSwap::new(RefCount::new(broadcast_channel(capacity).0)),
+      unresponsive:                                 RefSwap::new(RefCount::new(broadcast_channel(capacity).0)),
+    }
+  }
+
+  /// Replace the senders that have public receivers, closing the receivers in the process.
+  pub fn close_public_receivers(&self, capacity: usize) {
+    utils::swap_new_broadcast_channel(&self.errors, capacity);
+    utils::swap_new_broadcast_channel(&self.pubsub, capacity);
+    utils::swap_new_broadcast_channel(&self.keyspace, capacity);
+    utils::swap_new_broadcast_channel(&self.reconnect, capacity);
+    utils::swap_new_broadcast_channel(&self.cluster_change, capacity);
+    utils::swap_new_broadcast_channel(&self.connect, capacity);
+    #[cfg(feature = "i-tracking")]
+    utils::swap_new_broadcast_channel(&self.invalidations, capacity);
+    utils::swap_new_broadcast_channel(&self.unresponsive, capacity);
+  }
+
+  pub fn broadcast_error(&self, error: RedisError) {
+    broadcast_send(self.errors.load().as_ref(), &error, |err| {
+      debug!("{}: No `on_error` listener. The error was: {err:?}", self.id);
+    });
+  }
+
+  pub fn broadcast_pubsub(&self, message: Message) {
+    broadcast_send(self.pubsub.load().as_ref(), &message, |_| {
+      debug!("{}: No `on_message` listeners.", self.id);
+    });
+  }
+
+  pub fn broadcast_keyspace(&self, event: KeyspaceEvent) {
+    broadcast_send(self.keyspace.load().as_ref(), &event, |_| {
+      debug!("{}: No `on_keyspace_event` listeners.", self.id);
+    });
+  }
+
+  pub fn broadcast_reconnect(&self, server: Server) {
+    broadcast_send(self.reconnect.load().as_ref(), &server, |_| {
+      debug!("{}: No `on_reconnect` listeners.", self.id);
+    });
+  }
+
+  pub fn broadcast_cluster_change(&self, changes: Vec<ClusterStateChange>) {
+    broadcast_send(self.cluster_change.load().as_ref(), &changes, |_| {
+      debug!("{}: No `on_cluster_change` listeners.", self.id);
+    });
+  }
+
+  pub fn broadcast_connect(&self, result: Result<(), RedisError>) {
+    broadcast_send(self.connect.load().as_ref(), &result, |_| {
+      debug!("{}: No `on_connect` listeners.", self.id);
+    });
+  }
+
+  /// Interrupt any tokio `sleep` calls.
+  //`RedisClientInner::wait_with_interrupt` hides the subscription part from callers.
+  pub fn broadcast_close(&self) {
+    broadcast_send(&self.close, &(), |_| {
+      debug!("{}: No `close` listeners.", self.id);
+    });
+  }
+
+  #[cfg(feature = "i-tracking")]
+  pub fn broadcast_invalidation(&self, msg: Invalidation) {
+    broadcast_send(self.invalidations.load().as_ref(), &msg, |_| {
+      debug!("{}: No `on_invalidation` listeners.", self.id);
+    });
+  }
+
+  pub fn broadcast_unresponsive(&self, server: Server) {
+    broadcast_send(self.unresponsive.load().as_ref(), &server, |_| {
+      debug!("{}: No unresponsive listeners", self.id);
+    });
+  }
+}
+
+#[derive(Clone)]
+pub struct ClientCounters {
+  pub cmd_buffer_len:   RefCount<AtomicUsize>,
+  pub redelivery_count: RefCount<AtomicUsize>,
+}
+
+impl Default for ClientCounters {
+  fn default() -> Self {
+    ClientCounters {
+      cmd_buffer_len:   RefCount::new(AtomicUsize::new(0)),
+      redelivery_count: RefCount::new(AtomicUsize::new(0)),
+    }
+  }
+}
+
+impl ClientCounters {
+  pub fn incr_cmd_buffer_len(&self) -> usize {
+    utils::incr_atomic(&self.cmd_buffer_len)
+  }
+
+  pub fn decr_cmd_buffer_len(&self) -> usize {
+    utils::decr_atomic(&self.cmd_buffer_len)
+  }
+
+  pub fn incr_redelivery_count(&self) -> usize {
+    utils::incr_atomic(&self.redelivery_count)
+  }
+
+  pub fn read_cmd_buffer_len(&self) -> usize {
+    utils::read_atomic(&self.cmd_buffer_len)
+  }
+
+  pub fn read_redelivery_count(&self) -> usize {
+    utils::read_atomic(&self.redelivery_count)
+  }
+
+  pub fn take_cmd_buffer_len(&self) -> usize {
+    utils::set_atomic(&self.cmd_buffer_len, 0)
+  }
+
+  pub fn take_redelivery_count(&self) -> usize {
+    utils::set_atomic(&self.redelivery_count, 0)
+  }
+
+  pub fn reset(&self) {
+    utils::set_atomic(&self.cmd_buffer_len, 0);
+    utils::set_atomic(&self.redelivery_count, 0);
+  }
+}
+
+/// Cached state related to the server(s).
+pub struct ServerState {
+  pub kind:     ServerKind,
+  #[cfg(feature = "replicas")]
+  pub replicas: HashMap<Server, Server>,
+}
+
+impl ServerState {
+  pub fn new(config: &RedisConfig) -> Self {
+    ServerState {
+      kind:                                  ServerKind::new(config),
+      #[cfg(feature = "replicas")]
+      replicas:                              HashMap::new(),
+    }
+  }
+
+  #[cfg(feature = "replicas")]
+  pub fn update_replicas(&mut self, map: HashMap<Server, Server>) {
+    self.replicas = map;
+  }
+}
+
+/// Added state associated with different server deployment types, synchronized by the router task.
+pub enum ServerKind {
+  Sentinel {
+    version:   Option<Version>,
+    /// An updated set of known sentinel nodes.
+    sentinels: Vec<Server>,
+    /// The server host/port resolved from the sentinel nodes, if known.
+    primary:   Option<Server>,
+  },
+  Cluster {
+    version: Option<Version>,
+    /// The cached cluster routing table.
+    cache:   Option<ClusterRouting>,
+  },
+  Centralized {
+    version: Option<Version>,
+  },
+}
+
+impl ServerKind {
+  /// Create a new, empty server state cache.
+  pub fn new(config: &RedisConfig) -> Self {
+    match config.server {
+      ServerConfig::Clustered { .. } => ServerKind::Cluster {
+        version: None,
+        cache:   None,
+      },
+      ServerConfig::Sentinel { ref hosts, .. } => ServerKind::Sentinel {
+        version:   None,
+        sentinels: hosts.clone(),
+        primary:   None,
+      },
+      ServerConfig::Centralized { .. } => ServerKind::Centralized { version: None },
+      #[cfg(feature = "unix-sockets")]
+      ServerConfig::Unix { .. } => ServerKind::Centralized { version: None },
+    }
+  }
+
+  pub fn set_server_version(&mut self, new_version: Version) {
+    match self {
+      ServerKind::Cluster { ref mut version, .. } => {
+        *version = Some(new_version);
+      },
+      ServerKind::Centralized { ref mut version, .. } => {
+        *version = Some(new_version);
+      },
+      ServerKind::Sentinel { ref mut version, .. } => {
+        *version = Some(new_version);
+      },
+    }
+  }
+
+  pub fn server_version(&self) -> Option<Version> {
+    match self {
+      ServerKind::Cluster { ref version, .. } => version.clone(),
+      ServerKind::Centralized { ref version, .. } => version.clone(),
+      ServerKind::Sentinel { ref version, .. } => version.clone(),
+    }
+  }
+
+  pub fn update_cluster_state(&mut self, state: Option<ClusterRouting>) {
+    if let ServerKind::Cluster { ref mut cache, .. } = *self {
+      *cache = state;
+    }
+  }
+
+  pub fn num_cluster_nodes(&self) -> usize {
+    if let ServerKind::Cluster { ref cache, .. } = *self {
+      cache
+        .as_ref()
+        .map(|state| state.unique_primary_nodes().len())
+        .unwrap_or(1)
+    } else {
+      1
+    }
+  }
+
+  pub fn with_cluster_state<F, R>(&self, func: F) -> Result<R, RedisError>
+  where
+    F: FnOnce(&ClusterRouting) -> Result<R, RedisError>,
+  {
+    if let ServerKind::Cluster { ref cache, .. } = *self {
+      if let Some(state) = cache.as_ref() {
+        func(state)
+      } else {
+        Err(RedisError::new(
+          RedisErrorKind::Cluster,
+          "Missing cluster routing state.",
+        ))
+      }
+    } else {
+      Err(RedisError::new(
+        RedisErrorKind::Cluster,
+        "Missing cluster routing state.",
+      ))
+    }
+  }
+
+  pub fn update_sentinel_primary(&mut self, server: &Server) {
+    if let ServerKind::Sentinel { ref mut primary, .. } = *self {
+      *primary = Some(server.clone());
+    }
+  }
+
+  pub fn sentinel_primary(&self) -> Option<Server> {
+    if let ServerKind::Sentinel { ref primary, .. } = *self {
+      primary.clone()
+    } else {
+      None
+    }
+  }
+
+  pub fn update_sentinel_nodes(&mut self, server: &Server, nodes: Vec<Server>) {
+    if let ServerKind::Sentinel {
+      ref mut sentinels,
+      ref mut primary,
+      ..
+    } = *self
+    {
+      *primary = Some(server.clone());
+      *sentinels = nodes;
+    }
+  }
+
+  pub fn read_sentinel_nodes(&self, config: &ServerConfig) -> Option<Vec<Server>> {
+    if let ServerKind::Sentinel { ref sentinels, .. } = *self {
+      if sentinels.is_empty() {
+        match config {
+          ServerConfig::Sentinel { ref hosts, .. } => Some(hosts.clone()),
+          _ => None,
+        }
+      } else {
+        Some(sentinels.clone())
+      }
+    } else {
+      None
+    }
+  }
+}
+
+// TODO make a config option for other defaults and extend this
+fn create_resolver(id: &Str) -> RefCount<dyn Resolve> {
+  RefCount::new(DefaultResolver::new(id))
+}
+
+pub struct RedisClientInner {
+  /// An internal lock used to sync certain select operations that should not run concurrently across tasks.
+  pub _lock:         Mutex<()>,
+  /// The client ID used for logging and the default `CLIENT SETNAME` value.
+  pub id:            Str,
+  /// Whether the client uses RESP3.
+  pub resp3:         RefCount<AtomicBool>,
+  /// The state of the underlying connection.
+  pub state:         RwLock<ClientState>,
+  /// Client configuration options.
+  pub config:        RefCount<RedisConfig>,
+  /// Connection configuration options.
+  pub connection:    RefCount<ConnectionConfig>,
+  /// Performance config options for the client.
+  pub performance:   RefSwap<RefCount<PerformanceConfig>>,
+  /// An optional reconnect policy.
+  pub policy:        RwLock<Option<ReconnectPolicy>>,
+  /// Notification channels for the event interfaces.
+  pub notifications: RefCount<Notifications>,
+  /// Shared counters.
+  pub counters:      ClientCounters,
+  /// The DNS resolver to use when establishing new connections.
+  pub resolver:      AsyncRwLock<RefCount<dyn Resolve>>,
+  /// A backchannel that can be used to control the router connections even while the connections are blocked.
+  pub backchannel:   RefCount<AsyncRwLock<Backchannel>>,
+  /// Server state cache for various deployment types.
+  pub server_state:  RwLock<ServerState>,
+
+  /// An mpsc sender for commands to the router.
+  pub command_tx: RefSwap<RefCount<CommandSender>>,
+  /// Temporary storage for the receiver half of the router command channel.
+  pub command_rx: RwLock<Option<CommandReceiver>>,
+
+  /// Command latency metrics.
+  #[cfg(feature = "metrics")]
+  pub latency_stats:         RwLock<MovingStats>,
+  /// Network latency metrics.
+  #[cfg(feature = "metrics")]
+  pub network_latency_stats: RwLock<MovingStats>,
+  /// Payload size metrics tracking for requests.
+  #[cfg(feature = "metrics")]
+  pub req_size_stats:        RefCount<RwLock<MovingStats>>,
+  /// Payload size metrics tracking for responses
+  #[cfg(feature = "metrics")]
+  pub res_size_stats:        RefCount<RwLock<MovingStats>>,
+}
+
+impl RedisClientInner {
+  pub fn new(
+    config: RedisConfig,
+    perf: PerformanceConfig,
+    connection: ConnectionConfig,
+    policy: Option<ReconnectPolicy>,
+  ) -> RefCount<RedisClientInner> {
+    let id = Str::from(format!("fred-{}", utils::random_string(10)));
+    let resolver = AsyncRwLock::new(create_resolver(&id));
+    let (command_tx, command_rx) = unbounded_channel();
+    let notifications = RefCount::new(Notifications::new(&id, perf.broadcast_channel_capacity));
+    let (config, policy) = (RefCount::new(config), RwLock::new(policy));
+    let performance = RefSwap::new(RefCount::new(perf));
+    let (counters, state) = (ClientCounters::default(), RwLock::new(ClientState::Disconnected));
+    let command_rx = RwLock::new(Some(command_rx));
+    let backchannel = RefCount::new(AsyncRwLock::new(Backchannel::default()));
+    let server_state = RwLock::new(ServerState::new(&config));
+    let resp3 = if config.version == RespVersion::RESP3 {
+      RefCount::new(AtomicBool::new(true))
+    } else {
+      RefCount::new(AtomicBool::new(false))
+    };
+    let connection = RefCount::new(connection);
+
+    #[cfg(feature = "glommio")]
+    let command_tx = command_tx.into();
+    let command_tx = RefSwap::new(RefCount::new(command_tx));
+
+    RefCount::new(RedisClientInner {
+      _lock: Mutex::new(()),
+      #[cfg(feature = "metrics")]
+      latency_stats: RwLock::new(MovingStats::default()),
+      #[cfg(feature = "metrics")]
+      network_latency_stats: RwLock::new(MovingStats::default()),
+      #[cfg(feature = "metrics")]
+      req_size_stats: RefCount::new(RwLock::new(MovingStats::default())),
+      #[cfg(feature = "metrics")]
+      res_size_stats: RefCount::new(RwLock::new(MovingStats::default())),
+
+      backchannel,
+      command_rx,
+      server_state,
+      command_tx,
+      state,
+      counters,
+      config,
+      performance,
+      policy,
+      resp3,
+      notifications,
+      resolver,
+      connection,
+      id,
+    })
+  }
+
+  pub fn is_pipelined(&self) -> bool {
+    self.performance.load().as_ref().auto_pipeline
+  }
+
+  #[cfg(feature = "replicas")]
+  pub fn ignore_replica_reconnect_errors(&self) -> bool {
+    self.connection.replica.ignore_reconnection_errors
+  }
+
+  #[cfg(not(feature = "replicas"))]
+  pub fn ignore_replica_reconnect_errors(&self) -> bool {
+    true
+  }
+
+  /// Swap the command channel sender, returning the old one.
+  pub fn swap_command_tx(&self, tx: CommandSender) -> RefCount<CommandSender> {
+    self.command_tx.swap(RefCount::new(tx))
+  }
+
+  /// Whether the client has the command channel receiver stored. If not then the caller can assume another
+  /// connection/router instance is using it.
+  pub fn has_command_rx(&self) -> bool {
+    self.command_rx.read().is_some()
+  }
+
+  pub fn reset_server_state(&self) {
+    #[cfg(feature = "replicas")]
+    self.server_state.write().replicas.clear()
+  }
+
+  pub fn shared_resp3(&self) -> RefCount<AtomicBool> {
+    self.resp3.clone()
+  }
+
+  pub fn log_client_name_fn<F>(&self, level: log::Level, func: F)
+  where
+    F: FnOnce(&str),
+  {
+    if log_enabled!(level) {
+      func(&self.id)
+    }
+  }
+
+  pub async fn set_resolver(&self, resolver: RefCount<dyn Resolve>) {
+    let mut guard = self.resolver.write().await;
+    *guard = resolver;
+  }
+
+  pub fn cluster_discovery_policy(&self) -> Option<&ClusterDiscoveryPolicy> {
+    match self.config.server {
+      ServerConfig::Clustered { ref policy, .. } => Some(policy),
+      _ => None,
+    }
+  }
+
+  pub async fn get_resolver(&self) -> RefCount<dyn Resolve> {
+    self.resolver.write().await.clone()
+  }
+
+  pub fn client_name(&self) -> &str {
+    &self.id
+  }
+
+  pub fn num_cluster_nodes(&self) -> usize {
+    self.server_state.read().kind.num_cluster_nodes()
+  }
+
+  pub fn with_cluster_state<F, R>(&self, func: F) -> Result<R, RedisError>
+  where
+    F: FnOnce(&ClusterRouting) -> Result<R, RedisError>,
+  {
+    self.server_state.read().kind.with_cluster_state(func)
+  }
+
+  pub fn with_perf_config<F, R>(&self, func: F) -> R
+  where
+    F: FnOnce(&PerformanceConfig) -> R,
+  {
+    let guard = self.performance.load();
+    func(guard.as_ref())
+  }
+
+  #[cfg(feature = "partial-tracing")]
+  pub fn should_trace(&self) -> bool {
+    self.config.tracing.enabled
+  }
+
+  #[cfg(feature = "partial-tracing")]
+  pub fn tracing_span_level(&self) -> tracing::Level {
+    self.config.tracing.default_tracing_level
+  }
+
+  #[cfg(feature = "full-tracing")]
+  pub fn full_tracing_span_level(&self) -> tracing::Level {
+    self.config.tracing.full_tracing_level
+  }
+
+  #[cfg(not(feature = "partial-tracing"))]
+  pub fn should_trace(&self) -> bool {
+    false
+  }
+
+  pub fn take_command_rx(&self) -> Option<CommandReceiver> {
+    self.command_rx.write().take()
+  }
+
+  pub fn store_command_rx(&self, rx: CommandReceiver, force: bool) {
+    let mut guard = self.command_rx.write();
+    if guard.is_none() || force {
+      *guard = Some(rx);
+    }
+  }
+
+  pub fn is_resp3(&self) -> bool {
+    utils::read_bool_atomic(&self.resp3)
+  }
+
+  pub fn switch_protocol_versions(&self, version: RespVersion) {
+    match version {
+      RespVersion::RESP3 => utils::set_bool_atomic(&self.resp3, true),
+      RespVersion::RESP2 => utils::set_bool_atomic(&self.resp3, false),
+    };
+  }
+
+  pub fn update_performance_config(&self, config: PerformanceConfig) {
+    self.performance.store(RefCount::new(config));
+  }
+
+  pub fn performance_config(&self) -> PerformanceConfig {
+    self.performance.load().as_ref().clone()
+  }
+
+  pub fn connection_config(&self) -> ConnectionConfig {
+    self.connection.as_ref().clone()
+  }
+
+  pub fn reconnect_policy(&self) -> Option<ReconnectPolicy> {
+    self.policy.read().as_ref().cloned()
+  }
+
+  pub fn reset_protocol_version(&self) {
+    let resp3 = match self.config.version {
+      RespVersion::RESP3 => true,
+      RespVersion::RESP2 => false,
+    };
+
+    utils::set_bool_atomic(&self.resp3, resp3);
+  }
+
+  pub fn max_command_attempts(&self) -> u32 {
+    self.connection.max_command_attempts
+  }
+
+  pub fn max_feed_count(&self) -> u64 {
+    self.performance.load().max_feed_count
+  }
+
+  pub fn default_command_timeout(&self) -> Duration {
+    self.performance.load().default_command_timeout
+  }
+
+  pub fn connection_timeout(&self) -> Duration {
+    self.connection.connection_timeout
+  }
+
+  pub fn internal_command_timeout(&self) -> Duration {
+    self.connection.internal_command_timeout
+  }
+
+  pub async fn set_blocked_server(&self, server: &Server) {
+    self.backchannel.write().await.set_blocked(server);
+  }
+
+  pub fn should_reconnect(&self) -> bool {
+    let has_policy = self
+      .policy
+      .read()
+      .as_ref()
+      .map(|policy| policy.should_reconnect())
+      .unwrap_or(false);
+
+    // do not attempt a reconnection if the client is intentionally disconnecting. the QUIT and SHUTDOWN commands set
+    // this flag.
+    let is_disconnecting = utils::read_locked(&self.state) == ClientState::Disconnecting;
+
+    debug!(
+      "{}: Checking reconnect state. Has policy: {}, Is intentionally disconnecting: {}",
+      self.id, has_policy, is_disconnecting,
+    );
+    has_policy && !is_disconnecting
+  }
+
+  pub fn send_reconnect(
+    self: &RefCount<RedisClientInner>,
+    server: Option<Server>,
+    force: bool,
+    tx: Option<ResponseSender>,
+  ) {
+    debug!("{}: Sending reconnect message to router for {:?}", self.id, server);
+
+    let cmd = RouterCommand::Reconnect {
+      server,
+      force,
+      tx,
+      #[cfg(feature = "replicas")]
+      replica: false,
+    };
+    if let Err(_) = interfaces::send_to_router(self, cmd) {
+      warn!("{}: Error sending reconnect command to router.", self.id);
+    }
+  }
+
+  #[cfg(feature = "replicas")]
+  pub fn send_replica_reconnect(self: &RefCount<RedisClientInner>, server: &Server) {
+    debug!(
+      "{}: Sending replica reconnect message to router for {:?}",
+      self.id, server
+    );
+
+    let cmd = RouterCommand::Reconnect {
+      server:  Some(server.clone()),
+      force:   false,
+      tx:      None,
+      replica: true,
+    };
+    if let Err(_) = interfaces::send_to_router(self, cmd) {
+      warn!("{}: Error sending reconnect command to router.", self.id);
+    }
+  }
+
+  pub fn reset_reconnection_attempts(&self) {
+    if let Some(policy) = self.policy.write().deref_mut() {
+      policy.reset_attempts();
+    }
+  }
+
+  pub fn should_cluster_sync(&self, error: &RedisError) -> bool {
+    self.config.server.is_clustered() && error.is_cluster()
+  }
+
+  pub async fn update_backchannel(&self, transport: RedisTransport) {
+    self.backchannel.write().await.transport = Some(transport);
+  }
+
+  pub async fn wait_with_interrupt(&self, duration: Duration) -> Result<(), RedisError> {
+    #[allow(unused_mut)]
+    let mut rx = self.notifications.close.subscribe();
+    debug!("{}: Sleeping for {} ms", self.id, duration.as_millis());
+    let (sleep_ft, recv_ft) = (sleep(duration), rx.recv());
+    tokio::pin!(sleep_ft);
+    tokio::pin!(recv_ft);
+
+    if let Either::Right((_, _)) = select(sleep_ft, recv_ft).await {
+      Err(RedisError::new(RedisErrorKind::Canceled, "Connection(s) closed."))
+    } else {
+      Ok(())
+    }
+  }
+
+  #[cfg(not(feature = "glommio"))]
+  pub fn send_command(&self, command: RouterCommand) -> Result<(), RouterCommand> {
+    self.command_tx.load().send(command).map_err(|e| e.0)
+  }
+
+  #[cfg(feature = "glommio")]
+  pub fn send_command(&self, command: RouterCommand) -> Result<(), RouterCommand> {
+    self.command_tx.load().try_send(command).map_err(|e| match e {
+      glommio::GlommioError::Closed(glommio::ResourceType::Channel(v)) => v,
+      glommio::GlommioError::WouldBlock(glommio::ResourceType::Channel(v)) => v,
+      _ => unreachable!(),
+    })
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/modules/metrics.rs.html b/doc/src/fred/modules/metrics.rs.html new file mode 100644 index 00000000..d7fc34e8 --- /dev/null +++ b/doc/src/fred/modules/metrics.rs.html @@ -0,0 +1,221 @@ +metrics.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+
#![allow(unused_variables)]
+#![allow(dead_code)]
+
+use std::cmp;
+
+/// Stats describing a distribution of samples.
+///
+/// Time units are in milliseconds, data size units are in bytes.
+pub struct Stats {
+  pub min:     i64,
+  pub max:     i64,
+  pub avg:     f64,
+  pub stddev:  f64,
+  pub samples: u64,
+  pub sum:     i64,
+}
+
+/// Struct for tracking moving stats about network latency or request/response sizes.
+///
+/// Time units are in milliseconds, data size units are in bytes.
+pub struct MovingStats {
+  pub min:      i64,
+  pub max:      i64,
+  pub avg:      f64,
+  pub variance: f64,
+  pub samples:  u64,
+  pub sum:      i64,
+  old_avg:      f64,
+  s:            f64,
+  old_s:        f64,
+}
+
+impl Default for MovingStats {
+  fn default() -> Self {
+    MovingStats {
+      min:      0,
+      max:      0,
+      avg:      0.0,
+      sum:      0,
+      variance: 0.0,
+      samples:  0,
+      s:        0.0,
+      old_s:    0.0,
+      old_avg:  0.0,
+    }
+  }
+}
+
+impl MovingStats {
+  pub fn sample(&mut self, value: i64) {
+    self.samples += 1;
+    let num_samples = self.samples as f64;
+    let value_f = value as f64;
+    self.sum += value;
+
+    if self.samples == 1 {
+      self.avg = value_f;
+      self.variance = 0.0;
+      self.old_avg = value_f;
+      self.old_s = 0.0;
+      self.min = value;
+      self.max = value;
+    } else {
+      self.avg = self.old_avg + (value_f - self.old_avg) / num_samples;
+      self.s = self.old_s + (value_f - self.old_avg) * (value_f - self.avg);
+
+      self.old_avg = self.avg;
+      self.old_s = self.s;
+      self.variance = self.s / (num_samples - 1.0);
+
+      self.min = cmp::min(self.min, value);
+      self.max = cmp::max(self.max, value);
+    }
+  }
+
+  pub fn reset(&mut self) {
+    self.min = 0;
+    self.max = 0;
+    self.avg = 0.0;
+    self.variance = 0.0;
+    self.samples = 0;
+    self.sum = 0;
+    self.s = 0.0;
+    self.old_s = 0.0;
+    self.old_avg = 0.0;
+  }
+
+  pub fn read_metrics(&self) -> Stats {
+    self.into()
+  }
+
+  pub fn take_metrics(&mut self) -> Stats {
+    let metrics = self.read_metrics();
+    self.reset();
+    metrics
+  }
+}
+
+impl<'a> From<&'a MovingStats> for Stats {
+  fn from(stats: &'a MovingStats) -> Stats {
+    Stats {
+      avg:     stats.avg,
+      stddev:  stats.variance.sqrt(),
+      min:     stats.min,
+      max:     stats.max,
+      samples: stats.samples,
+      sum:     stats.sum,
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/modules/mod.rs.html b/doc/src/fred/modules/mod.rs.html new file mode 100644 index 00000000..3c84eae8 --- /dev/null +++ b/doc/src/fred/modules/mod.rs.html @@ -0,0 +1,19 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+
pub mod backchannel;
+/// Utility functions for reading or changing global config values.
+pub mod inner;
+pub mod metrics;
+pub mod response;
+
+#[cfg(feature = "mocks")]
+#[cfg_attr(docsrs, doc(cfg(feature = "mocks")))]
+pub mod mocks;
+
\ No newline at end of file diff --git a/doc/src/fred/modules/response.rs.html b/doc/src/fred/modules/response.rs.html new file mode 100644 index 00000000..be7bb7a5 --- /dev/null +++ b/doc/src/fred/modules/response.rs.html @@ -0,0 +1,1985 @@ +response.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  types::{RedisKey, RedisValue, QUEUED},
+};
+use bytes::Bytes;
+use bytes_utils::Str;
+use std::{
+  collections::{BTreeMap, BTreeSet, HashMap, HashSet},
+  hash::{BuildHasher, Hash},
+};
+
+#[cfg(feature = "i-cluster")]
+use crate::types::ClusterInfo;
+#[cfg(feature = "i-geo")]
+use crate::types::GeoPosition;
+#[cfg(feature = "i-slowlog")]
+use crate::types::SlowlogEntry;
+#[cfg(feature = "i-memory")]
+use crate::types::{DatabaseMemoryStats, MemoryStats};
+
+#[allow(unused_imports)]
+use std::any::type_name;
+
+#[cfg(feature = "serde-json")]
+use serde_json::{Map, Value};
+
+macro_rules! debug_type(
+  ($($arg:tt)*) => {
+    #[cfg(feature="network-logs")]
+    log::trace!($($arg)*);
+  }
+);
+
+macro_rules! check_single_bulk_reply(
+  ($v:expr) => {
+    if $v.is_single_element_vec() {
+      return Self::from_value($v.pop_or_take());
+    }
+  };
+  ($t:ty, $v:expr) => {
+    if $v.is_single_element_vec() {
+      return $t::from_value($v.pop_or_take());
+    }
+  }
+);
+
+macro_rules! to_signed_number(
+  ($t:ty, $v:expr) => {
+    match $v {
+      RedisValue::Double(f) => Ok(f as $t),
+      RedisValue::Integer(i) => Ok(i as $t),
+      RedisValue::String(s) => s.parse::<$t>().map_err(|e| e.into()),
+      RedisValue::Array(mut a) => if a.len() == 1 {
+        match a.pop().unwrap() {
+          RedisValue::Integer(i) => Ok(i as $t),
+          RedisValue::String(s) => s.parse::<$t>().map_err(|e| e.into()),
+          #[cfg(feature = "default-nil-types")]
+          RedisValue::Null => Ok(0),
+          #[cfg(not(feature = "default-nil-types"))]
+          RedisValue::Null => Err(RedisError::new(RedisErrorKind::NotFound, "Cannot convert nil to number.")),
+          _ => Err(RedisError::new_parse("Cannot convert to number."))
+        }
+      }else{
+        Err(RedisError::new_parse("Cannot convert array to number."))
+      }
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Ok(0),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => Err(RedisError::new(RedisErrorKind::NotFound, "Cannot convert nil to number.")),
+      _ => Err(RedisError::new_parse("Cannot convert to number.")),
+    }
+  }
+);
+
+macro_rules! to_unsigned_number(
+  ($t:ty, $v:expr) => {
+    match $v {
+      RedisValue::Double(f) => if f.is_sign_negative() {
+        Err(RedisError::new_parse("Cannot convert from negative number."))
+      }else{
+        Ok(f as $t)
+      },
+      RedisValue::Integer(i) => if i < 0 {
+        Err(RedisError::new_parse("Cannot convert from negative number."))
+      }else{
+        Ok(i as $t)
+      },
+      RedisValue::String(s) => s.parse::<$t>().map_err(|e| e.into()),
+      RedisValue::Array(mut a) => if a.len() == 1 {
+        match a.pop().unwrap() {
+          RedisValue::Integer(i) => if i < 0 {
+            Err(RedisError::new_parse("Cannot convert from negative number."))
+          }else{
+            Ok(i as $t)
+          },
+          #[cfg(feature = "default-nil-types")]
+          RedisValue::Null => Ok(0),
+          #[cfg(not(feature = "default-nil-types"))]
+          RedisValue::Null => Err(RedisError::new(RedisErrorKind::NotFound, "Cannot convert nil to number.")),
+          RedisValue::String(s) => s.parse::<$t>().map_err(|e| e.into()),
+          _ => Err(RedisError::new_parse("Cannot convert to number."))
+        }
+      }else{
+        Err(RedisError::new_parse("Cannot convert array to number."))
+      },
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Ok(0),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => Err(RedisError::new(RedisErrorKind::NotFound, "Cannot convert nil to number.")),
+      _ => Err(RedisError::new_parse("Cannot convert to number.")),
+    }
+  }
+);
+
+macro_rules! impl_signed_number (
+  ($t:ty) => {
+    impl FromRedis for $t {
+      fn from_value(value: RedisValue) -> Result<$t, RedisError> {
+        check_single_bulk_reply!(value);
+        to_signed_number!($t, value)
+      }
+    }
+  }
+);
+
+macro_rules! impl_unsigned_number (
+  ($t:ty) => {
+    impl FromRedis for $t {
+      fn from_value(value: RedisValue) -> Result<$t, RedisError> {
+        check_single_bulk_reply!(value);
+        to_unsigned_number!($t, value)
+      }
+    }
+  }
+);
+
+/// A trait used to [convert](RedisValue::convert) various forms of [RedisValue](RedisValue) into different types.
+///
+/// ## Examples
+///
+/// ```rust
+/// # use fred::types::RedisValue;
+/// # use std::collections::HashMap;
+/// let foo: usize = RedisValue::String("123".into()).convert()?;
+/// let foo: i64 = RedisValue::String("123".into()).convert()?;
+/// let foo: String = RedisValue::String("123".into()).convert()?;
+/// let foo: Vec<u8> = RedisValue::Bytes(vec![102, 111, 111].into()).convert()?;
+/// let foo: Vec<u8> = RedisValue::String("foo".into()).convert()?;
+/// let foo: Vec<String> = RedisValue::Array(vec!["a".into(), "b".into()]).convert()?;
+/// let foo: HashMap<String, u16> =
+///   RedisValue::Array(vec!["a".into(), 1.into(), "b".into(), 2.into()]).convert()?;
+/// let foo: (String, i64) = RedisValue::Array(vec!["a".into(), 1.into()]).convert()?;
+/// let foo: Vec<(String, i64)> =
+///   RedisValue::Array(vec!["a".into(), 1.into(), "b".into(), 2.into()]).convert()?;
+/// // ...
+/// ```
+///
+/// ## Bulk Values
+///
+/// This interface can also convert single-element vectors to scalar values in certain scenarios. This is often
+/// useful with commands that conditionally return bulk values, or where the number of elements in the response
+/// depends on the number of arguments (`MGET`, etc).
+///
+/// For example:
+///
+/// ```rust
+/// # use fred::types::RedisValue;
+/// let _: String = RedisValue::Array(vec![]).convert()?; // error
+/// let _: String = RedisValue::Array(vec!["a".into()]).convert()?; // "a"
+/// let _: String = RedisValue::Array(vec!["a".into(), "b".into()]).convert()?; // error
+/// let _: Option<String> = RedisValue::Array(vec![]).convert()?; // None
+/// let _: Option<String> = RedisValue::Array(vec!["a".into()]).convert()?; // Some("a")
+/// let _: Option<String> = RedisValue::Array(vec!["a".into(), "b".into()]).convert()?; // error
+/// ```
+///
+/// ## The `default-nil-types` Feature Flag
+///
+/// By default a `nil` value cannot be converted directly into any of the scalar types (`u8`, `String`, `Bytes`,
+/// etc). In practice this often requires callers to use an `Option` or `Vec` container with commands that can return
+/// `nil`.
+///
+/// The `default-nil-types` feature flag can enable some further type conversion branches that treat `nil` values as
+/// default values for the relevant type. For `RedisValue::Null` these include:
+///
+/// * `impl FromRedis` for `String` or `Str` returns an empty string.
+/// * `impl FromRedis` for `Bytes` or `Vec<T>` returns an empty array.
+/// * `impl FromRedis` for any integer or float type returns `0`
+/// * `impl FromRedis` for `bool` returns `false`
+/// * `impl FromRedis` for map or set types return an empty map or set.
+pub trait FromRedis: Sized {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError>;
+
+  #[doc(hidden)]
+  fn from_values(values: Vec<RedisValue>) -> Result<Vec<Self>, RedisError> {
+    values.into_iter().map(|v| Self::from_value(v)).collect()
+  }
+
+  #[doc(hidden)]
+  // FIXME if/when specialization is stable
+  fn from_owned_bytes(_: Vec<u8>) -> Option<Vec<Self>> {
+    None
+  }
+  #[doc(hidden)]
+  fn is_tuple() -> bool {
+    false
+  }
+}
+
+impl FromRedis for RedisValue {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    Ok(value)
+  }
+}
+
+impl FromRedis for () {
+  fn from_value(_: RedisValue) -> Result<Self, RedisError> {
+    Ok(())
+  }
+}
+
+impl_signed_number!(i8);
+impl_signed_number!(i16);
+impl_signed_number!(i32);
+impl_signed_number!(i64);
+impl_signed_number!(i128);
+impl_signed_number!(isize);
+
+impl FromRedis for u8 {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    check_single_bulk_reply!(value);
+    to_unsigned_number!(u8, value)
+  }
+
+  fn from_owned_bytes(d: Vec<u8>) -> Option<Vec<Self>> {
+    Some(d)
+  }
+}
+
+impl_unsigned_number!(u16);
+impl_unsigned_number!(u32);
+impl_unsigned_number!(u64);
+impl_unsigned_number!(u128);
+impl_unsigned_number!(usize);
+
+impl FromRedis for String {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    debug_type!("FromRedis(String): {:?}", value);
+    check_single_bulk_reply!(value);
+
+    value
+      .into_string()
+      .ok_or(RedisError::new_parse("Could not convert to string."))
+  }
+}
+
+impl FromRedis for Str {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    debug_type!("FromRedis(Str): {:?}", value);
+    check_single_bulk_reply!(value);
+
+    value
+      .into_bytes_str()
+      .ok_or(RedisError::new_parse("Could not convert to string."))
+  }
+}
+
+impl FromRedis for f64 {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    debug_type!("FromRedis(f64): {:?}", value);
+    check_single_bulk_reply!(value);
+
+    value
+      .as_f64()
+      .ok_or(RedisError::new_parse("Could not convert to double."))
+  }
+}
+
+impl FromRedis for f32 {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    debug_type!("FromRedis(f32): {:?}", value);
+    check_single_bulk_reply!(value);
+
+    value
+      .as_f64()
+      .map(|f| f as f32)
+      .ok_or(RedisError::new_parse("Could not convert to float."))
+  }
+}
+
+impl FromRedis for bool {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    debug_type!("FromRedis(bool): {:?}", value);
+    check_single_bulk_reply!(value);
+
+    if let Some(val) = value.as_bool() {
+      Ok(val)
+    } else {
+      // it's not obvious how to convert the value to a bool in this block, so we go with a
+      // tried and true approach that i'm sure we'll never regret - JS semantics
+      Ok(match value {
+        RedisValue::String(s) => !s.is_empty(),
+        RedisValue::Bytes(b) => !b.is_empty(),
+        // everything else should be covered by `as_bool` above
+        _ => return Err(RedisError::new_parse("Could not convert to bool.")),
+      })
+    }
+  }
+}
+
+impl<T> FromRedis for Option<T>
+where
+  T: FromRedis,
+{
+  fn from_value(value: RedisValue) -> Result<Option<T>, RedisError> {
+    debug_type!("FromRedis(Option<{}>): {:?}", type_name::<T>(), value);
+
+    if let Some(0) = value.array_len() {
+      Ok(None)
+    } else if value.is_null() {
+      Ok(None)
+    } else {
+      Ok(Some(T::from_value(value)?))
+    }
+  }
+}
+
+impl FromRedis for Bytes {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    debug_type!("FromRedis(Bytes): {:?}", value);
+    check_single_bulk_reply!(value);
+
+    value
+      .into_bytes()
+      .ok_or(RedisError::new_parse("Cannot parse into bytes."))
+  }
+}
+
+impl<T> FromRedis for Vec<T>
+where
+  T: FromRedis,
+{
+  fn from_value(value: RedisValue) -> Result<Vec<T>, RedisError> {
+    debug_type!("FromRedis(Vec<{}>): {:?}", type_name::<T>(), value);
+
+    match value {
+      RedisValue::Bytes(bytes) => {
+        T::from_owned_bytes(bytes.to_vec()).ok_or(RedisError::new_parse("Cannot convert from bytes"))
+      },
+      RedisValue::String(string) => {
+        // hacky way to check if T is bytes without consuming `string`
+        if T::from_owned_bytes(Vec::new()).is_some() {
+          T::from_owned_bytes(string.into_inner().to_vec())
+            .ok_or(RedisError::new_parse("Could not convert string to bytes."))
+        } else {
+          Ok(vec![T::from_value(RedisValue::String(string))?])
+        }
+      },
+      RedisValue::Array(values) => {
+        if !values.is_empty() {
+          if let RedisValue::Array(_) = &values[0] {
+            values.into_iter().map(|x| T::from_value(x)).collect()
+          } else {
+            T::from_values(values)
+          }
+        } else {
+          Ok(vec![])
+        }
+      },
+      RedisValue::Map(map) => {
+        // not being able to use collect() here is unfortunate
+        let out = Vec::with_capacity(map.len() * 2);
+        map.inner().into_iter().try_fold(out, |mut out, (key, value)| {
+          if T::is_tuple() {
+            // try to convert to a 2-element tuple since that's a common use case from `HGETALL`, etc
+            out.push(T::from_value(RedisValue::Array(vec![key.into(), value]))?);
+          } else {
+            out.push(T::from_value(key.into())?);
+            out.push(T::from_value(value)?);
+          }
+
+          Ok(out)
+        })
+      },
+      RedisValue::Integer(i) => Ok(vec![T::from_value(RedisValue::Integer(i))?]),
+      RedisValue::Double(f) => Ok(vec![T::from_value(RedisValue::Double(f))?]),
+      RedisValue::Boolean(b) => Ok(vec![T::from_value(RedisValue::Boolean(b))?]),
+      RedisValue::Queued => Ok(vec![T::from_value(RedisValue::from_static_str(QUEUED))?]),
+      RedisValue::Null => Ok(Vec::new()),
+    }
+  }
+}
+
+impl<T, const N: usize> FromRedis for [T; N]
+where
+  T: FromRedis,
+{
+  fn from_value(value: RedisValue) -> Result<[T; N], RedisError> {
+    debug_type!("FromRedis([{}; {}]): {:?}", type_name::<T>(), N, value);
+    // use the `from_value` impl for Vec<T>
+    let value: Vec<T> = value.convert()?;
+    let len = value.len();
+
+    value
+      .try_into()
+      .map_err(|_| RedisError::new_parse(format!("Failed to convert to array. Expected {}, found {}.", N, len)))
+  }
+}
+
+impl<K, V, S> FromRedis for HashMap<K, V, S>
+where
+  K: FromRedisKey + Eq + Hash,
+  V: FromRedis,
+  S: BuildHasher + Default,
+{
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    debug_type!(
+      "FromRedis(HashMap<{}, {}>): {:?}",
+      type_name::<K>(),
+      type_name::<V>(),
+      value
+    );
+
+    let as_map = if value.is_array() || value.is_map() || value.is_null() {
+      value
+        .into_map()
+        .map_err(|_| RedisError::new_parse("Cannot convert to map."))?
+    } else {
+      return Err(RedisError::new_parse("Cannot convert to map."));
+    };
+
+    as_map
+      .inner()
+      .into_iter()
+      .map(|(k, v)| Ok((K::from_key(k)?, V::from_value(v)?)))
+      .collect()
+  }
+}
+
+impl<V, S> FromRedis for HashSet<V, S>
+where
+  V: FromRedis + Hash + Eq,
+  S: BuildHasher + Default,
+{
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    debug_type!("FromRedis(HashSet<{}>): {:?}", type_name::<V>(), value);
+    value.into_set()?.into_iter().map(|v| V::from_value(v)).collect()
+  }
+}
+
+impl<K, V> FromRedis for BTreeMap<K, V>
+where
+  K: FromRedisKey + Ord,
+  V: FromRedis,
+{
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    debug_type!(
+      "FromRedis(BTreeMap<{}, {}>): {:?}",
+      type_name::<K>(),
+      type_name::<V>(),
+      value
+    );
+    let as_map = if value.is_array() || value.is_map() || value.is_null() {
+      value
+        .into_map()
+        .map_err(|_| RedisError::new_parse("Cannot convert to map."))?
+    } else {
+      return Err(RedisError::new_parse("Cannot convert to map."));
+    };
+
+    as_map
+      .inner()
+      .into_iter()
+      .map(|(k, v)| Ok((K::from_key(k)?, V::from_value(v)?)))
+      .collect()
+  }
+}
+
+impl<V> FromRedis for BTreeSet<V>
+where
+  V: FromRedis + Ord,
+{
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    debug_type!("FromRedis(BTreeSet<{}>): {:?}", type_name::<V>(), value);
+    value.into_set()?.into_iter().map(|v| V::from_value(v)).collect()
+  }
+}
+
+// adapted from mitsuhiko
+macro_rules! impl_from_redis_tuple {
+  () => ();
+  ($($name:ident,)+) => (
+    #[doc(hidden)]
+    impl<$($name: FromRedis),*> FromRedis for ($($name,)*) {
+      fn is_tuple() -> bool {
+        true
+      }
+
+      #[allow(non_snake_case, unused_variables)]
+      fn from_value(v: RedisValue) -> Result<($($name,)*), RedisError> {
+        if let RedisValue::Array(mut values) = v {
+          let mut n = 0;
+          $(let $name = (); n += 1;)*
+          debug_type!("FromRedis({}-tuple): {:?}", n, values);
+          if values.len() != n {
+            return Err(RedisError::new_parse(format!("Invalid tuple dimension. Expected {}, found {}.", n, values.len())));
+          }
+
+          // since we have ownership over the values we have some freedom in how to implement this
+          values.reverse();
+          Ok(($({let $name = (); values
+            .pop()
+            .ok_or(RedisError::new_parse("Expected value, found none."))?
+            .convert()?
+          },)*))
+        }else{
+          Err(RedisError::new_parse("Could not convert to tuple."))
+        }
+      }
+
+      #[allow(non_snake_case, unused_variables)]
+      fn from_values(mut values: Vec<RedisValue>) -> Result<Vec<($($name,)*)>, RedisError> {
+        let mut n = 0;
+        $(let $name = (); n += 1;)*
+        debug_type!("FromRedis({}-tuple): {:?}", n, values);
+        if values.len() % n != 0 {
+          return Err(RedisError::new_parse(format!("Invalid tuple dimension. Expected {}, found {}.", n, values.len())));
+        }
+
+        let mut out = Vec::with_capacity(values.len() / n);
+        // this would be cleaner if there were an owned `chunks` variant
+        for chunk in values.chunks_exact_mut(n) {
+          match chunk {
+            [$($name),*] => out.push(($($name.take().convert()?),*),),
+             _ => unreachable!(),
+          }
+        }
+
+        Ok(out)
+      }
+    }
+    impl_from_redis_peel!($($name,)*);
+  )
+}
+
+macro_rules! impl_from_redis_peel {
+  ($name:ident, $($other:ident,)*) => (impl_from_redis_tuple!($($other,)*);)
+}
+
+impl_from_redis_tuple! { T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, }
+
+macro_rules! impl_from_str_from_redis_key (
+  ($t:ty) => {
+    impl FromRedisKey for $t {
+      fn from_key(value: RedisKey) -> Result<$t, RedisError> {
+        value
+          .as_str()
+          .and_then(|k| k.parse::<$t>().ok())
+          .ok_or(RedisError::new_parse("Cannot parse key from bytes."))
+      }
+    }
+  }
+);
+
+#[cfg(feature = "serde-json")]
+#[cfg_attr(docsrs, doc(cfg(feature = "serde-json")))]
+impl FromRedis for Value {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    let value = match value {
+      RedisValue::Null => Value::Null,
+      RedisValue::Queued => QUEUED.into(),
+      RedisValue::String(s) => {
+        // check for nested json. this is particularly useful with JSON.GET
+        serde_json::from_str(&s).ok().unwrap_or_else(|| s.to_string().into())
+      },
+      RedisValue::Bytes(b) => {
+        let val = RedisValue::String(Str::from_inner(b)?);
+        Self::from_value(val)?
+      },
+      RedisValue::Integer(i) => i.into(),
+      RedisValue::Double(f) => f.into(),
+      RedisValue::Boolean(b) => b.into(),
+      RedisValue::Array(v) => {
+        let mut out = Vec::with_capacity(v.len());
+        for value in v.into_iter() {
+          out.push(Self::from_value(value)?);
+        }
+        Value::Array(out)
+      },
+      RedisValue::Map(v) => {
+        let mut out = Map::with_capacity(v.len());
+        for (key, value) in v.inner().into_iter() {
+          let key = key
+            .into_string()
+            .ok_or(RedisError::new_parse("Cannot convert key to string."))?;
+          let value = Self::from_value(value)?;
+
+          out.insert(key, value);
+        }
+        Value::Object(out)
+      },
+    };
+
+    Ok(value)
+  }
+}
+
+#[cfg(feature = "i-geo")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))]
+impl FromRedis for GeoPosition {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    GeoPosition::try_from(value)
+  }
+}
+
+#[cfg(feature = "i-slowlog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))]
+impl FromRedis for SlowlogEntry {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    SlowlogEntry::try_from(value)
+  }
+}
+
+#[cfg(feature = "i-cluster")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))]
+impl FromRedis for ClusterInfo {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    ClusterInfo::try_from(value)
+  }
+}
+
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+impl FromRedis for MemoryStats {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    MemoryStats::try_from(value)
+  }
+}
+
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+impl FromRedis for DatabaseMemoryStats {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    DatabaseMemoryStats::try_from(value)
+  }
+}
+
+impl FromRedis for RedisKey {
+  fn from_value(value: RedisValue) -> Result<Self, RedisError> {
+    let key = match value {
+      RedisValue::Boolean(b) => b.into(),
+      RedisValue::Integer(i) => i.into(),
+      RedisValue::Double(f) => f.into(),
+      RedisValue::String(s) => s.into(),
+      RedisValue::Bytes(b) => b.into(),
+      RedisValue::Queued => RedisKey::from_static_str(QUEUED),
+      RedisValue::Map(_) | RedisValue::Array(_) => {
+        return Err(RedisError::new_parse("Cannot convert aggregate type to key."))
+      },
+      RedisValue::Null => return Err(RedisError::new(RedisErrorKind::NotFound, "Cannot convert nil to key.")),
+    };
+
+    Ok(key)
+  }
+}
+
+/// A trait used to convert [RedisKey](crate::types::RedisKey) values to various types.
+///
+/// See the [convert](crate::types::RedisKey::convert) documentation for more information.
+pub trait FromRedisKey: Sized {
+  fn from_key(value: RedisKey) -> Result<Self, RedisError>;
+}
+
+impl_from_str_from_redis_key!(u8);
+impl_from_str_from_redis_key!(u16);
+impl_from_str_from_redis_key!(u32);
+impl_from_str_from_redis_key!(u64);
+impl_from_str_from_redis_key!(u128);
+impl_from_str_from_redis_key!(usize);
+impl_from_str_from_redis_key!(i8);
+impl_from_str_from_redis_key!(i16);
+impl_from_str_from_redis_key!(i32);
+impl_from_str_from_redis_key!(i64);
+impl_from_str_from_redis_key!(i128);
+impl_from_str_from_redis_key!(isize);
+impl_from_str_from_redis_key!(f32);
+impl_from_str_from_redis_key!(f64);
+
+impl FromRedisKey for () {
+  fn from_key(_: RedisKey) -> Result<Self, RedisError> {
+    Ok(())
+  }
+}
+
+impl FromRedisKey for RedisValue {
+  fn from_key(value: RedisKey) -> Result<Self, RedisError> {
+    Ok(RedisValue::Bytes(value.into_bytes()))
+  }
+}
+
+impl FromRedisKey for RedisKey {
+  fn from_key(value: RedisKey) -> Result<Self, RedisError> {
+    Ok(value)
+  }
+}
+
+impl FromRedisKey for String {
+  fn from_key(value: RedisKey) -> Result<Self, RedisError> {
+    value
+      .into_string()
+      .ok_or(RedisError::new_parse("Cannot parse key as string."))
+  }
+}
+
+impl FromRedisKey for Str {
+  fn from_key(value: RedisKey) -> Result<Self, RedisError> {
+    Ok(Str::from_inner(value.into_bytes())?)
+  }
+}
+
+impl FromRedisKey for Vec<u8> {
+  fn from_key(value: RedisKey) -> Result<Self, RedisError> {
+    Ok(value.into_bytes().to_vec())
+  }
+}
+
+impl FromRedisKey for Bytes {
+  fn from_key(value: RedisKey) -> Result<Self, RedisError> {
+    Ok(value.into_bytes())
+  }
+}
+
+#[cfg(test)]
+mod tests {
+  use crate::types::RedisValue;
+  use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
+
+  #[cfg(not(feature = "default-nil-types"))]
+  use crate::error::RedisError;
+
+  #[test]
+  fn should_convert_signed_numeric_types() {
+    let _foo: i8 = RedisValue::String("123".into()).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: i8 = RedisValue::Integer(123).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: i16 = RedisValue::String("123".into()).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: i16 = RedisValue::Integer(123).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: i32 = RedisValue::String("123".into()).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: i32 = RedisValue::Integer(123).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: i64 = RedisValue::String("123".into()).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: i64 = RedisValue::Integer(123).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: i128 = RedisValue::String("123".into()).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: i128 = RedisValue::Integer(123).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: isize = RedisValue::String("123".into()).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: isize = RedisValue::Integer(123).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: f32 = RedisValue::String("123.5".into()).convert().unwrap();
+    assert_eq!(_foo, 123.5);
+    let _foo: f64 = RedisValue::String("123.5".into()).convert().unwrap();
+    assert_eq!(_foo, 123.5);
+  }
+
+  #[test]
+  fn should_convert_unsigned_numeric_types() {
+    let _foo: u8 = RedisValue::String("123".into()).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: u8 = RedisValue::Integer(123).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: u16 = RedisValue::String("123".into()).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: u16 = RedisValue::Integer(123).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: u32 = RedisValue::String("123".into()).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: u32 = RedisValue::Integer(123).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: u64 = RedisValue::String("123".into()).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: u64 = RedisValue::Integer(123).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: u128 = RedisValue::String("123".into()).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: u128 = RedisValue::Integer(123).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: usize = RedisValue::String("123".into()).convert().unwrap();
+    assert_eq!(_foo, 123);
+    let _foo: usize = RedisValue::Integer(123).convert().unwrap();
+    assert_eq!(_foo, 123);
+  }
+
+  #[test]
+  #[cfg(not(feature = "default-nil-types"))]
+  fn should_return_not_found_with_null_number_types() {
+    let result: Result<u8, RedisError> = RedisValue::Null.convert();
+    assert!(result.unwrap_err().is_not_found());
+    let result: Result<u16, RedisError> = RedisValue::Null.convert();
+    assert!(result.unwrap_err().is_not_found());
+    let result: Result<u32, RedisError> = RedisValue::Null.convert();
+    assert!(result.unwrap_err().is_not_found());
+    let result: Result<u64, RedisError> = RedisValue::Null.convert();
+    assert!(result.unwrap_err().is_not_found());
+    let result: Result<u128, RedisError> = RedisValue::Null.convert();
+    assert!(result.unwrap_err().is_not_found());
+    let result: Result<usize, RedisError> = RedisValue::Null.convert();
+    assert!(result.unwrap_err().is_not_found());
+    let result: Result<i8, RedisError> = RedisValue::Null.convert();
+    assert!(result.unwrap_err().is_not_found());
+    let result: Result<i16, RedisError> = RedisValue::Null.convert();
+    assert!(result.unwrap_err().is_not_found());
+    let result: Result<i32, RedisError> = RedisValue::Null.convert();
+    assert!(result.unwrap_err().is_not_found());
+    let result: Result<i64, RedisError> = RedisValue::Null.convert();
+    assert!(result.unwrap_err().is_not_found());
+    let result: Result<i128, RedisError> = RedisValue::Null.convert();
+    assert!(result.unwrap_err().is_not_found());
+    let result: Result<isize, RedisError> = RedisValue::Null.convert();
+    assert!(result.unwrap_err().is_not_found());
+  }
+
+  #[test]
+  #[cfg(feature = "default-nil-types")]
+  fn should_return_zero_with_null_number_types() {
+    assert_eq!(0, RedisValue::Null.convert::<u8>().unwrap());
+    assert_eq!(0, RedisValue::Null.convert::<u16>().unwrap());
+    assert_eq!(0, RedisValue::Null.convert::<u32>().unwrap());
+    assert_eq!(0, RedisValue::Null.convert::<u64>().unwrap());
+    assert_eq!(0, RedisValue::Null.convert::<u128>().unwrap());
+    assert_eq!(0, RedisValue::Null.convert::<usize>().unwrap());
+    assert_eq!(0, RedisValue::Null.convert::<i8>().unwrap());
+    assert_eq!(0, RedisValue::Null.convert::<i16>().unwrap());
+    assert_eq!(0, RedisValue::Null.convert::<i32>().unwrap());
+    assert_eq!(0, RedisValue::Null.convert::<i64>().unwrap());
+    assert_eq!(0, RedisValue::Null.convert::<i128>().unwrap());
+    assert_eq!(0, RedisValue::Null.convert::<isize>().unwrap());
+    assert_eq!(0.0, RedisValue::Null.convert::<f32>().unwrap());
+    assert_eq!(0.0, RedisValue::Null.convert::<f64>().unwrap());
+  }
+
+  #[test]
+  #[cfg(feature = "default-nil-types")]
+  fn should_convert_null_to_false() {
+    assert!(!RedisValue::Null.convert::<bool>().unwrap());
+  }
+
+  #[test]
+  #[should_panic]
+  #[cfg(not(feature = "default-nil-types"))]
+  fn should_not_convert_null_to_false() {
+    assert!(!RedisValue::Null.convert::<bool>().unwrap());
+  }
+
+  #[test]
+  fn should_convert_strings() {
+    let _foo: String = RedisValue::String("foo".into()).convert().unwrap();
+    assert_eq!(_foo, "foo".to_owned());
+  }
+
+  #[test]
+  fn should_convert_numbers_to_bools() {
+    let foo: bool = RedisValue::Integer(0).convert().unwrap();
+    assert!(!foo);
+    let foo: bool = RedisValue::Integer(1).convert().unwrap();
+    assert!(foo);
+    let foo: bool = RedisValue::String("0".into()).convert().unwrap();
+    assert!(!foo);
+    let foo: bool = RedisValue::String("1".into()).convert().unwrap();
+    assert!(foo);
+  }
+
+  #[test]
+  fn should_convert_bytes() {
+    let foo: Vec<u8> = RedisValue::Bytes("foo".as_bytes().to_vec().into()).convert().unwrap();
+    assert_eq!(foo, "foo".as_bytes().to_vec());
+    let foo: Vec<u8> = RedisValue::String("foo".into()).convert().unwrap();
+    assert_eq!(foo, "foo".as_bytes().to_vec());
+    let foo: Vec<u8> = RedisValue::Array(vec![102.into(), 111.into(), 111.into()])
+      .convert()
+      .unwrap();
+    assert_eq!(foo, "foo".as_bytes().to_vec());
+  }
+
+  #[test]
+  fn should_convert_arrays() {
+    let foo: Vec<String> = RedisValue::Array(vec!["a".into(), "b".into()]).convert().unwrap();
+    assert_eq!(foo, vec!["a".to_owned(), "b".to_owned()]);
+  }
+
+  #[test]
+  fn should_convert_hash_maps() {
+    let foo: HashMap<String, u16> = RedisValue::Array(vec!["a".into(), 1.into(), "b".into(), 2.into()])
+      .convert()
+      .unwrap();
+
+    let mut expected = HashMap::new();
+    expected.insert("a".to_owned(), 1);
+    expected.insert("b".to_owned(), 2);
+    assert_eq!(foo, expected);
+  }
+
+  #[test]
+  fn should_convert_hash_sets() {
+    let foo: HashSet<String> = RedisValue::Array(vec!["a".into(), "b".into()]).convert().unwrap();
+
+    let mut expected = HashSet::new();
+    expected.insert("a".to_owned());
+    expected.insert("b".to_owned());
+    assert_eq!(foo, expected);
+  }
+
+  #[test]
+  fn should_convert_btree_maps() {
+    let foo: BTreeMap<String, u16> = RedisValue::Array(vec!["a".into(), 1.into(), "b".into(), 2.into()])
+      .convert()
+      .unwrap();
+
+    let mut expected = BTreeMap::new();
+    expected.insert("a".to_owned(), 1);
+    expected.insert("b".to_owned(), 2);
+    assert_eq!(foo, expected);
+  }
+
+  #[test]
+  fn should_convert_btree_sets() {
+    let foo: BTreeSet<String> = RedisValue::Array(vec!["a".into(), "b".into()]).convert().unwrap();
+
+    let mut expected = BTreeSet::new();
+    expected.insert("a".to_owned());
+    expected.insert("b".to_owned());
+    assert_eq!(foo, expected);
+  }
+
+  #[test]
+  fn should_convert_tuples() {
+    let foo: (String, i64) = RedisValue::Array(vec!["a".into(), 1.into()]).convert().unwrap();
+    assert_eq!(foo, ("a".to_owned(), 1));
+  }
+
+  #[test]
+  fn should_convert_array_tuples() {
+    let foo: Vec<(String, i64)> = RedisValue::Array(vec!["a".into(), 1.into(), "b".into(), 2.into()])
+      .convert()
+      .unwrap();
+    assert_eq!(foo, vec![("a".to_owned(), 1), ("b".to_owned(), 2)]);
+  }
+
+  #[test]
+  fn should_handle_single_element_vector_to_scalar() {
+    assert!(RedisValue::Array(vec![]).convert::<String>().is_err());
+    assert_eq!(
+      RedisValue::Array(vec!["foo".into()]).convert::<String>(),
+      Ok("foo".into())
+    );
+    assert!(RedisValue::Array(vec!["foo".into(), "bar".into()])
+      .convert::<String>()
+      .is_err());
+
+    assert_eq!(RedisValue::Array(vec![]).convert::<Option<String>>(), Ok(None));
+    assert_eq!(
+      RedisValue::Array(vec!["foo".into()]).convert::<Option<String>>(),
+      Ok(Some("foo".into()))
+    );
+    assert!(RedisValue::Array(vec!["foo".into(), "bar".into()])
+      .convert::<Option<String>>()
+      .is_err());
+  }
+
+  #[test]
+  fn should_convert_null_to_empty_array() {
+    assert_eq!(Vec::<String>::new(), RedisValue::Null.convert::<Vec<String>>().unwrap());
+    assert_eq!(Vec::<u8>::new(), RedisValue::Null.convert::<Vec<u8>>().unwrap());
+  }
+
+  #[test]
+  fn should_convert_to_fixed_arrays() {
+    let foo: [i64; 2] = RedisValue::Array(vec![1.into(), 2.into()]).convert().unwrap();
+    assert_eq!(foo, [1, 2]);
+
+    assert!(RedisValue::Array(vec![1.into(), 2.into()])
+      .convert::<[i64; 3]>()
+      .is_err());
+    assert!(RedisValue::Array(vec![]).convert::<[i64; 3]>().is_err());
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/monitor/mod.rs.html b/doc/src/fred/monitor/mod.rs.html new file mode 100644 index 00000000..b79b5e5f --- /dev/null +++ b/doc/src/fred/monitor/mod.rs.html @@ -0,0 +1,125 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+
use crate::{
+  error::RedisError,
+  types::{RedisConfig, RedisValue},
+  utils as client_utils,
+};
+use futures::Stream;
+use std::fmt;
+
+mod parser;
+mod utils;
+
+/// A command parsed from a [MONITOR](https://redis.io/commands/monitor) stream.
+///
+/// Formatting with the [Display](https://doc.rust-lang.org/std/fmt/trait.Display.html) trait will print the same output as `redis-cli`.
+#[derive(Clone, Debug)]
+pub struct Command {
+  /// The command run by the server.
+  pub command:   String,
+  /// Arguments passed to the command.
+  pub args:      Vec<RedisValue>,
+  /// When the command was run on the server.
+  pub timestamp: f64,
+  /// The database against which the command was run.
+  pub db:        u8,
+  /// The host and port of the client that ran the command, or `lua` when run from a script.
+  pub client:    String,
+}
+
+impl PartialEq for Command {
+  fn eq(&self, other: &Self) -> bool {
+    client_utils::f64_eq(self.timestamp, other.timestamp)
+      && self.client == other.client
+      && self.db == other.db
+      && self.command == other.command
+      && self.args == other.args
+  }
+}
+
+impl Eq for Command {}
+
+impl fmt::Display for Command {
+  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    write!(
+      f,
+      "{:.6} [{} {}] \"{}\"",
+      self.timestamp, self.db, self.client, self.command
+    )?;
+
+    for arg in self.args.iter() {
+      write!(f, " \"{}\"", arg.as_str().unwrap_or("unknown".into()))?;
+    }
+
+    Ok(())
+  }
+}
+
+/// Run the [MONITOR](https://redis.io/commands/monitor) command against the provided server.
+///
+/// Currently only centralized configurations are supported.
+pub async fn run(config: RedisConfig) -> Result<impl Stream<Item = Command>, RedisError> {
+  utils::start(config).await
+}
+
\ No newline at end of file diff --git a/doc/src/fred/monitor/parser.rs.html b/doc/src/fred/monitor/parser.rs.html new file mode 100644 index 00000000..f4c90982 --- /dev/null +++ b/doc/src/fred/monitor/parser.rs.html @@ -0,0 +1,417 @@ +parser.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+
use crate::{modules::inner::RedisClientInner, monitor::Command, runtime::RefCount, types::RedisValue};
+use nom::{
+  bytes::complete::{escaped as nom_escaped, tag as nom_tag, take as nom_take, take_until as nom_take_until},
+  character::complete::none_of as nom_none_of,
+  combinator::{map_res as nom_map_res, opt as nom_opt},
+  multi::many0 as nom_many0,
+  sequence::{delimited as nom_delimited, preceded as nom_preceded, terminated as nom_terminated},
+  IResult,
+};
+use redis_protocol::{
+  error::RedisParseError,
+  resp3::types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame},
+};
+use std::str;
+
+const EMPTY_SPACE: &str = " ";
+const RIGHT_BRACKET: &str = "]";
+const QUOTE: &str = "\"";
+
+fn to_f64(s: &str) -> Result<f64, RedisParseError<&[u8]>> {
+  s.parse::<f64>()
+    .map_err(|e| RedisParseError::new_custom("to_f64", format!("{:?}", e)))
+}
+
+fn to_u8(s: &str) -> Result<u8, RedisParseError<&[u8]>> {
+  s.parse::<u8>()
+    .map_err(|e| RedisParseError::new_custom("to_u8", format!("{:?}", e)))
+}
+
+fn to_redis_value(s: &[u8]) -> Result<RedisValue, RedisParseError<&[u8]>> {
+  // TODO make this smarter in the future
+  if let Ok(value) = str::from_utf8(s) {
+    Ok(RedisValue::String(value.into()))
+  } else {
+    Ok(RedisValue::Bytes(s.to_vec().into()))
+  }
+}
+
+fn to_str(input: &[u8]) -> Result<&str, RedisParseError<&[u8]>> {
+  str::from_utf8(input).map_err(|e| RedisParseError::new_custom("to_str", format!("{:?}", e)))
+}
+
+fn d_parse_timestamp(input: &[u8]) -> IResult<&[u8], f64, RedisParseError<&[u8]>> {
+  nom_map_res(
+    nom_map_res(nom_terminated(nom_take_until(EMPTY_SPACE), nom_take(1_usize)), to_str),
+    to_f64,
+  )(input)
+}
+
+fn d_parse_db(input: &[u8]) -> IResult<&[u8], u8, RedisParseError<&[u8]>> {
+  nom_map_res(
+    nom_map_res(
+      nom_preceded(
+        nom_take(1_usize),
+        nom_terminated(nom_take_until(EMPTY_SPACE), nom_take(1_usize)),
+      ),
+      to_str,
+    ),
+    to_u8,
+  )(input)
+}
+
+fn d_parse_client(input: &[u8]) -> IResult<&[u8], String, RedisParseError<&[u8]>> {
+  let (input, client) = nom_map_res(nom_terminated(nom_take_until(RIGHT_BRACKET), nom_take(2_usize)), to_str)(input)?;
+  Ok((input, client.to_owned()))
+}
+
+fn d_parse_command(input: &[u8]) -> IResult<&[u8], String, RedisParseError<&[u8]>> {
+  let (input, command) = nom_map_res(
+    nom_terminated(
+      nom_delimited(nom_tag(QUOTE), nom_take_until(QUOTE), nom_tag(QUOTE)),
+      // args are optional after the command string, including the empty space separating the command and args
+      nom_opt(nom_take(1_usize)),
+    ),
+    to_str,
+  )(input)?;
+
+  Ok((input, command.to_owned()))
+}
+
+fn d_parse_arg(input: &[u8]) -> IResult<&[u8], RedisValue, RedisParseError<&[u8]>> {
+  let escaped_parser = nom_escaped(nom_none_of("\\\""), '\\', nom_tag(QUOTE));
+  nom_map_res(
+    nom_terminated(
+      nom_delimited(nom_tag(QUOTE), escaped_parser, nom_tag(QUOTE)),
+      nom_opt(nom_take(1_usize)),
+    ),
+    to_redis_value,
+  )(input)
+}
+
+fn d_parse_args(input: &[u8]) -> IResult<&[u8], Vec<RedisValue>, RedisParseError<&[u8]>> {
+  nom_many0(d_parse_arg)(input)
+}
+
+fn d_parse_frame(input: &[u8]) -> Result<Command, RedisParseError<&[u8]>> {
+  let (input, timestamp) = d_parse_timestamp(input)?;
+  let (input, db) = d_parse_db(input)?;
+  let (input, client) = d_parse_client(input)?;
+  let (input, command) = d_parse_command(input)?;
+  let (_, args) = d_parse_args(input)?;
+
+  Ok(Command {
+    timestamp,
+    db,
+    client,
+    command,
+    args,
+  })
+}
+
+#[cfg(feature = "network-logs")]
+fn log_frame(inner: &RefCount<RedisClientInner>, frame: &[u8]) {
+  if let Ok(s) = str::from_utf8(frame) {
+    _trace!(inner, "Monitor frame: {}", s);
+  } else {
+    _trace!(inner, "Monitor frame: {:?}", frame);
+  }
+}
+
+#[cfg(not(feature = "network-logs"))]
+fn log_frame(_: &RefCount<RedisClientInner>, _: &[u8]) {}
+
+pub fn parse(inner: &RefCount<RedisClientInner>, frame: Resp3Frame) -> Option<Command> {
+  let frame_bytes = match frame {
+    Resp3Frame::SimpleString { ref data, .. } => data,
+    Resp3Frame::BlobString { ref data, .. } => data,
+    Resp3Frame::VerbatimString { ref data, .. } => data,
+    _ => {
+      _warn!(inner, "Unexpected frame type on monitor stream: {:?}", frame.kind());
+      return None;
+    },
+  };
+
+  log_frame(inner, frame_bytes);
+  d_parse_frame(frame_bytes).ok()
+}
+
+#[cfg(test)]
+mod tests {
+  use crate::monitor::{parser::d_parse_frame, Command};
+
+  #[test]
+  fn should_parse_frame_without_spaces_or_quotes() {
+    let input = "1631469940.785623 [0 127.0.0.1:46998] \"SET\" \"foo\" \"2\"";
+    let expected = Command {
+      timestamp: 1631469940.785623,
+      db:        0,
+      client:    "127.0.0.1:46998".into(),
+      command:   "SET".into(),
+      args:      vec!["foo".into(), "2".into()],
+    };
+
+    let actual = d_parse_frame(input.as_bytes()).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_frame_with_inner_spaces() {
+    let input = "1631469940.785623 [0 127.0.0.1:46998] \"SET\" \"foo bar\" \"2\"";
+    let expected = Command {
+      timestamp: 1631469940.785623,
+      db:        0,
+      client:    "127.0.0.1:46998".into(),
+      command:   "SET".into(),
+      args:      vec!["foo bar".into(), "2".into()],
+    };
+
+    let actual = d_parse_frame(input.as_bytes()).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_frame_with_inner_quotes() {
+    let input = "1631475365.563304 [0 127.0.0.1:47438] \"SET\" \"foo\" \"0 - \\\"abc\\\"\" \"1 - \\\"def\\\"\" \"2 \
+                 - \\\"ghi\\\" \\\"jkl\\\"\"";
+    let expected = Command {
+      timestamp: 1631475365.563304,
+      db:        0,
+      client:    "127.0.0.1:47438".into(),
+      command:   "SET".into(),
+      args:      vec![
+        "foo".into(),
+        "0 - \\\"abc\\\"".into(),
+        "1 - \\\"def\\\"".into(),
+        "2 - \\\"ghi\\\" \\\"jkl\\\"".into(),
+      ],
+    };
+
+    let actual = d_parse_frame(input.as_bytes()).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_frame_without_args() {
+    let input = "1631469940.785623 [0 127.0.0.1:46998] \"KEYS\"";
+    let expected = Command {
+      timestamp: 1631469940.785623,
+      db:        0,
+      client:    "127.0.0.1:46998".into(),
+      command:   "KEYS".into(),
+      args:      vec![],
+    };
+
+    let actual = d_parse_frame(input.as_bytes()).unwrap();
+    assert_eq!(actual, expected);
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/monitor/utils.rs.html b/doc/src/fred/monitor/utils.rs.html new file mode 100644 index 00000000..90a38126 --- /dev/null +++ b/doc/src/fred/monitor/utils.rs.html @@ -0,0 +1,319 @@ +utils.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  modules::inner::RedisClientInner,
+  monitor::{parser, Command},
+  protocol::{
+    codec::RedisCodec,
+    command::{RedisCommand, RedisCommandKind},
+    connection::{self, ConnectionKind, RedisTransport},
+    types::ProtocolFrame,
+    utils as protocol_utils,
+  },
+  runtime::{rx_stream, spawn, unbounded_channel, RefCount, UnboundedSender},
+  types::{ConnectionConfig, PerformanceConfig, RedisConfig, ServerConfig},
+};
+use futures::stream::{Stream, StreamExt};
+use tokio::io::{AsyncRead, AsyncWrite};
+use tokio_util::codec::Framed;
+
+#[cfg(not(feature = "glommio"))]
+use tokio_stream::wrappers::UnboundedReceiverStream;
+
+#[cfg(all(feature = "blocking-encoding", not(feature = "glommio")))]
+async fn handle_monitor_frame(
+  inner: &RefCount<RedisClientInner>,
+  frame: Result<ProtocolFrame, RedisError>,
+) -> Option<Command> {
+  let frame = match frame {
+    Ok(frame) => frame.into_resp3(),
+    Err(e) => {
+      _error!(inner, "Error on monitor stream: {:?}", e);
+      return None;
+    },
+  };
+  let frame_size = frame.encode_len();
+
+  if frame_size >= inner.with_perf_config(|c| c.blocking_encode_threshold) {
+    // since this isn't called from the Encoder/Decoder trait we can use spawn_blocking here
+    _trace!(
+      inner,
+      "Parsing monitor frame with blocking task with size {}",
+      frame_size
+    );
+
+    let inner = inner.clone();
+    tokio::task::spawn_blocking(move || parser::parse(&inner, frame))
+      .await
+      .ok()
+      .flatten()
+  } else {
+    parser::parse(inner, frame)
+  }
+}
+
+#[cfg(any(not(feature = "blocking-encoding"), feature = "glommio"))]
+async fn handle_monitor_frame(
+  inner: &RefCount<RedisClientInner>,
+  frame: Result<ProtocolFrame, RedisError>,
+) -> Option<Command> {
+  let frame = match frame {
+    Ok(frame) => frame.into_resp3(),
+    Err(e) => {
+      _error!(inner, "Error on monitor stream: {:?}", e);
+      return None;
+    },
+  };
+
+  parser::parse(inner, frame)
+}
+
+async fn send_monitor_command(
+  inner: &RefCount<RedisClientInner>,
+  mut connection: RedisTransport,
+) -> Result<RedisTransport, RedisError> {
+  _debug!(inner, "Sending MONITOR command.");
+
+  let command = RedisCommand::new(RedisCommandKind::Monitor, vec![]);
+  let frame = connection.request_response(command, inner.is_resp3()).await?;
+
+  _trace!(inner, "Recv MONITOR response: {:?}", frame);
+  let response = protocol_utils::frame_to_results(frame)?;
+  protocol_utils::expect_ok(&response)?;
+  Ok(connection)
+}
+
+async fn forward_results<T>(
+  inner: &RefCount<RedisClientInner>,
+  tx: UnboundedSender<Command>,
+  mut framed: Framed<T, RedisCodec>,
+) where
+  T: AsyncRead + AsyncWrite + Unpin + 'static,
+{
+  while let Some(frame) = framed.next().await {
+    if let Some(command) = handle_monitor_frame(inner, frame).await {
+      if let Err(_) = tx.send(command) {
+        _warn!(inner, "Stopping monitor stream.");
+        return;
+      }
+    } else {
+      _debug!(inner, "Skipping invalid monitor frame.");
+    }
+  }
+}
+
+async fn process_stream(
+  inner: &RefCount<RedisClientInner>,
+  tx: UnboundedSender<Command>,
+  connection: RedisTransport,
+) {
+  _debug!(inner, "Starting monitor stream processing...");
+
+  match connection.transport {
+    ConnectionKind::Tcp(framed) => forward_results(inner, tx, framed).await,
+    #[cfg(feature = "enable-rustls")]
+    ConnectionKind::Rustls(framed) => forward_results(inner, tx, framed).await,
+    #[cfg(feature = "enable-native-tls")]
+    ConnectionKind::NativeTls(framed) => forward_results(inner, tx, framed).await,
+    #[cfg(feature = "unix-sockets")]
+    ConnectionKind::Unix(framed) => forward_results(inner, tx, framed).await,
+  };
+
+  _warn!(inner, "Stopping monitor stream.");
+}
+
+pub async fn start(config: RedisConfig) -> Result<impl Stream<Item = Command>, RedisError> {
+  let perf = PerformanceConfig {
+    auto_pipeline: false,
+    ..Default::default()
+  };
+  let connection = ConnectionConfig::default();
+  let server = match config.server {
+    ServerConfig::Centralized { ref server } => server.clone(),
+    _ => {
+      return Err(RedisError::new(
+        RedisErrorKind::Config,
+        "Expected centralized server config.",
+      ))
+    },
+  };
+
+  let inner = RedisClientInner::new(config, perf, connection, None);
+  let mut connection = connection::create(&inner, &server, None).await?;
+  connection.setup(&inner, None).await?;
+  let connection = send_monitor_command(&inner, connection).await?;
+
+  // there isn't really a mechanism to surface backpressure to the server for the MONITOR stream, so we use a
+  // background task with a channel to process the frames so that the server can keep sending data even if the
+  // stream consumer slows down processing the frames.
+  let (tx, rx) = unbounded_channel();
+  #[cfg(feature = "glommio")]
+  let tx = tx.into();
+  spawn(async move {
+    process_stream(&inner, tx, connection).await;
+  });
+
+  #[cfg(feature = "glommio")]
+  return Ok(rx_stream(rx));
+  #[cfg(not(feature = "glommio"))]
+  return Ok(UnboundedReceiverStream::new(rx));
+}
+
\ No newline at end of file diff --git a/doc/src/fred/protocol/cluster.rs.html b/doc/src/fred/protocol/cluster.rs.html new file mode 100644 index 00000000..19cbf499 --- /dev/null +++ b/doc/src/fred/protocol/cluster.rs.html @@ -0,0 +1,2107 @@ +cluster.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  modules::inner::RedisClientInner,
+  protocol::types::{Server, SlotRange},
+  runtime::RefCount,
+  types::RedisValue,
+  utils,
+};
+use bytes_utils::Str;
+use std::{collections::HashMap, net::IpAddr, str::FromStr};
+
+#[cfg(any(
+  feature = "enable-native-tls",
+  feature = "enable-rustls",
+  feature = "enable-rustls-ring"
+))]
+use crate::protocol::tls::TlsHostMapping;
+
+fn parse_as_u16(value: RedisValue) -> Result<u16, RedisError> {
+  match value {
+    RedisValue::Integer(i) => {
+      if i < 0 || i > u16::MAX as i64 {
+        Err(RedisError::new(RedisErrorKind::Parse, "Invalid cluster slot integer."))
+      } else {
+        Ok(i as u16)
+      }
+    },
+    RedisValue::String(s) => s.parse::<u16>().map_err(|e| e.into()),
+    _ => Err(RedisError::new(
+      RedisErrorKind::Parse,
+      "Could not parse value as cluster slot.",
+    )),
+  }
+}
+
+fn is_ip_address(value: &Str) -> bool {
+  IpAddr::from_str(value).is_ok()
+}
+
+fn check_metadata_hostname(data: &HashMap<Str, Str>) -> Option<&Str> {
+  data
+    .get(&utils::static_str("hostname"))
+    .filter(|&hostname| !hostname.is_empty())
+}
+
+/// Find the correct hostname for the server, preferring hostnames over IP addresses for TLS purposes.
+///
+/// The format of `server` is `[<preferred host>|null, <port>, <id>, <metadata>]`. However, in Redis <=6 the
+/// `metadata` value will not be present.
+///
+/// The implementation here does the following:
+/// 1. If `server[0]` is a hostname then use that.
+/// 2. If `server[0]` is an IP address, then check `server[3]` for a "hostname" metadata field and use that if found.
+///    Otherwise use the IP address in `server[0]`.
+/// 3. If `server[0]` is null, but `server[3]` has a "hostname" metadata field, then use the metadata field. Otherwise
+///    use `default_host`.
+///
+/// The `default_host` is the host that returned the `CLUSTER SLOTS` response.
+///
+/// <https://redis.io/commands/cluster-slots/#nested-result-array>
+fn parse_cluster_slot_hostname(server: &[RedisValue], default_host: &Str) -> Result<Str, RedisError> {
+  if server.is_empty() {
+    return Err(RedisError::new(
+      RedisErrorKind::Protocol,
+      "Invalid CLUSTER SLOTS server block.",
+    ));
+  }
+  let should_parse_metadata = server.len() >= 4 && !server[3].is_null() && server[3].array_len().unwrap_or(0) > 0;
+
+  let metadata: HashMap<Str, Str> = if should_parse_metadata {
+    // all the variants with data on the heap are ref counted (`Bytes`, `Str`, etc)
+    server[3].clone().convert()?
+  } else {
+    HashMap::new()
+  };
+  if server[0].is_null() {
+    // option 3
+    Ok(check_metadata_hostname(&metadata).unwrap_or(default_host).clone())
+  } else {
+    let preferred_host = match server[0].clone().convert::<Str>() {
+      Ok(host) => host,
+      Err(_) => {
+        return Err(RedisError::new(
+          RedisErrorKind::Protocol,
+          "Invalid CLUSTER SLOTS server block hostname.",
+        ))
+      },
+    };
+
+    if is_ip_address(&preferred_host) {
+      // option 2
+      Ok(check_metadata_hostname(&metadata).unwrap_or(&preferred_host).clone())
+    } else {
+      // option 1
+      Ok(preferred_host)
+    }
+  }
+}
+
+/// Read the node block with format `<hostname>|null, <port>, <id>, [metadata]`
+fn parse_node_block(data: &[RedisValue], default_host: &Str) -> Option<(Str, u16, Str, Str)> {
+  if data.len() < 3 {
+    return None;
+  }
+
+  let hostname = match parse_cluster_slot_hostname(data, default_host) {
+    Ok(host) => host,
+    Err(_) => return None,
+  };
+  let port: u16 = match parse_as_u16(data[1].clone()) {
+    Ok(port) => port,
+    Err(_) => return None,
+  };
+  let primary = Str::from(format!("{}:{}", hostname, port));
+  let id = data[2].as_bytes_str()?;
+
+  Some((hostname, port, primary, id))
+}
+
+/// Parse the optional trailing replica nodes in each `CLUSTER SLOTS` slot range block.
+#[cfg(feature = "replicas")]
+fn parse_cluster_slot_replica_nodes(slot_range: Vec<RedisValue>, default_host: &Str) -> Vec<Server> {
+  slot_range
+    .into_iter()
+    .filter_map(|value| {
+      let server_block: Vec<RedisValue> = match value.convert() {
+        Ok(v) => v,
+        Err(_) => {
+          warn!("Skip replica CLUSTER SLOTS block from {}", default_host);
+          return None;
+        },
+      };
+
+      let (host, port) = match parse_node_block(&server_block, default_host) {
+        Some((h, p, _, _)) => (h, p),
+        None => {
+          warn!("Skip replica CLUSTER SLOTS block from {}", default_host);
+          return None;
+        },
+      };
+
+      Some(Server {
+        host,
+        port,
+        #[cfg(any(
+          feature = "enable-native-tls",
+          feature = "enable-rustls",
+          feature = "enable-rustls-ring"
+        ))]
+        tls_server_name: None,
+      })
+    })
+    .collect()
+}
+
+/// Parse the cluster slot range and associated server blocks.
+fn parse_cluster_slot_nodes(mut slot_range: Vec<RedisValue>, default_host: &Str) -> Result<SlotRange, RedisError> {
+  if slot_range.len() < 3 {
+    return Err(RedisError::new(
+      RedisErrorKind::Protocol,
+      "Invalid CLUSTER SLOTS response.",
+    ));
+  }
+  slot_range.reverse();
+  // length checked above
+  let start = parse_as_u16(slot_range.pop().unwrap())?;
+  let end = parse_as_u16(slot_range.pop().unwrap())?;
+
+  // the third value is the primary node, following values are optional replica nodes
+  // length checked above. format is `<hostname>|null, <port>, <id>, [metadata]`
+  let server_block: Vec<RedisValue> = slot_range.pop().unwrap().convert()?;
+  let (host, port, id) = match parse_node_block(&server_block, default_host) {
+    Some((h, p, _, i)) => (h, p, i),
+    None => {
+      trace!("Failed to parse CLUSTER SLOTS response: {:?}", server_block);
+      return Err(RedisError::new(
+        RedisErrorKind::Cluster,
+        "Invalid CLUSTER SLOTS response.",
+      ));
+    },
+  };
+
+  Ok(SlotRange {
+    start,
+    end,
+    id,
+    primary: Server {
+      host,
+      port,
+      #[cfg(any(
+        feature = "enable-native-tls",
+        feature = "enable-rustls",
+        feature = "enable-rustls-ring"
+      ))]
+      tls_server_name: None,
+    },
+    #[cfg(feature = "replicas")]
+    replicas: parse_cluster_slot_replica_nodes(slot_range, default_host),
+  })
+}
+
+/// Parse the entire CLUSTER SLOTS response with the provided `default_host` of the connection used to send the
+/// command.
+pub fn parse_cluster_slots(frame: RedisValue, default_host: &Str) -> Result<Vec<SlotRange>, RedisError> {
+  let slot_ranges: Vec<Vec<RedisValue>> = frame.convert()?;
+  let mut out: Vec<SlotRange> = Vec::with_capacity(slot_ranges.len());
+
+  for slot_range in slot_ranges.into_iter() {
+    out.push(parse_cluster_slot_nodes(slot_range, default_host)?);
+  }
+
+  out.shrink_to_fit();
+  Ok(out)
+}
+
+#[cfg(any(
+  feature = "enable-rustls",
+  feature = "enable-native-tls",
+  feature = "enable-rustls-ring"
+))]
+fn replace_tls_server_names(policy: &TlsHostMapping, ranges: &mut [SlotRange], default_host: &Str) {
+  for slot_range in ranges.iter_mut() {
+    slot_range.primary.set_tls_server_name(policy, default_host);
+
+    #[cfg(feature = "replicas")]
+    for server in slot_range.replicas.iter_mut() {
+      server.set_tls_server_name(policy, default_host);
+    }
+  }
+}
+
+/// Modify the `CLUSTER SLOTS` command according to the hostname mapping policy in the `TlsHostMapping`.
+#[cfg(any(
+  feature = "enable-rustls",
+  feature = "enable-native-tls",
+  feature = "enable-rustls-ring"
+))]
+pub fn modify_cluster_slot_hostnames(
+  inner: &RefCount<RedisClientInner>,
+  ranges: &mut [SlotRange],
+  default_host: &Str,
+) {
+  let policy = match inner.config.tls {
+    Some(ref config) => &config.hostnames,
+    None => {
+      _trace!(inner, "Skip modifying TLS hostnames.");
+      return;
+    },
+  };
+  if *policy == TlsHostMapping::None {
+    _trace!(inner, "Skip modifying TLS hostnames.");
+    return;
+  }
+
+  replace_tls_server_names(policy, ranges, default_host);
+}
+
+#[cfg(not(any(
+  feature = "enable-rustls",
+  feature = "enable-native-tls",
+  feature = "enable-rustls-ring"
+)))]
+pub fn modify_cluster_slot_hostnames(inner: &RefCount<RedisClientInner>, _: &mut Vec<SlotRange>, _: &Str) {
+  _trace!(inner, "Skip modifying TLS hostnames.")
+}
+
+#[cfg(test)]
+mod tests {
+  use super::*;
+  #[cfg(any(
+    feature = "enable-native-tls",
+    feature = "enable-rustls",
+    feature = "enable-rustls-ring"
+  ))]
+  use crate::protocol::tls::{HostMapping, TlsHostMapping};
+  use crate::protocol::types::SlotRange;
+
+  #[cfg(any(
+    feature = "enable-native-tls",
+    feature = "enable-rustls",
+    feature = "enable-rustls-ring"
+  ))]
+  #[derive(Debug)]
+  struct FakeHostMapper;
+
+  #[cfg(any(
+    feature = "enable-native-tls",
+    feature = "enable-rustls",
+    feature = "enable-rustls-ring"
+  ))]
+  impl HostMapping for FakeHostMapper {
+    fn map(&self, _: &IpAddr, _: &str) -> Option<String> {
+      Some("foobarbaz".into())
+    }
+  }
+
+  fn fake_cluster_slots_without_metadata() -> RedisValue {
+    let first_slot_range = RedisValue::Array(vec![
+      0.into(),
+      5460.into(),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30001.into(),
+        "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(),
+      ]),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30004.into(),
+        "821d8ca00d7ccf931ed3ffc7e3db0599d2271abf".into(),
+      ]),
+    ]);
+    let second_slot_range = RedisValue::Array(vec![
+      5461.into(),
+      10922.into(),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30002.into(),
+        "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(),
+      ]),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30005.into(),
+        "faadb3eb99009de4ab72ad6b6ed87634c7ee410f".into(),
+      ]),
+    ]);
+    let third_slot_range = RedisValue::Array(vec![
+      10923.into(),
+      16383.into(),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30003.into(),
+        "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(),
+      ]),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30006.into(),
+        "58e6e48d41228013e5d9c1c37c5060693925e97e".into(),
+      ]),
+    ]);
+
+    RedisValue::Array(vec![first_slot_range, second_slot_range, third_slot_range])
+  }
+
+  fn fake_cluster_slots_with_metadata() -> RedisValue {
+    let first_slot_range = RedisValue::Array(vec![
+      0.into(),
+      5460.into(),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30001.into(),
+        "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(),
+        RedisValue::Array(vec!["hostname".into(), "host-1.redis.example.com".into()]),
+      ]),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30004.into(),
+        "821d8ca00d7ccf931ed3ffc7e3db0599d2271abf".into(),
+        RedisValue::Array(vec!["hostname".into(), "host-2.redis.example.com".into()]),
+      ]),
+    ]);
+    let second_slot_range = RedisValue::Array(vec![
+      5461.into(),
+      10922.into(),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30002.into(),
+        "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(),
+        RedisValue::Array(vec!["hostname".into(), "host-3.redis.example.com".into()]),
+      ]),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30005.into(),
+        "faadb3eb99009de4ab72ad6b6ed87634c7ee410f".into(),
+        RedisValue::Array(vec!["hostname".into(), "host-4.redis.example.com".into()]),
+      ]),
+    ]);
+    let third_slot_range = RedisValue::Array(vec![
+      10923.into(),
+      16383.into(),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30003.into(),
+        "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(),
+        RedisValue::Array(vec!["hostname".into(), "host-5.redis.example.com".into()]),
+      ]),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30006.into(),
+        "58e6e48d41228013e5d9c1c37c5060693925e97e".into(),
+        RedisValue::Array(vec!["hostname".into(), "host-6.redis.example.com".into()]),
+      ]),
+    ]);
+
+    RedisValue::Array(vec![first_slot_range, second_slot_range, third_slot_range])
+  }
+
+  #[test]
+  #[cfg(any(
+    feature = "enable-native-tls",
+    feature = "enable-rustls",
+    feature = "enable-rustls-ring"
+  ))]
+  fn should_modify_cluster_slot_hostnames_default_host_without_metadata() {
+    let policy = TlsHostMapping::DefaultHost;
+    let fake_data = fake_cluster_slots_without_metadata();
+    let mut ranges = parse_cluster_slots(fake_data, &Str::from("default-host")).unwrap();
+    replace_tls_server_names(&policy, &mut ranges, &Str::from("default-host"));
+
+    for slot_range in ranges.iter() {
+      assert_ne!(slot_range.primary.host, "default-host");
+      assert_eq!(slot_range.primary.tls_server_name, Some("default-host".into()));
+
+      #[cfg(feature = "replicas")]
+      for replica in slot_range.replicas.iter() {
+        assert_ne!(replica.host, "default-host");
+        assert_eq!(replica.tls_server_name, Some("default-host".into()));
+      }
+    }
+  }
+
+  #[test]
+  #[cfg(any(
+    feature = "enable-native-tls",
+    feature = "enable-rustls",
+    feature = "enable-rustls-ring"
+  ))]
+  fn should_not_modify_cluster_slot_hostnames_default_host_with_metadata() {
+    let policy = TlsHostMapping::DefaultHost;
+    let fake_data = fake_cluster_slots_with_metadata();
+    let mut ranges = parse_cluster_slots(fake_data, &Str::from("default-host")).unwrap();
+    replace_tls_server_names(&policy, &mut ranges, &Str::from("default-host"));
+
+    for slot_range in ranges.iter() {
+      assert_ne!(slot_range.primary.host, "default-host");
+      // since there's a metadata hostname then expect that instead of the default host
+      assert_ne!(slot_range.primary.tls_server_name, Some("default-host".into()));
+
+      #[cfg(feature = "replicas")]
+      for replica in slot_range.replicas.iter() {
+        assert_ne!(replica.host, "default-host");
+        assert_ne!(replica.tls_server_name, Some("default-host".into()));
+      }
+    }
+  }
+
+  #[test]
+  #[cfg(any(
+    feature = "enable-native-tls",
+    feature = "enable-rustls",
+    feature = "enable-rustls-ring"
+  ))]
+  fn should_modify_cluster_slot_hostnames_custom() {
+    let policy = TlsHostMapping::Custom(RefCount::new(FakeHostMapper));
+    let fake_data = fake_cluster_slots_without_metadata();
+    let mut ranges = parse_cluster_slots(fake_data, &Str::from("default-host")).unwrap();
+    replace_tls_server_names(&policy, &mut ranges, &Str::from("default-host"));
+
+    for slot_range in ranges.iter() {
+      assert_ne!(slot_range.primary.host, "default-host");
+      assert_eq!(slot_range.primary.tls_server_name, Some("foobarbaz".into()));
+
+      #[cfg(feature = "replicas")]
+      for replica in slot_range.replicas.iter() {
+        assert_ne!(replica.host, "default-host");
+        assert_eq!(replica.tls_server_name, Some("foobarbaz".into()));
+      }
+    }
+  }
+
+  #[test]
+  fn should_parse_cluster_slots_example_metadata_hostnames() {
+    let input = fake_cluster_slots_with_metadata();
+
+    let actual = parse_cluster_slots(input, &Str::from("bad-host")).expect("Failed to parse input");
+    let expected = vec![
+      SlotRange {
+        start:                                 0,
+        end:                                   5460,
+        primary:                               Server {
+          host:            "host-1.redis.example.com".into(),
+          port:            30001,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "host-2.redis.example.com".into(),
+          port:            30004,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+      SlotRange {
+        start:                                 5461,
+        end:                                   10922,
+        primary:                               Server {
+          host:            "host-3.redis.example.com".into(),
+          port:            30002,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "host-4.redis.example.com".into(),
+          port:            30005,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+      SlotRange {
+        start:                                 10923,
+        end:                                   16383,
+        primary:                               Server {
+          host:            "host-5.redis.example.com".into(),
+          port:            30003,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "host-6.redis.example.com".into(),
+          port:            30006,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+    ];
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_cluster_slots_example_no_metadata() {
+    let input = fake_cluster_slots_without_metadata();
+
+    let actual = parse_cluster_slots(input, &Str::from("bad-host")).expect("Failed to parse input");
+    let expected = vec![
+      SlotRange {
+        start:                                 0,
+        end:                                   5460,
+        primary:                               Server {
+          host:            "127.0.0.1".into(),
+          port:            30001,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "127.0.0.1".into(),
+          port:            30004,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+      SlotRange {
+        start:                                 5461,
+        end:                                   10922,
+        primary:                               Server {
+          host:            "127.0.0.1".into(),
+          port:            30002,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "127.0.0.1".into(),
+          port:            30005,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+      SlotRange {
+        start:                                 10923,
+        end:                                   16383,
+        primary:                               Server {
+          host:            "127.0.0.1".into(),
+          port:            30003,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "127.0.0.1".into(),
+          port:            30006,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+    ];
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_cluster_slots_example_empty_metadata() {
+    let first_slot_range = RedisValue::Array(vec![
+      0.into(),
+      5460.into(),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30001.into(),
+        "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(),
+        RedisValue::Array(vec![]),
+      ]),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30004.into(),
+        "821d8ca00d7ccf931ed3ffc7e3db0599d2271abf".into(),
+        RedisValue::Array(vec![]),
+      ]),
+    ]);
+    let second_slot_range = RedisValue::Array(vec![
+      5461.into(),
+      10922.into(),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30002.into(),
+        "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(),
+        RedisValue::Array(vec![]),
+      ]),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30005.into(),
+        "faadb3eb99009de4ab72ad6b6ed87634c7ee410f".into(),
+        RedisValue::Array(vec![]),
+      ]),
+    ]);
+    let third_slot_range = RedisValue::Array(vec![
+      10923.into(),
+      16383.into(),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30003.into(),
+        "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(),
+        RedisValue::Array(vec![]),
+      ]),
+      RedisValue::Array(vec![
+        "127.0.0.1".into(),
+        30006.into(),
+        "58e6e48d41228013e5d9c1c37c5060693925e97e".into(),
+        RedisValue::Array(vec![]),
+      ]),
+    ]);
+    let input = RedisValue::Array(vec![first_slot_range, second_slot_range, third_slot_range]);
+
+    let actual = parse_cluster_slots(input, &Str::from("bad-host")).expect("Failed to parse input");
+    let expected = vec![
+      SlotRange {
+        start:                                 0,
+        end:                                   5460,
+        primary:                               Server {
+          host:            "127.0.0.1".into(),
+          port:            30001,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "127.0.0.1".into(),
+          port:            30004,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+      SlotRange {
+        start:                                 5461,
+        end:                                   10922,
+        primary:                               Server {
+          host:            "127.0.0.1".into(),
+          port:            30002,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "127.0.0.1".into(),
+          port:            30005,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+      SlotRange {
+        start:                                 10923,
+        end:                                   16383,
+        primary:                               Server {
+          host:            "127.0.0.1".into(),
+          port:            30003,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "127.0.0.1".into(),
+          port:            30006,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+    ];
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_cluster_slots_example_null_hostname() {
+    let first_slot_range = RedisValue::Array(vec![
+      0.into(),
+      5460.into(),
+      RedisValue::Array(vec![
+        RedisValue::Null,
+        30001.into(),
+        "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(),
+        RedisValue::Array(vec![]),
+      ]),
+      RedisValue::Array(vec![
+        RedisValue::Null,
+        30004.into(),
+        "821d8ca00d7ccf931ed3ffc7e3db0599d2271abf".into(),
+        RedisValue::Array(vec![]),
+      ]),
+    ]);
+    let second_slot_range = RedisValue::Array(vec![
+      5461.into(),
+      10922.into(),
+      RedisValue::Array(vec![
+        RedisValue::Null,
+        30002.into(),
+        "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(),
+        RedisValue::Array(vec![]),
+      ]),
+      RedisValue::Array(vec![
+        RedisValue::Null,
+        30005.into(),
+        "faadb3eb99009de4ab72ad6b6ed87634c7ee410f".into(),
+        RedisValue::Array(vec![]),
+      ]),
+    ]);
+    let third_slot_range = RedisValue::Array(vec![
+      10923.into(),
+      16383.into(),
+      RedisValue::Array(vec![
+        RedisValue::Null,
+        30003.into(),
+        "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(),
+        RedisValue::Array(vec![]),
+      ]),
+      RedisValue::Array(vec![
+        RedisValue::Null,
+        30006.into(),
+        "58e6e48d41228013e5d9c1c37c5060693925e97e".into(),
+        RedisValue::Array(vec![]),
+      ]),
+    ]);
+    let input = RedisValue::Array(vec![first_slot_range, second_slot_range, third_slot_range]);
+
+    let actual = parse_cluster_slots(input, &Str::from("fake-host")).expect("Failed to parse input");
+    let expected = vec![
+      SlotRange {
+        start:                                 0,
+        end:                                   5460,
+        primary:                               Server {
+          host:            "fake-host".into(),
+          port:            30001,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "fake-host".into(),
+          port:            30004,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+      SlotRange {
+        start:                                 5461,
+        end:                                   10922,
+        primary:                               Server {
+          host:            "fake-host".into(),
+          port:            30002,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "fake-host".into(),
+          port:            30005,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+      SlotRange {
+        start:                                 10923,
+        end:                                   16383,
+        primary:                               Server {
+          host:            "fake-host".into(),
+          port:            30003,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "fake-host".into(),
+          port:            30006,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+    ];
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_cluster_slots_example_empty_hostname() {
+    let first_slot_range = RedisValue::Array(vec![
+      0.into(),
+      5460.into(),
+      RedisValue::Array(vec![
+        RedisValue::Null,
+        30001.into(),
+        "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(),
+        RedisValue::Array(vec!["hostname".into(), "".into()]),
+      ]),
+      RedisValue::Array(vec![
+        RedisValue::Null,
+        30004.into(),
+        "821d8ca00d7ccf931ed3ffc7e3db0599d2271abf".into(),
+        RedisValue::Array(vec!["hostname".into(), "".into()]),
+      ]),
+    ]);
+    let second_slot_range = RedisValue::Array(vec![
+      5461.into(),
+      10922.into(),
+      RedisValue::Array(vec![
+        RedisValue::Null,
+        30002.into(),
+        "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(),
+        RedisValue::Array(vec!["hostname".into(), "".into()]),
+      ]),
+      RedisValue::Array(vec![
+        RedisValue::Null,
+        30005.into(),
+        "faadb3eb99009de4ab72ad6b6ed87634c7ee410f".into(),
+        RedisValue::Array(vec!["hostname".into(), "".into()]),
+      ]),
+    ]);
+    let third_slot_range = RedisValue::Array(vec![
+      10923.into(),
+      16383.into(),
+      RedisValue::Array(vec![
+        RedisValue::Null,
+        30003.into(),
+        "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(),
+        RedisValue::Array(vec!["hostname".into(), "".into()]),
+      ]),
+      RedisValue::Array(vec![
+        RedisValue::Null,
+        30006.into(),
+        "58e6e48d41228013e5d9c1c37c5060693925e97e".into(),
+        RedisValue::Array(vec!["hostname".into(), "".into()]),
+      ]),
+    ]);
+    let input = RedisValue::Array(vec![first_slot_range, second_slot_range, third_slot_range]);
+
+    let actual = parse_cluster_slots(input, &Str::from("fake-host")).expect("Failed to parse input");
+    let expected = vec![
+      SlotRange {
+        start:                                 0,
+        end:                                   5460,
+        primary:                               Server {
+          host:            "fake-host".into(),
+          port:            30001,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "fake-host".into(),
+          port:            30004,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+      SlotRange {
+        start:                                 5461,
+        end:                                   10922,
+        primary:                               Server {
+          host:            "fake-host".into(),
+          port:            30002,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "fake-host".into(),
+          port:            30005,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+      SlotRange {
+        start:                                 10923,
+        end:                                   16383,
+        primary:                               Server {
+          host:            "fake-host".into(),
+          port:            30003,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        },
+        id:                                    "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(),
+        #[cfg(feature = "replicas")]
+        replicas:                              vec![Server {
+          host:            "fake-host".into(),
+          port:            30006,
+          #[cfg(any(
+            feature = "enable-native-tls",
+            feature = "enable-rustls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        }],
+      },
+    ];
+    assert_eq!(actual, expected);
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/protocol/codec.rs.html b/doc/src/fred/protocol/codec.rs.html new file mode 100644 index 00000000..31ac88cb --- /dev/null +++ b/doc/src/fred/protocol/codec.rs.html @@ -0,0 +1,489 @@ +codec.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  modules::inner::RedisClientInner,
+  protocol::{
+    types::{ProtocolFrame, Server},
+    utils as protocol_utils,
+  },
+  runtime::{AtomicBool, RefCount},
+  utils,
+};
+use bytes::BytesMut;
+use bytes_utils::Str;
+use redis_protocol::{
+  resp2::{
+    decode::decode_bytes_mut as resp2_decode,
+    encode::extend_encode as resp2_encode,
+    types::BytesFrame as Resp2Frame,
+  },
+  resp3::{
+    decode::streaming::decode_bytes_mut as resp3_decode,
+    encode::complete::extend_encode as resp3_encode,
+    types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame, StreamedFrame},
+  },
+};
+use tokio_util::codec::{Decoder, Encoder};
+
+#[cfg(feature = "metrics")]
+use crate::modules::metrics::MovingStats;
+#[cfg(feature = "metrics")]
+use crate::runtime::RwLock;
+
+#[cfg(not(feature = "network-logs"))]
+fn log_resp2_frame(_: &str, _: &Resp2Frame, _: bool) {}
+#[cfg(not(feature = "network-logs"))]
+fn log_resp3_frame(_: &str, _: &Resp3Frame, _: bool) {}
+#[cfg(feature = "network-logs")]
+pub use crate::protocol::debug::log_resp2_frame;
+#[cfg(feature = "network-logs")]
+pub use crate::protocol::debug::log_resp3_frame;
+
+#[cfg(feature = "metrics")]
+fn sample_stats(codec: &RedisCodec, decode: bool, value: i64) {
+  if decode {
+    codec.res_size_stats.write().sample(value);
+  } else {
+    codec.req_size_stats.write().sample(value);
+  }
+}
+
+#[cfg(not(feature = "metrics"))]
+fn sample_stats(_: &RedisCodec, _: bool, _: i64) {}
+
+fn resp2_encode_frame(codec: &RedisCodec, item: Resp2Frame, dst: &mut BytesMut) -> Result<(), RedisError> {
+  let offset = dst.len();
+
+  let res = resp2_encode(dst, &item)?;
+  let len = res.saturating_sub(offset);
+
+  trace!(
+    "{}: Encoded {} bytes to {}. Buffer len: {} (RESP2)",
+    codec.name,
+    len,
+    codec.server,
+    res
+  );
+  log_resp2_frame(&codec.name, &item, true);
+  sample_stats(codec, false, len as i64);
+
+  Ok(())
+}
+
+fn resp2_decode_frame(codec: &RedisCodec, src: &mut BytesMut) -> Result<Option<Resp2Frame>, RedisError> {
+  trace!(
+    "{}: Recv {} bytes from {} (RESP2).",
+    codec.name,
+    src.len(),
+    codec.server
+  );
+  if src.is_empty() {
+    return Ok(None);
+  }
+
+  if let Some((frame, amt, _)) = resp2_decode(src)? {
+    trace!("{}: Parsed {} bytes from {}", codec.name, amt, codec.server);
+    log_resp2_frame(&codec.name, &frame, false);
+    sample_stats(codec, true, amt as i64);
+
+    Ok(Some(protocol_utils::check_resp2_auth_error(codec, frame)))
+  } else {
+    Ok(None)
+  }
+}
+
+fn resp3_encode_frame(codec: &RedisCodec, item: Resp3Frame, dst: &mut BytesMut) -> Result<(), RedisError> {
+  let offset = dst.len();
+
+  let res = resp3_encode(dst, &item)?;
+  let len = res.saturating_sub(offset);
+
+  trace!(
+    "{}: Encoded {} bytes to {}. Buffer len: {} (RESP3)",
+    codec.name,
+    len,
+    codec.server,
+    res
+  );
+  log_resp3_frame(&codec.name, &item, true);
+  sample_stats(codec, false, len as i64);
+
+  Ok(())
+}
+
+fn resp3_decode_frame(codec: &mut RedisCodec, src: &mut BytesMut) -> Result<Option<Resp3Frame>, RedisError> {
+  trace!(
+    "{}: Recv {} bytes from {} (RESP3).",
+    codec.name,
+    src.len(),
+    codec.server
+  );
+  if src.is_empty() {
+    return Ok(None);
+  }
+
+  if let Some((frame, amt, _)) = resp3_decode(src)? {
+    sample_stats(codec, true, amt as i64);
+
+    if codec.streaming_state.is_some() && frame.is_streaming() {
+      return Err(RedisError::new(
+        RedisErrorKind::Protocol,
+        "Cannot start a stream while already inside a stream.",
+      ));
+    }
+
+    let result = if let Some(ref mut streamed_frame) = codec.streaming_state {
+      // we started receiving streamed data earlier
+      let frame = frame.into_complete_frame()?;
+      streamed_frame.add_frame(frame);
+
+      if streamed_frame.is_finished() {
+        let frame = streamed_frame.take()?;
+        trace!("{}: Ending {:?} stream", codec.name, frame.kind());
+        log_resp3_frame(&codec.name, &frame, false);
+        Some(frame)
+      } else {
+        trace!("{}: Continuing {:?} stream", codec.name, streamed_frame.kind);
+        None
+      }
+    } else {
+      // we're processing a complete frame or starting a new streamed frame
+      if frame.is_streaming() {
+        let frame = frame.into_streaming_frame()?;
+        trace!("{}: Starting {:?} stream", codec.name, frame.kind);
+        codec.streaming_state = Some(frame);
+        None
+      } else {
+        // we're not in the middle of a stream and we found a complete frame
+        let frame = frame.into_complete_frame()?;
+        log_resp3_frame(&codec.name, &frame, false);
+        Some(protocol_utils::check_resp3_auth_error(codec, frame))
+      }
+    };
+
+    if result.is_some() {
+      let _ = codec.streaming_state.take();
+    }
+    Ok(result)
+  } else {
+    Ok(None)
+  }
+}
+
+/// Attempt to decode with RESP2, and if that fails try once with RESP3.
+///
+/// This is useful when handling HELLO commands sent in the middle of a RESP2 command sequence.
+fn resp2_decode_with_fallback(
+  codec: &mut RedisCodec,
+  src: &mut BytesMut,
+) -> Result<Option<ProtocolFrame>, RedisError> {
+  let resp2_result = resp2_decode_frame(codec, src).map(|f| f.map(|f| f.into()));
+  if resp2_result.is_err() {
+    let resp3_result = resp3_decode_frame(codec, src).map(|f| f.map(|f| f.into()));
+    if resp3_result.is_ok() {
+      resp3_result
+    } else {
+      resp2_result
+    }
+  } else {
+    resp2_result
+  }
+}
+
+pub struct RedisCodec {
+  pub name:            Str,
+  pub server:          Server,
+  pub resp3:           RefCount<AtomicBool>,
+  pub streaming_state: Option<StreamedFrame<Resp3Frame>>,
+  #[cfg(feature = "metrics")]
+  pub req_size_stats:  RefCount<RwLock<MovingStats>>,
+  #[cfg(feature = "metrics")]
+  pub res_size_stats:  RefCount<RwLock<MovingStats>>,
+}
+
+impl RedisCodec {
+  pub fn new(inner: &RefCount<RedisClientInner>, server: &Server) -> Self {
+    RedisCodec {
+      server:                                     server.clone(),
+      name:                                       inner.id.clone(),
+      resp3:                                      inner.shared_resp3(),
+      streaming_state:                            None,
+      #[cfg(feature = "metrics")]
+      req_size_stats:                             inner.req_size_stats.clone(),
+      #[cfg(feature = "metrics")]
+      res_size_stats:                             inner.res_size_stats.clone(),
+    }
+  }
+
+  pub fn is_resp3(&self) -> bool {
+    utils::read_bool_atomic(&self.resp3)
+  }
+}
+
+impl Encoder<ProtocolFrame> for RedisCodec {
+  type Error = RedisError;
+
+  fn encode(&mut self, item: ProtocolFrame, dst: &mut BytesMut) -> Result<(), Self::Error> {
+    match item {
+      ProtocolFrame::Resp2(frame) => resp2_encode_frame(self, frame, dst),
+      ProtocolFrame::Resp3(frame) => resp3_encode_frame(self, frame, dst),
+    }
+  }
+}
+
+impl Decoder for RedisCodec {
+  type Error = RedisError;
+  type Item = ProtocolFrame;
+
+  fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
+    if self.is_resp3() {
+      resp3_decode_frame(self, src).map(|f| f.map(|f| f.into()))
+    } else {
+      resp2_decode_with_fallback(self, src)
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/protocol/command.rs.html b/doc/src/fred/protocol/command.rs.html new file mode 100644 index 00000000..b843405d --- /dev/null +++ b/doc/src/fred/protocol/command.rs.html @@ -0,0 +1,4571 @@ +command.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
+1265
+1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+1274
+1275
+1276
+1277
+1278
+1279
+1280
+1281
+1282
+1283
+1284
+1285
+1286
+1287
+1288
+1289
+1290
+1291
+1292
+1293
+1294
+1295
+1296
+1297
+1298
+1299
+1300
+1301
+1302
+1303
+1304
+1305
+1306
+1307
+1308
+1309
+1310
+1311
+1312
+1313
+1314
+1315
+1316
+1317
+1318
+1319
+1320
+1321
+1322
+1323
+1324
+1325
+1326
+1327
+1328
+1329
+1330
+1331
+1332
+1333
+1334
+1335
+1336
+1337
+1338
+1339
+1340
+1341
+1342
+1343
+1344
+1345
+1346
+1347
+1348
+1349
+1350
+1351
+1352
+1353
+1354
+1355
+1356
+1357
+1358
+1359
+1360
+1361
+1362
+1363
+1364
+1365
+1366
+1367
+1368
+1369
+1370
+1371
+1372
+1373
+1374
+1375
+1376
+1377
+1378
+1379
+1380
+1381
+1382
+1383
+1384
+1385
+1386
+1387
+1388
+1389
+1390
+1391
+1392
+1393
+1394
+1395
+1396
+1397
+1398
+1399
+1400
+1401
+1402
+1403
+1404
+1405
+1406
+1407
+1408
+1409
+1410
+1411
+1412
+1413
+1414
+1415
+1416
+1417
+1418
+1419
+1420
+1421
+1422
+1423
+1424
+1425
+1426
+1427
+1428
+1429
+1430
+1431
+1432
+1433
+1434
+1435
+1436
+1437
+1438
+1439
+1440
+1441
+1442
+1443
+1444
+1445
+1446
+1447
+1448
+1449
+1450
+1451
+1452
+1453
+1454
+1455
+1456
+1457
+1458
+1459
+1460
+1461
+1462
+1463
+1464
+1465
+1466
+1467
+1468
+1469
+1470
+1471
+1472
+1473
+1474
+1475
+1476
+1477
+1478
+1479
+1480
+1481
+1482
+1483
+1484
+1485
+1486
+1487
+1488
+1489
+1490
+1491
+1492
+1493
+1494
+1495
+1496
+1497
+1498
+1499
+1500
+1501
+1502
+1503
+1504
+1505
+1506
+1507
+1508
+1509
+1510
+1511
+1512
+1513
+1514
+1515
+1516
+1517
+1518
+1519
+1520
+1521
+1522
+1523
+1524
+1525
+1526
+1527
+1528
+1529
+1530
+1531
+1532
+1533
+1534
+1535
+1536
+1537
+1538
+1539
+1540
+1541
+1542
+1543
+1544
+1545
+1546
+1547
+1548
+1549
+1550
+1551
+1552
+1553
+1554
+1555
+1556
+1557
+1558
+1559
+1560
+1561
+1562
+1563
+1564
+1565
+1566
+1567
+1568
+1569
+1570
+1571
+1572
+1573
+1574
+1575
+1576
+1577
+1578
+1579
+1580
+1581
+1582
+1583
+1584
+1585
+1586
+1587
+1588
+1589
+1590
+1591
+1592
+1593
+1594
+1595
+1596
+1597
+1598
+1599
+1600
+1601
+1602
+1603
+1604
+1605
+1606
+1607
+1608
+1609
+1610
+1611
+1612
+1613
+1614
+1615
+1616
+1617
+1618
+1619
+1620
+1621
+1622
+1623
+1624
+1625
+1626
+1627
+1628
+1629
+1630
+1631
+1632
+1633
+1634
+1635
+1636
+1637
+1638
+1639
+1640
+1641
+1642
+1643
+1644
+1645
+1646
+1647
+1648
+1649
+1650
+1651
+1652
+1653
+1654
+1655
+1656
+1657
+1658
+1659
+1660
+1661
+1662
+1663
+1664
+1665
+1666
+1667
+1668
+1669
+1670
+1671
+1672
+1673
+1674
+1675
+1676
+1677
+1678
+1679
+1680
+1681
+1682
+1683
+1684
+1685
+1686
+1687
+1688
+1689
+1690
+1691
+1692
+1693
+1694
+1695
+1696
+1697
+1698
+1699
+1700
+1701
+1702
+1703
+1704
+1705
+1706
+1707
+1708
+1709
+1710
+1711
+1712
+1713
+1714
+1715
+1716
+1717
+1718
+1719
+1720
+1721
+1722
+1723
+1724
+1725
+1726
+1727
+1728
+1729
+1730
+1731
+1732
+1733
+1734
+1735
+1736
+1737
+1738
+1739
+1740
+1741
+1742
+1743
+1744
+1745
+1746
+1747
+1748
+1749
+1750
+1751
+1752
+1753
+1754
+1755
+1756
+1757
+1758
+1759
+1760
+1761
+1762
+1763
+1764
+1765
+1766
+1767
+1768
+1769
+1770
+1771
+1772
+1773
+1774
+1775
+1776
+1777
+1778
+1779
+1780
+1781
+1782
+1783
+1784
+1785
+1786
+1787
+1788
+1789
+1790
+1791
+1792
+1793
+1794
+1795
+1796
+1797
+1798
+1799
+1800
+1801
+1802
+1803
+1804
+1805
+1806
+1807
+1808
+1809
+1810
+1811
+1812
+1813
+1814
+1815
+1816
+1817
+1818
+1819
+1820
+1821
+1822
+1823
+1824
+1825
+1826
+1827
+1828
+1829
+1830
+1831
+1832
+1833
+1834
+1835
+1836
+1837
+1838
+1839
+1840
+1841
+1842
+1843
+1844
+1845
+1846
+1847
+1848
+1849
+1850
+1851
+1852
+1853
+1854
+1855
+1856
+1857
+1858
+1859
+1860
+1861
+1862
+1863
+1864
+1865
+1866
+1867
+1868
+1869
+1870
+1871
+1872
+1873
+1874
+1875
+1876
+1877
+1878
+1879
+1880
+1881
+1882
+1883
+1884
+1885
+1886
+1887
+1888
+1889
+1890
+1891
+1892
+1893
+1894
+1895
+1896
+1897
+1898
+1899
+1900
+1901
+1902
+1903
+1904
+1905
+1906
+1907
+1908
+1909
+1910
+1911
+1912
+1913
+1914
+1915
+1916
+1917
+1918
+1919
+1920
+1921
+1922
+1923
+1924
+1925
+1926
+1927
+1928
+1929
+1930
+1931
+1932
+1933
+1934
+1935
+1936
+1937
+1938
+1939
+1940
+1941
+1942
+1943
+1944
+1945
+1946
+1947
+1948
+1949
+1950
+1951
+1952
+1953
+1954
+1955
+1956
+1957
+1958
+1959
+1960
+1961
+1962
+1963
+1964
+1965
+1966
+1967
+1968
+1969
+1970
+1971
+1972
+1973
+1974
+1975
+1976
+1977
+1978
+1979
+1980
+1981
+1982
+1983
+1984
+1985
+1986
+1987
+1988
+1989
+1990
+1991
+1992
+1993
+1994
+1995
+1996
+1997
+1998
+1999
+2000
+2001
+2002
+2003
+2004
+2005
+2006
+2007
+2008
+2009
+2010
+2011
+2012
+2013
+2014
+2015
+2016
+2017
+2018
+2019
+2020
+2021
+2022
+2023
+2024
+2025
+2026
+2027
+2028
+2029
+2030
+2031
+2032
+2033
+2034
+2035
+2036
+2037
+2038
+2039
+2040
+2041
+2042
+2043
+2044
+2045
+2046
+2047
+2048
+2049
+2050
+2051
+2052
+2053
+2054
+2055
+2056
+2057
+2058
+2059
+2060
+2061
+2062
+2063
+2064
+2065
+2066
+2067
+2068
+2069
+2070
+2071
+2072
+2073
+2074
+2075
+2076
+2077
+2078
+2079
+2080
+2081
+2082
+2083
+2084
+2085
+2086
+2087
+2088
+2089
+2090
+2091
+2092
+2093
+2094
+2095
+2096
+2097
+2098
+2099
+2100
+2101
+2102
+2103
+2104
+2105
+2106
+2107
+2108
+2109
+2110
+2111
+2112
+2113
+2114
+2115
+2116
+2117
+2118
+2119
+2120
+2121
+2122
+2123
+2124
+2125
+2126
+2127
+2128
+2129
+2130
+2131
+2132
+2133
+2134
+2135
+2136
+2137
+2138
+2139
+2140
+2141
+2142
+2143
+2144
+2145
+2146
+2147
+2148
+2149
+2150
+2151
+2152
+2153
+2154
+2155
+2156
+2157
+2158
+2159
+2160
+2161
+2162
+2163
+2164
+2165
+2166
+2167
+2168
+2169
+2170
+2171
+2172
+2173
+2174
+2175
+2176
+2177
+2178
+2179
+2180
+2181
+2182
+2183
+2184
+2185
+2186
+2187
+2188
+2189
+2190
+2191
+2192
+2193
+2194
+2195
+2196
+2197
+2198
+2199
+2200
+2201
+2202
+2203
+2204
+2205
+2206
+2207
+2208
+2209
+2210
+2211
+2212
+2213
+2214
+2215
+2216
+2217
+2218
+2219
+2220
+2221
+2222
+2223
+2224
+2225
+2226
+2227
+2228
+2229
+2230
+2231
+2232
+2233
+2234
+2235
+2236
+2237
+2238
+2239
+2240
+2241
+2242
+2243
+2244
+2245
+2246
+2247
+2248
+2249
+2250
+2251
+2252
+2253
+2254
+2255
+2256
+2257
+2258
+2259
+2260
+2261
+2262
+2263
+2264
+2265
+2266
+2267
+2268
+2269
+2270
+2271
+2272
+2273
+2274
+2275
+2276
+2277
+2278
+2279
+2280
+2281
+2282
+2283
+2284
+2285
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  interfaces::Resp3Frame,
+  modules::inner::RedisClientInner,
+  protocol::{
+    hashers::ClusterHash,
+    responders::ResponseKind,
+    types::{ProtocolFrame, Server},
+    utils as protocol_utils,
+  },
+  runtime::{oneshot_channel, AtomicBool, Mutex, OneshotReceiver, OneshotSender, RefCount},
+  trace,
+  types::{CustomCommand, RedisValue},
+  utils as client_utils,
+  utils,
+};
+use bytes_utils::Str;
+use redis_protocol::resp3::types::RespVersion;
+use std::{
+  convert::TryFrom,
+  fmt,
+  fmt::Formatter,
+  mem,
+  str,
+  time::{Duration, Instant},
+};
+
+#[cfg(feature = "mocks")]
+use crate::modules::mocks::MockCommand;
+#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))]
+use crate::trace::CommandTraces;
+
+#[cfg(feature = "debug-ids")]
+static COMMAND_COUNTER: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
+#[cfg(feature = "debug-ids")]
+pub fn command_counter() -> usize {
+  COMMAND_COUNTER
+    .fetch_add(1, std::sync::atomic::Ordering::SeqCst)
+    .saturating_add(1)
+}
+
+/// A command interface for communication between connection reader tasks and the router.
+///
+/// Use of this interface assumes that a command was **not** pipelined. The reader task may instead
+/// choose to communicate with the router via the shared command queue if no channel exists on
+/// which to send this command.
+#[derive(Debug)]
+pub enum RouterResponse {
+  /// Continue with the next command.
+  Continue,
+  /// Retry the command immediately against the provided server, but with an `ASKING` prefix.
+  ///
+  /// Typically used with transactions to retry the entire transaction against a different node.
+  ///
+  /// Reader tasks will attempt to use the router channel first when handling cluster errors, but
+  /// may fall back to communication via the command channel in the context of pipelined commands.
+  Ask((u16, Server, RedisCommand)),
+  /// Retry the command immediately against the provided server, updating the cached routing table first.
+  ///
+  /// Reader tasks will attempt to use the router channel first when handling cluster errors, but
+  /// may fall back to communication via the command channel in the context of pipelined commands.
+  Moved((u16, Server, RedisCommand)),
+  /// Indicate to the router that the provided transaction command failed with the associated error.
+  ///
+  /// The router is responsible for responding to the caller with the error, if needed. Transaction commands are
+  /// never pipelined.
+  TransactionError((RedisError, RedisCommand)),
+  /// Indicates to the router that the transaction finished with the associated result.
+  TransactionResult(Resp3Frame),
+  /// Indicates that the connection closed while the command was in-flight.
+  ///
+  /// This is only used for non-pipelined commands where the router task is blocked on a response before
+  /// checking the next command.
+  ConnectionClosed((RedisError, RedisCommand)),
+}
+
+/// A channel for communication between connection reader tasks and futures returned to the caller.
+pub type ResponseSender = OneshotSender<Result<Resp3Frame, RedisError>>;
+/// A sender channel for communication between connection reader tasks and the router.
+pub type RouterSender = OneshotSender<RouterResponse>;
+/// A receiver channel for communication between connection reader tasks and the router.
+pub type RouterReceiver = OneshotReceiver<RouterResponse>;
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClusterErrorKind {
+  Moved,
+  Ask,
+}
+
+impl fmt::Display for ClusterErrorKind {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    match self {
+      ClusterErrorKind::Moved => write!(f, "MOVED"),
+      ClusterErrorKind::Ask => write!(f, "ASK"),
+    }
+  }
+}
+
+impl<'a> TryFrom<&'a str> for ClusterErrorKind {
+  type Error = RedisError;
+
+  fn try_from(value: &'a str) -> Result<Self, Self::Error> {
+    match value {
+      "MOVED" => Ok(ClusterErrorKind::Moved),
+      "ASK" => Ok(ClusterErrorKind::Ask),
+      _ => Err(RedisError::new(
+        RedisErrorKind::Protocol,
+        "Expected MOVED or ASK error.",
+      )),
+    }
+  }
+}
+
+// TODO organize these and gate them w/ the appropriate feature flags
+#[derive(Clone, Eq, PartialEq)]
+pub enum RedisCommandKind {
+  AclLoad,
+  AclSave,
+  AclList,
+  AclUsers,
+  AclGetUser,
+  AclSetUser,
+  AclDelUser,
+  AclCat,
+  AclGenPass,
+  AclWhoAmI,
+  AclLog,
+  AclHelp,
+  Append,
+  Auth,
+  Asking,
+  BgreWriteAof,
+  BgSave,
+  BitCount,
+  BitField,
+  BitOp,
+  BitPos,
+  BlPop,
+  BlMove,
+  BrPop,
+  BrPopLPush,
+  BzPopMin,
+  BzPopMax,
+  BlmPop,
+  BzmPop,
+  ClientID,
+  ClientInfo,
+  ClientKill,
+  ClientList,
+  ClientGetName,
+  ClientPause,
+  ClientUnpause,
+  ClientUnblock,
+  ClientReply,
+  ClientSetname,
+  ClientGetRedir,
+  ClientTracking,
+  ClientTrackingInfo,
+  ClientCaching,
+  ClusterAddSlots,
+  ClusterCountFailureReports,
+  ClusterCountKeysInSlot,
+  ClusterDelSlots,
+  ClusterFailOver,
+  ClusterForget,
+  ClusterFlushSlots,
+  ClusterGetKeysInSlot,
+  ClusterInfo,
+  ClusterKeySlot,
+  ClusterMeet,
+  ClusterMyID,
+  ClusterNodes,
+  ClusterReplicate,
+  ClusterReset,
+  ClusterSaveConfig,
+  ClusterSetConfigEpoch,
+  ClusterBumpEpoch,
+  ClusterSetSlot,
+  ClusterReplicas,
+  ClusterSlots,
+  ConfigGet,
+  ConfigRewrite,
+  ConfigSet,
+  ConfigResetStat,
+  Copy,
+  DBSize,
+  Decr,
+  DecrBy,
+  Del,
+  Discard,
+  Dump,
+  Echo,
+  Eval,
+  EvalSha,
+  Exec,
+  Exists,
+  Expire,
+  ExpireAt,
+  Failover,
+  FlushAll,
+  FlushDB,
+  GeoAdd,
+  GeoHash,
+  GeoPos,
+  GeoDist,
+  GeoRadius,
+  GeoRadiusByMember,
+  GeoSearch,
+  GeoSearchStore,
+  Get,
+  GetBit,
+  GetDel,
+  GetRange,
+  GetSet,
+  HDel,
+  HExists,
+  HGet,
+  HGetAll,
+  HIncrBy,
+  HIncrByFloat,
+  HKeys,
+  HLen,
+  HMGet,
+  HMSet,
+  HSet,
+  HSetNx,
+  HStrLen,
+  HVals,
+  HRandField,
+  Incr,
+  IncrBy,
+  IncrByFloat,
+  Info,
+  Keys,
+  LastSave,
+  LIndex,
+  LInsert,
+  LLen,
+  LMove,
+  LPop,
+  LPos,
+  LPush,
+  LPushX,
+  LRange,
+  LMPop,
+  LRem,
+  LSet,
+  LTrim,
+  Lcs,
+  MemoryDoctor,
+  MemoryHelp,
+  MemoryMallocStats,
+  MemoryPurge,
+  MemoryStats,
+  MemoryUsage,
+  Mget,
+  Migrate,
+  Monitor,
+  Move,
+  Mset,
+  Msetnx,
+  Multi,
+  Object,
+  Persist,
+  Pexpire,
+  Pexpireat,
+  Pfadd,
+  Pfcount,
+  Pfmerge,
+  Ping,
+  Psetex,
+  Pttl,
+  Quit,
+  Randomkey,
+  Readonly,
+  Readwrite,
+  Rename,
+  Renamenx,
+  Restore,
+  Role,
+  Rpop,
+  Rpoplpush,
+  Rpush,
+  Rpushx,
+  Sadd,
+  Save,
+  Scard,
+  Sdiff,
+  Sdiffstore,
+  Select,
+  Sentinel,
+  Set,
+  Setbit,
+  Setex,
+  Setnx,
+  Setrange,
+  Shutdown,
+  Sinter,
+  Sinterstore,
+  Sismember,
+  Replicaof,
+  Slowlog,
+  Smembers,
+  Smismember,
+  Smove,
+  Sort,
+  SortRo,
+  Spop,
+  Srandmember,
+  Srem,
+  Strlen,
+  Sunion,
+  Sunionstore,
+  Swapdb,
+  Sync,
+  Time,
+  Touch,
+  Ttl,
+  Type,
+  Unlink,
+  Unwatch,
+  Wait,
+  Watch,
+  // Streams
+  XinfoConsumers,
+  XinfoGroups,
+  XinfoStream,
+  Xadd,
+  Xtrim,
+  Xdel,
+  Xrange,
+  Xrevrange,
+  Xlen,
+  Xread,
+  Xgroupcreate,
+  XgroupCreateConsumer,
+  XgroupDelConsumer,
+  XgroupDestroy,
+  XgroupSetId,
+  Xreadgroup,
+  Xack,
+  Xclaim,
+  Xautoclaim,
+  Xpending,
+  // Sorted Sets
+  Zadd,
+  Zcard,
+  Zcount,
+  Zdiff,
+  Zdiffstore,
+  Zincrby,
+  Zinter,
+  Zinterstore,
+  Zlexcount,
+  Zrandmember,
+  Zrange,
+  Zrangestore,
+  Zrangebylex,
+  Zrangebyscore,
+  Zrank,
+  Zrem,
+  Zremrangebylex,
+  Zremrangebyrank,
+  Zremrangebyscore,
+  Zrevrange,
+  Zrevrangebylex,
+  Zrevrangebyscore,
+  Zrevrank,
+  Zscore,
+  Zmscore,
+  Zunion,
+  Zunionstore,
+  Zpopmax,
+  Zpopmin,
+  Zmpop,
+  // Scripts
+  ScriptLoad,
+  ScriptDebug,
+  ScriptExists,
+  ScriptFlush,
+  ScriptKill,
+  // Scanning
+  Scan,
+  Sscan,
+  Hscan,
+  Zscan,
+  // Function
+  Fcall,
+  FcallRO,
+  FunctionDelete,
+  FunctionDump,
+  FunctionFlush,
+  FunctionKill,
+  FunctionList,
+  FunctionLoad,
+  FunctionRestore,
+  FunctionStats,
+  // Pubsub
+  Publish,
+  PubsubChannels,
+  PubsubNumpat,
+  PubsubNumsub,
+  PubsubShardchannels,
+  PubsubShardnumsub,
+  Spublish,
+  Ssubscribe,
+  Sunsubscribe,
+  Unsubscribe,
+  Subscribe,
+  Psubscribe,
+  Punsubscribe,
+  // RedisJSON
+  JsonArrAppend,
+  JsonArrIndex,
+  JsonArrInsert,
+  JsonArrLen,
+  JsonArrPop,
+  JsonArrTrim,
+  JsonClear,
+  JsonDebugMemory,
+  JsonDel,
+  JsonGet,
+  JsonMerge,
+  JsonMGet,
+  JsonMSet,
+  JsonNumIncrBy,
+  JsonObjKeys,
+  JsonObjLen,
+  JsonResp,
+  JsonSet,
+  JsonStrAppend,
+  JsonStrLen,
+  JsonToggle,
+  JsonType,
+  // Time Series
+  TsAdd,
+  TsAlter,
+  TsCreate,
+  TsCreateRule,
+  TsDecrBy,
+  TsDel,
+  TsDeleteRule,
+  TsGet,
+  TsIncrBy,
+  TsInfo,
+  TsMAdd,
+  TsMGet,
+  TsMRange,
+  TsMRevRange,
+  TsQueryIndex,
+  TsRange,
+  TsRevRange,
+  // RediSearch
+  FtList,
+  FtAggregate,
+  FtSearch,
+  FtCreate,
+  FtAlter,
+  FtAliasAdd,
+  FtAliasDel,
+  FtAliasUpdate,
+  FtConfigGet,
+  FtConfigSet,
+  FtCursorDel,
+  FtCursorRead,
+  FtDictAdd,
+  FtDictDel,
+  FtDictDump,
+  FtDropIndex,
+  FtExplain,
+  FtInfo,
+  FtSpellCheck,
+  FtSugAdd,
+  FtSugDel,
+  FtSugGet,
+  FtSugLen,
+  FtSynDump,
+  FtSynUpdate,
+  FtTagVals,
+  // Commands with custom state or commands that don't map directly to the server's command interface.
+  _Hello(RespVersion),
+  _AuthAllCluster,
+  _HelloAllCluster(RespVersion),
+  _FlushAllCluster,
+  _ScriptFlushCluster,
+  _ScriptLoadCluster,
+  _ScriptKillCluster,
+  _FunctionLoadCluster,
+  _FunctionFlushCluster,
+  _FunctionDeleteCluster,
+  _FunctionRestoreCluster,
+  // When in RESP3 mode and **not** using the `bcast` arg then we send the command on all cluster node connections
+  _ClientTrackingCluster,
+  _Custom(CustomCommand),
+}
+
+impl fmt::Debug for RedisCommandKind {
+  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    write!(f, "{}", self.to_str_debug())
+  }
+}
+
+impl RedisCommandKind {
+  pub fn is_scan(&self) -> bool {
+    matches!(*self, RedisCommandKind::Scan)
+  }
+
+  pub fn is_hscan(&self) -> bool {
+    matches!(*self, RedisCommandKind::Hscan)
+  }
+
+  pub fn is_sscan(&self) -> bool {
+    matches!(*self, RedisCommandKind::Sscan)
+  }
+
+  pub fn is_zscan(&self) -> bool {
+    matches!(*self, RedisCommandKind::Zscan)
+  }
+
+  pub fn is_hello(&self) -> bool {
+    matches!(
+      *self,
+      RedisCommandKind::_Hello(_) | RedisCommandKind::_HelloAllCluster(_)
+    )
+  }
+
+  pub fn is_auth(&self) -> bool {
+    matches!(*self, RedisCommandKind::Auth)
+  }
+
+  pub fn is_value_scan(&self) -> bool {
+    matches!(
+      *self,
+      RedisCommandKind::Zscan | RedisCommandKind::Hscan | RedisCommandKind::Sscan
+    )
+  }
+
+  pub fn is_multi(&self) -> bool {
+    matches!(*self, RedisCommandKind::Multi)
+  }
+
+  pub fn is_exec(&self) -> bool {
+    matches!(*self, RedisCommandKind::Exec)
+  }
+
+  pub fn is_discard(&self) -> bool {
+    matches!(*self, RedisCommandKind::Discard)
+  }
+
+  pub fn ends_transaction(&self) -> bool {
+    matches!(*self, RedisCommandKind::Exec | RedisCommandKind::Discard)
+  }
+
+  pub fn is_mset(&self) -> bool {
+    matches!(*self, RedisCommandKind::Mset | RedisCommandKind::Msetnx)
+  }
+
+  pub fn is_custom(&self) -> bool {
+    matches!(*self, RedisCommandKind::_Custom(_))
+  }
+
+  pub fn closes_connection(&self) -> bool {
+    matches!(*self, RedisCommandKind::Quit | RedisCommandKind::Shutdown)
+  }
+
+  pub fn custom_hash_slot(&self) -> Option<u16> {
+    match self {
+      RedisCommandKind::_Custom(ref cmd) => match cmd.cluster_hash {
+        ClusterHash::Custom(ref val) => Some(*val),
+        _ => None,
+      },
+      _ => None,
+    }
+  }
+
+  /// Read the command's protocol string without panicking.
+  ///
+  /// Typically used for logging or debugging.
+  pub fn to_str_debug(&self) -> &str {
+    match *self {
+      RedisCommandKind::AclLoad => "ACL LOAD",
+      RedisCommandKind::AclSave => "ACL SAVE",
+      RedisCommandKind::AclList => "ACL LIST",
+      RedisCommandKind::AclUsers => "ACL USERS",
+      RedisCommandKind::AclGetUser => "ACL GETUSER",
+      RedisCommandKind::AclSetUser => "ACL SETUSER",
+      RedisCommandKind::AclDelUser => "ACL DELUSER",
+      RedisCommandKind::AclCat => "ACL CAT",
+      RedisCommandKind::AclGenPass => "ACL GENPASS",
+      RedisCommandKind::AclWhoAmI => "ACL WHOAMI",
+      RedisCommandKind::AclLog => "ACL LOG",
+      RedisCommandKind::AclHelp => "ACL HELP",
+      RedisCommandKind::Append => "APPEND",
+      RedisCommandKind::Auth => "AUTH",
+      RedisCommandKind::Asking => "ASKING",
+      RedisCommandKind::BgreWriteAof => "BGREWRITEAOF",
+      RedisCommandKind::BgSave => "BGSAVE",
+      RedisCommandKind::BitCount => "BITCOUNT",
+      RedisCommandKind::BitField => "BITFIELD",
+      RedisCommandKind::BitOp => "BITOP",
+      RedisCommandKind::BitPos => "BITPOS",
+      RedisCommandKind::BlPop => "BLPOP",
+      RedisCommandKind::BlMove => "BLMOVE",
+      RedisCommandKind::BrPop => "BRPOP",
+      RedisCommandKind::BzmPop => "BZMPOP",
+      RedisCommandKind::BlmPop => "BLMPOP",
+      RedisCommandKind::BrPopLPush => "BRPOPLPUSH",
+      RedisCommandKind::BzPopMin => "BZPOPMIN",
+      RedisCommandKind::BzPopMax => "BZPOPMAX",
+      RedisCommandKind::ClientID => "CLIENT ID",
+      RedisCommandKind::ClientInfo => "CLIENT INFO",
+      RedisCommandKind::ClientKill => "CLIENT KILL",
+      RedisCommandKind::ClientList => "CLIENT LIST",
+      RedisCommandKind::ClientGetName => "CLIENT GETNAME",
+      RedisCommandKind::ClientPause => "CLIENT PAUSE",
+      RedisCommandKind::ClientUnpause => "CLIENT UNPAUSE",
+      RedisCommandKind::ClientUnblock => "CLIENT UNBLOCK",
+      RedisCommandKind::ClientReply => "CLIENT REPLY",
+      RedisCommandKind::ClientSetname => "CLIENT SETNAME",
+      RedisCommandKind::ClientGetRedir => "CLIENT GETREDIR",
+      RedisCommandKind::ClientTracking => "CLIENT TRACKING",
+      RedisCommandKind::ClientTrackingInfo => "CLIENT TRACKINGINFO",
+      RedisCommandKind::ClientCaching => "CLIENT CACHING",
+      RedisCommandKind::ClusterAddSlots => "CLUSTER ADDSLOTS",
+      RedisCommandKind::ClusterCountFailureReports => "CLUSTER COUNT-FAILURE-REPORTS",
+      RedisCommandKind::ClusterCountKeysInSlot => "CLUSTER COUNTKEYSINSLOT",
+      RedisCommandKind::ClusterDelSlots => "CLUSTER DEL SLOTS",
+      RedisCommandKind::ClusterFailOver => "CLUSTER FAILOVER",
+      RedisCommandKind::ClusterForget => "CLUSTER FORGET",
+      RedisCommandKind::ClusterGetKeysInSlot => "CLUSTER GETKEYSINSLOTS",
+      RedisCommandKind::ClusterInfo => "CLUSTER INFO",
+      RedisCommandKind::ClusterKeySlot => "CLUSTER KEYSLOT",
+      RedisCommandKind::ClusterMeet => "CLUSTER MEET",
+      RedisCommandKind::ClusterNodes => "CLUSTER NODES",
+      RedisCommandKind::ClusterReplicate => "CLUSTER REPLICATE",
+      RedisCommandKind::ClusterReset => "CLUSTER RESET",
+      RedisCommandKind::ClusterSaveConfig => "CLUSTER SAVECONFIG",
+      RedisCommandKind::ClusterSetConfigEpoch => "CLUSTER SET-CONFIG-EPOCH",
+      RedisCommandKind::ClusterSetSlot => "CLUSTER SETSLOT",
+      RedisCommandKind::ClusterReplicas => "CLUSTER REPLICAS",
+      RedisCommandKind::ClusterSlots => "CLUSTER SLOTS",
+      RedisCommandKind::ClusterBumpEpoch => "CLUSTER BUMPEPOCH",
+      RedisCommandKind::ClusterFlushSlots => "CLUSTER FLUSHSLOTS",
+      RedisCommandKind::ClusterMyID => "CLUSTER MYID",
+      RedisCommandKind::ConfigGet => "CONFIG GET",
+      RedisCommandKind::ConfigRewrite => "CONFIG REWRITE",
+      RedisCommandKind::ConfigSet => "CONFIG SET",
+      RedisCommandKind::ConfigResetStat => "CONFIG RESETSTAT",
+      RedisCommandKind::Copy => "COPY",
+      RedisCommandKind::DBSize => "DBSIZE",
+      RedisCommandKind::Decr => "DECR",
+      RedisCommandKind::DecrBy => "DECRBY",
+      RedisCommandKind::Del => "DEL",
+      RedisCommandKind::Discard => "DISCARD",
+      RedisCommandKind::Dump => "DUMP",
+      RedisCommandKind::Echo => "ECHO",
+      RedisCommandKind::Eval => "EVAL",
+      RedisCommandKind::EvalSha => "EVALSHA",
+      RedisCommandKind::Exec => "EXEC",
+      RedisCommandKind::Exists => "EXISTS",
+      RedisCommandKind::Expire => "EXPIRE",
+      RedisCommandKind::ExpireAt => "EXPIREAT",
+      RedisCommandKind::Failover => "FAILOVER",
+      RedisCommandKind::FlushAll => "FLUSHALL",
+      RedisCommandKind::FlushDB => "FLUSHDB",
+      RedisCommandKind::GeoAdd => "GEOADD",
+      RedisCommandKind::GeoHash => "GEOHASH",
+      RedisCommandKind::GeoPos => "GEOPOS",
+      RedisCommandKind::GeoDist => "GEODIST",
+      RedisCommandKind::GeoRadius => "GEORADIUS",
+      RedisCommandKind::GeoRadiusByMember => "GEORADIUSBYMEMBER",
+      RedisCommandKind::GeoSearch => "GEOSEARCH",
+      RedisCommandKind::GeoSearchStore => "GEOSEARCHSTORE",
+      RedisCommandKind::Get => "GET",
+      RedisCommandKind::GetDel => "GETDEL",
+      RedisCommandKind::GetBit => "GETBIT",
+      RedisCommandKind::GetRange => "GETRANGE",
+      RedisCommandKind::GetSet => "GETSET",
+      RedisCommandKind::HDel => "HDEL",
+      RedisCommandKind::_Hello(_) => "HELLO",
+      RedisCommandKind::HExists => "HEXISTS",
+      RedisCommandKind::HGet => "HGET",
+      RedisCommandKind::HGetAll => "HGETALL",
+      RedisCommandKind::HIncrBy => "HINCRBY",
+      RedisCommandKind::HIncrByFloat => "HINCRBYFLOAT",
+      RedisCommandKind::HKeys => "HKEYS",
+      RedisCommandKind::HLen => "HLEN",
+      RedisCommandKind::HMGet => "HMGET",
+      RedisCommandKind::HMSet => "HMSET",
+      RedisCommandKind::HSet => "HSET",
+      RedisCommandKind::HSetNx => "HSETNX",
+      RedisCommandKind::HStrLen => "HSTRLEN",
+      RedisCommandKind::HRandField => "HRANDFIELD",
+      RedisCommandKind::HVals => "HVALS",
+      RedisCommandKind::Incr => "INCR",
+      RedisCommandKind::IncrBy => "INCRBY",
+      RedisCommandKind::IncrByFloat => "INCRBYFLOAT",
+      RedisCommandKind::Info => "INFO",
+      RedisCommandKind::Keys => "KEYS",
+      RedisCommandKind::LastSave => "LASTSAVE",
+      RedisCommandKind::LIndex => "LINDEX",
+      RedisCommandKind::LInsert => "LINSERT",
+      RedisCommandKind::LLen => "LLEN",
+      RedisCommandKind::LMove => "LMOVE",
+      RedisCommandKind::LPop => "LPOP",
+      RedisCommandKind::LPos => "LPOS",
+      RedisCommandKind::LPush => "LPUSH",
+      RedisCommandKind::LPushX => "LPUSHX",
+      RedisCommandKind::LRange => "LRANGE",
+      RedisCommandKind::LMPop => "LMPOP",
+      RedisCommandKind::LRem => "LREM",
+      RedisCommandKind::LSet => "LSET",
+      RedisCommandKind::LTrim => "LTRIM",
+      RedisCommandKind::Lcs => "LCS",
+      RedisCommandKind::MemoryDoctor => "MEMORY DOCTOR",
+      RedisCommandKind::MemoryHelp => "MEMORY HELP",
+      RedisCommandKind::MemoryMallocStats => "MEMORY MALLOC-STATS",
+      RedisCommandKind::MemoryPurge => "MEMORY PURGE",
+      RedisCommandKind::MemoryStats => "MEMORY STATS",
+      RedisCommandKind::MemoryUsage => "MEMORY USAGE",
+      RedisCommandKind::Mget => "MGET",
+      RedisCommandKind::Migrate => "MIGRATE",
+      RedisCommandKind::Monitor => "MONITOR",
+      RedisCommandKind::Move => "MOVE",
+      RedisCommandKind::Mset => "MSET",
+      RedisCommandKind::Msetnx => "MSETNX",
+      RedisCommandKind::Multi => "MULTI",
+      RedisCommandKind::Object => "OBJECT",
+      RedisCommandKind::Persist => "PERSIST",
+      RedisCommandKind::Pexpire => "PEXPIRE",
+      RedisCommandKind::Pexpireat => "PEXPIREAT",
+      RedisCommandKind::Pfadd => "PFADD",
+      RedisCommandKind::Pfcount => "PFCOUNT",
+      RedisCommandKind::Pfmerge => "PFMERGE",
+      RedisCommandKind::Ping => "PING",
+      RedisCommandKind::Psetex => "PSETEX",
+      RedisCommandKind::Psubscribe => "PSUBSCRIBE",
+      RedisCommandKind::Pttl => "PTTL",
+      RedisCommandKind::Publish => "PUBLISH",
+      RedisCommandKind::Punsubscribe => "PUNSUBSCRIBE",
+      RedisCommandKind::Quit => "QUIT",
+      RedisCommandKind::Randomkey => "RANDOMKEY",
+      RedisCommandKind::Readonly => "READONLY",
+      RedisCommandKind::Readwrite => "READWRITE",
+      RedisCommandKind::Rename => "RENAME",
+      RedisCommandKind::Renamenx => "RENAMENX",
+      RedisCommandKind::Restore => "RESTORE",
+      RedisCommandKind::Role => "ROLE",
+      RedisCommandKind::Rpop => "RPOP",
+      RedisCommandKind::Rpoplpush => "RPOPLPUSH",
+      RedisCommandKind::Rpush => "RPUSH",
+      RedisCommandKind::Rpushx => "RPUSHX",
+      RedisCommandKind::Sadd => "SADD",
+      RedisCommandKind::Save => "SAVE",
+      RedisCommandKind::Scard => "SCARD",
+      RedisCommandKind::Sdiff => "SDIFF",
+      RedisCommandKind::Sdiffstore => "SDIFFSTORE",
+      RedisCommandKind::Select => "SELECT",
+      RedisCommandKind::Sentinel => "SENTINEL",
+      RedisCommandKind::Set => "SET",
+      RedisCommandKind::Setbit => "SETBIT",
+      RedisCommandKind::Setex => "SETEX",
+      RedisCommandKind::Setnx => "SETNX",
+      RedisCommandKind::Setrange => "SETRANGE",
+      RedisCommandKind::Shutdown => "SHUTDOWN",
+      RedisCommandKind::Sinter => "SINTER",
+      RedisCommandKind::Sinterstore => "SINTERSTORE",
+      RedisCommandKind::Sismember => "SISMEMBER",
+      RedisCommandKind::Replicaof => "REPLICAOF",
+      RedisCommandKind::Slowlog => "SLOWLOG",
+      RedisCommandKind::Smembers => "SMEMBERS",
+      RedisCommandKind::Smismember => "SMISMEMBER",
+      RedisCommandKind::Smove => "SMOVE",
+      RedisCommandKind::Sort => "SORT",
+      RedisCommandKind::SortRo => "SORT_RO",
+      RedisCommandKind::Spop => "SPOP",
+      RedisCommandKind::Srandmember => "SRANDMEMBER",
+      RedisCommandKind::Srem => "SREM",
+      RedisCommandKind::Strlen => "STRLEN",
+      RedisCommandKind::Subscribe => "SUBSCRIBE",
+      RedisCommandKind::Sunion => "SUNION",
+      RedisCommandKind::Sunionstore => "SUNIONSTORE",
+      RedisCommandKind::Swapdb => "SWAPDB",
+      RedisCommandKind::Sync => "SYNC",
+      RedisCommandKind::Time => "TIME",
+      RedisCommandKind::Touch => "TOUCH",
+      RedisCommandKind::Ttl => "TTL",
+      RedisCommandKind::Type => "TYPE",
+      RedisCommandKind::Unsubscribe => "UNSUBSCRIBE",
+      RedisCommandKind::Unlink => "UNLINK",
+      RedisCommandKind::Unwatch => "UNWATCH",
+      RedisCommandKind::Wait => "WAIT",
+      RedisCommandKind::Watch => "WATCH",
+      RedisCommandKind::XinfoConsumers => "XINFO CONSUMERS",
+      RedisCommandKind::XinfoGroups => "XINFO GROUPS",
+      RedisCommandKind::XinfoStream => "XINFO STREAM",
+      RedisCommandKind::Xadd => "XADD",
+      RedisCommandKind::Xtrim => "XTRIM",
+      RedisCommandKind::Xdel => "XDEL",
+      RedisCommandKind::Xrange => "XRANGE",
+      RedisCommandKind::Xrevrange => "XREVRANGE",
+      RedisCommandKind::Xlen => "XLEN",
+      RedisCommandKind::Xread => "XREAD",
+      RedisCommandKind::Xgroupcreate => "XGROUP CREATE",
+      RedisCommandKind::XgroupCreateConsumer => "XGROUP CREATECONSUMER",
+      RedisCommandKind::XgroupDelConsumer => "XGROUP DELCONSUMER",
+      RedisCommandKind::XgroupDestroy => "XGROUP DESTROY",
+      RedisCommandKind::XgroupSetId => "XGROUP SETID",
+      RedisCommandKind::Xreadgroup => "XREADGROUP",
+      RedisCommandKind::Xack => "XACK",
+      RedisCommandKind::Xclaim => "XCLAIM",
+      RedisCommandKind::Xautoclaim => "XAUTOCLAIM",
+      RedisCommandKind::Xpending => "XPENDING",
+      RedisCommandKind::Zadd => "ZADD",
+      RedisCommandKind::Zcard => "ZCARD",
+      RedisCommandKind::Zcount => "ZCOUNT",
+      RedisCommandKind::Zdiff => "ZDIFF",
+      RedisCommandKind::Zdiffstore => "ZDIFFSTORE",
+      RedisCommandKind::Zincrby => "ZINCRBY",
+      RedisCommandKind::Zinter => "ZINTER",
+      RedisCommandKind::Zinterstore => "ZINTERSTORE",
+      RedisCommandKind::Zlexcount => "ZLEXCOUNT",
+      RedisCommandKind::Zrandmember => "ZRANDMEMBER",
+      RedisCommandKind::Zrange => "ZRANGE",
+      RedisCommandKind::Zrangestore => "ZRANGESTORE",
+      RedisCommandKind::Zrangebylex => "ZRANGEBYLEX",
+      RedisCommandKind::Zrangebyscore => "ZRANGEBYSCORE",
+      RedisCommandKind::Zrank => "ZRANK",
+      RedisCommandKind::Zrem => "ZREM",
+      RedisCommandKind::Zremrangebylex => "ZREMRANGEBYLEX",
+      RedisCommandKind::Zremrangebyrank => "ZREMRANGEBYRANK",
+      RedisCommandKind::Zremrangebyscore => "ZREMRANGEBYSCORE",
+      RedisCommandKind::Zrevrange => "ZREVRANGE",
+      RedisCommandKind::Zrevrangebylex => "ZREVRANGEBYLEX",
+      RedisCommandKind::Zrevrangebyscore => "ZREVRANGEBYSCORE",
+      RedisCommandKind::Zrevrank => "ZREVRANK",
+      RedisCommandKind::Zscore => "ZSCORE",
+      RedisCommandKind::Zmscore => "ZMSCORE",
+      RedisCommandKind::Zunion => "ZUNION",
+      RedisCommandKind::Zunionstore => "ZUNIONSTORE",
+      RedisCommandKind::Zpopmax => "ZPOPMAX",
+      RedisCommandKind::Zpopmin => "ZPOPMIN",
+      RedisCommandKind::Zmpop => "ZMPOP",
+      RedisCommandKind::Scan => "SCAN",
+      RedisCommandKind::Sscan => "SSCAN",
+      RedisCommandKind::Hscan => "HSCAN",
+      RedisCommandKind::Zscan => "ZSCAN",
+      RedisCommandKind::ScriptDebug => "SCRIPT DEBUG",
+      RedisCommandKind::ScriptExists => "SCRIPT EXISTS",
+      RedisCommandKind::ScriptFlush => "SCRIPT FLUSH",
+      RedisCommandKind::ScriptKill => "SCRIPT KILL",
+      RedisCommandKind::ScriptLoad => "SCRIPT LOAD",
+      RedisCommandKind::Spublish => "SPUBLISH",
+      RedisCommandKind::Ssubscribe => "SSUBSCRIBE",
+      RedisCommandKind::Sunsubscribe => "SUNSUBSCRIBE",
+      RedisCommandKind::_AuthAllCluster => "AUTH ALL CLUSTER",
+      RedisCommandKind::_HelloAllCluster(_) => "HELLO ALL CLUSTER",
+      RedisCommandKind::_FlushAllCluster => "FLUSHALL CLUSTER",
+      RedisCommandKind::_ScriptFlushCluster => "SCRIPT FLUSH CLUSTER",
+      RedisCommandKind::_ScriptLoadCluster => "SCRIPT LOAD CLUSTER",
+      RedisCommandKind::_ScriptKillCluster => "SCRIPT Kill CLUSTER",
+      RedisCommandKind::_FunctionLoadCluster => "FUNCTION LOAD CLUSTER",
+      RedisCommandKind::_FunctionFlushCluster => "FUNCTION FLUSH CLUSTER",
+      RedisCommandKind::_FunctionDeleteCluster => "FUNCTION DELETE CLUSTER",
+      RedisCommandKind::_FunctionRestoreCluster => "FUNCTION RESTORE CLUSTER",
+      RedisCommandKind::_ClientTrackingCluster => "CLIENT TRACKING CLUSTER",
+      RedisCommandKind::Fcall => "FCALL",
+      RedisCommandKind::FcallRO => "FCALL_RO",
+      RedisCommandKind::FunctionDelete => "FUNCTION DELETE",
+      RedisCommandKind::FunctionDump => "FUNCTION DUMP",
+      RedisCommandKind::FunctionFlush => "FUNCTION FLUSH",
+      RedisCommandKind::FunctionKill => "FUNCTION KILL",
+      RedisCommandKind::FunctionList => "FUNCTION LIST",
+      RedisCommandKind::FunctionLoad => "FUNCTION LOAD",
+      RedisCommandKind::FunctionRestore => "FUNCTION RESTORE",
+      RedisCommandKind::FunctionStats => "FUNCTION STATS",
+      RedisCommandKind::PubsubChannels => "PUBSUB CHANNELS",
+      RedisCommandKind::PubsubNumpat => "PUBSUB NUMPAT",
+      RedisCommandKind::PubsubNumsub => "PUBSUB NUMSUB",
+      RedisCommandKind::PubsubShardchannels => "PUBSUB SHARDCHANNELS",
+      RedisCommandKind::PubsubShardnumsub => "PUBSUB SHARDNUMSUB",
+      RedisCommandKind::JsonArrAppend => "JSON.ARRAPPEND",
+      RedisCommandKind::JsonArrIndex => "JSON.ARRINDEX",
+      RedisCommandKind::JsonArrInsert => "JSON.ARRINSERT",
+      RedisCommandKind::JsonArrLen => "JSON.ARRLEN",
+      RedisCommandKind::JsonArrPop => "JSON.ARRPOP",
+      RedisCommandKind::JsonArrTrim => "JSON.ARRTRIM",
+      RedisCommandKind::JsonClear => "JSON.CLEAR",
+      RedisCommandKind::JsonDebugMemory => "JSON.DEBUG MEMORY",
+      RedisCommandKind::JsonDel => "JSON.DEL",
+      RedisCommandKind::JsonGet => "JSON.GET",
+      RedisCommandKind::JsonMerge => "JSON.MERGE",
+      RedisCommandKind::JsonMGet => "JSON.MGET",
+      RedisCommandKind::JsonMSet => "JSON.MSET",
+      RedisCommandKind::JsonNumIncrBy => "JSON.NUMINCRBY",
+      RedisCommandKind::JsonObjKeys => "JSON.OBJKEYS",
+      RedisCommandKind::JsonObjLen => "JSON.OBJLEN",
+      RedisCommandKind::JsonResp => "JSON.RESP",
+      RedisCommandKind::JsonSet => "JSON.SET",
+      RedisCommandKind::JsonStrAppend => "JSON.STRAPPEND",
+      RedisCommandKind::JsonStrLen => "JSON.STRLEN",
+      RedisCommandKind::JsonToggle => "JSON.TOGGLE",
+      RedisCommandKind::JsonType => "JSON.TYPE",
+      RedisCommandKind::TsAdd => "TS.ADD",
+      RedisCommandKind::TsAlter => "TS.ALTER",
+      RedisCommandKind::TsCreate => "TS.CREATE",
+      RedisCommandKind::TsCreateRule => "TS.CREATERULE",
+      RedisCommandKind::TsDecrBy => "TS.DECRBY",
+      RedisCommandKind::TsDel => "TS.DEL",
+      RedisCommandKind::TsDeleteRule => "TS.DELETERULE",
+      RedisCommandKind::TsGet => "TS.GET",
+      RedisCommandKind::TsIncrBy => "TS.INCRBY",
+      RedisCommandKind::TsInfo => "TS.INFO",
+      RedisCommandKind::TsMAdd => "TS.MADD",
+      RedisCommandKind::TsMGet => "TS.MGET",
+      RedisCommandKind::TsMRange => "TS.MRANGE",
+      RedisCommandKind::TsMRevRange => "TS.MREVRANGE",
+      RedisCommandKind::TsQueryIndex => "TS.QUERYINDEX",
+      RedisCommandKind::TsRange => "TS.RANGE",
+      RedisCommandKind::TsRevRange => "TS.REVRANGE",
+      RedisCommandKind::FtList => "FT._LIST",
+      RedisCommandKind::FtAggregate => "FT.AGGREGATE",
+      RedisCommandKind::FtSearch => "FT.SEARCH",
+      RedisCommandKind::FtCreate => "FT.CREATE",
+      RedisCommandKind::FtAlter => "FT.ALTER",
+      RedisCommandKind::FtAliasAdd => "FT.ALIASADD",
+      RedisCommandKind::FtAliasDel => "FT.ALIASDEL",
+      RedisCommandKind::FtAliasUpdate => "FT.ALIASUPDATE",
+      RedisCommandKind::FtConfigGet => "FT.CONFIG GET",
+      RedisCommandKind::FtConfigSet => "FT.CONFIG SET",
+      RedisCommandKind::FtCursorDel => "FT.CURSOR DEL",
+      RedisCommandKind::FtCursorRead => "FT.CURSOR READ",
+      RedisCommandKind::FtDictAdd => "FT.DICTADD",
+      RedisCommandKind::FtDictDel => "FT.DICTDEL",
+      RedisCommandKind::FtDictDump => "FT.DICTDUMP",
+      RedisCommandKind::FtDropIndex => "FT.DROPINDEX",
+      RedisCommandKind::FtExplain => "FT.EXPLAIN",
+      RedisCommandKind::FtInfo => "FT.INFO",
+      RedisCommandKind::FtSpellCheck => "FT.SPELLCHECK",
+      RedisCommandKind::FtSugAdd => "FT.SUGADD",
+      RedisCommandKind::FtSugDel => "FT.SUGDEL",
+      RedisCommandKind::FtSugGet => "FT.SUGGET",
+      RedisCommandKind::FtSugLen => "FT.SUGLEN",
+      RedisCommandKind::FtSynDump => "FT.SYNDUMP",
+      RedisCommandKind::FtSynUpdate => "FT.SYNUPDATE",
+      RedisCommandKind::FtTagVals => "FT.TAGVALS",
+      RedisCommandKind::_Custom(ref kind) => &kind.cmd,
+    }
+  }
+
+  /// Read the protocol string for a command, panicking for internal commands that don't map directly to redis
+  /// command.
+  pub(crate) fn cmd_str(&self) -> Str {
+    let s = match *self {
+      RedisCommandKind::AclLoad
+      | RedisCommandKind::AclSave
+      | RedisCommandKind::AclList
+      | RedisCommandKind::AclUsers
+      | RedisCommandKind::AclGetUser
+      | RedisCommandKind::AclSetUser
+      | RedisCommandKind::AclDelUser
+      | RedisCommandKind::AclCat
+      | RedisCommandKind::AclGenPass
+      | RedisCommandKind::AclWhoAmI
+      | RedisCommandKind::AclLog
+      | RedisCommandKind::AclHelp => "ACL",
+      RedisCommandKind::Append => "APPEND",
+      RedisCommandKind::Auth => "AUTH",
+      RedisCommandKind::Asking => "ASKING",
+      RedisCommandKind::BgreWriteAof => "BGREWRITEAOF",
+      RedisCommandKind::BgSave => "BGSAVE",
+      RedisCommandKind::BitCount => "BITCOUNT",
+      RedisCommandKind::BitField => "BITFIELD",
+      RedisCommandKind::BitOp => "BITOP",
+      RedisCommandKind::BitPos => "BITPOS",
+      RedisCommandKind::BlPop => "BLPOP",
+      RedisCommandKind::BlMove => "BLMOVE",
+      RedisCommandKind::BrPop => "BRPOP",
+      RedisCommandKind::BrPopLPush => "BRPOPLPUSH",
+      RedisCommandKind::BzPopMin => "BZPOPMIN",
+      RedisCommandKind::BzPopMax => "BZPOPMAX",
+      RedisCommandKind::BzmPop => "BZMPOP",
+      RedisCommandKind::BlmPop => "BLMPOP",
+      RedisCommandKind::ClientID
+      | RedisCommandKind::ClientInfo
+      | RedisCommandKind::ClientKill
+      | RedisCommandKind::ClientList
+      | RedisCommandKind::ClientGetName
+      | RedisCommandKind::ClientPause
+      | RedisCommandKind::ClientUnpause
+      | RedisCommandKind::ClientUnblock
+      | RedisCommandKind::ClientReply
+      | RedisCommandKind::ClientSetname
+      | RedisCommandKind::ClientCaching
+      | RedisCommandKind::ClientTrackingInfo
+      | RedisCommandKind::ClientTracking
+      | RedisCommandKind::ClientGetRedir => "CLIENT",
+      RedisCommandKind::ClusterAddSlots
+      | RedisCommandKind::ClusterCountFailureReports
+      | RedisCommandKind::ClusterCountKeysInSlot
+      | RedisCommandKind::ClusterDelSlots
+      | RedisCommandKind::ClusterFailOver
+      | RedisCommandKind::ClusterForget
+      | RedisCommandKind::ClusterGetKeysInSlot
+      | RedisCommandKind::ClusterInfo
+      | RedisCommandKind::ClusterKeySlot
+      | RedisCommandKind::ClusterMeet
+      | RedisCommandKind::ClusterNodes
+      | RedisCommandKind::ClusterReplicate
+      | RedisCommandKind::ClusterReset
+      | RedisCommandKind::ClusterSaveConfig
+      | RedisCommandKind::ClusterSetConfigEpoch
+      | RedisCommandKind::ClusterSetSlot
+      | RedisCommandKind::ClusterReplicas
+      | RedisCommandKind::ClusterSlots
+      | RedisCommandKind::ClusterBumpEpoch
+      | RedisCommandKind::ClusterFlushSlots
+      | RedisCommandKind::ClusterMyID => "CLUSTER",
+      RedisCommandKind::ConfigGet
+      | RedisCommandKind::ConfigRewrite
+      | RedisCommandKind::ConfigSet
+      | RedisCommandKind::ConfigResetStat => "CONFIG",
+      RedisCommandKind::Copy => "COPY",
+      RedisCommandKind::DBSize => "DBSIZE",
+      RedisCommandKind::Decr => "DECR",
+      RedisCommandKind::DecrBy => "DECRBY",
+      RedisCommandKind::Del => "DEL",
+      RedisCommandKind::Discard => "DISCARD",
+      RedisCommandKind::Dump => "DUMP",
+      RedisCommandKind::Echo => "ECHO",
+      RedisCommandKind::Eval => "EVAL",
+      RedisCommandKind::EvalSha => "EVALSHA",
+      RedisCommandKind::Exec => "EXEC",
+      RedisCommandKind::Exists => "EXISTS",
+      RedisCommandKind::Expire => "EXPIRE",
+      RedisCommandKind::ExpireAt => "EXPIREAT",
+      RedisCommandKind::Failover => "FAILOVER",
+      RedisCommandKind::FlushAll => "FLUSHALL",
+      RedisCommandKind::_FlushAllCluster => "FLUSHALL",
+      RedisCommandKind::FlushDB => "FLUSHDB",
+      RedisCommandKind::GeoAdd => "GEOADD",
+      RedisCommandKind::GeoHash => "GEOHASH",
+      RedisCommandKind::GeoPos => "GEOPOS",
+      RedisCommandKind::GeoDist => "GEODIST",
+      RedisCommandKind::GeoRadius => "GEORADIUS",
+      RedisCommandKind::GeoRadiusByMember => "GEORADIUSBYMEMBER",
+      RedisCommandKind::GeoSearch => "GEOSEARCH",
+      RedisCommandKind::GeoSearchStore => "GEOSEARCHSTORE",
+      RedisCommandKind::Get => "GET",
+      RedisCommandKind::GetDel => "GETDEL",
+      RedisCommandKind::GetBit => "GETBIT",
+      RedisCommandKind::GetRange => "GETRANGE",
+      RedisCommandKind::GetSet => "GETSET",
+      RedisCommandKind::HDel => "HDEL",
+      RedisCommandKind::_Hello(_) => "HELLO",
+      RedisCommandKind::HExists => "HEXISTS",
+      RedisCommandKind::HGet => "HGET",
+      RedisCommandKind::HGetAll => "HGETALL",
+      RedisCommandKind::HIncrBy => "HINCRBY",
+      RedisCommandKind::HIncrByFloat => "HINCRBYFLOAT",
+      RedisCommandKind::HKeys => "HKEYS",
+      RedisCommandKind::HLen => "HLEN",
+      RedisCommandKind::HMGet => "HMGET",
+      RedisCommandKind::HMSet => "HMSET",
+      RedisCommandKind::HSet => "HSET",
+      RedisCommandKind::HSetNx => "HSETNX",
+      RedisCommandKind::HStrLen => "HSTRLEN",
+      RedisCommandKind::HRandField => "HRANDFIELD",
+      RedisCommandKind::HVals => "HVALS",
+      RedisCommandKind::Incr => "INCR",
+      RedisCommandKind::IncrBy => "INCRBY",
+      RedisCommandKind::IncrByFloat => "INCRBYFLOAT",
+      RedisCommandKind::Info => "INFO",
+      RedisCommandKind::Keys => "KEYS",
+      RedisCommandKind::LastSave => "LASTSAVE",
+      RedisCommandKind::LIndex => "LINDEX",
+      RedisCommandKind::LInsert => "LINSERT",
+      RedisCommandKind::LLen => "LLEN",
+      RedisCommandKind::LMove => "LMOVE",
+      RedisCommandKind::LPop => "LPOP",
+      RedisCommandKind::LPos => "LPOS",
+      RedisCommandKind::LPush => "LPUSH",
+      RedisCommandKind::LPushX => "LPUSHX",
+      RedisCommandKind::LRange => "LRANGE",
+      RedisCommandKind::LMPop => "LMPOP",
+      RedisCommandKind::LRem => "LREM",
+      RedisCommandKind::LSet => "LSET",
+      RedisCommandKind::LTrim => "LTRIM",
+      RedisCommandKind::Lcs => "LCS",
+      RedisCommandKind::MemoryDoctor => "MEMORY",
+      RedisCommandKind::MemoryHelp => "MEMORY",
+      RedisCommandKind::MemoryMallocStats => "MEMORY",
+      RedisCommandKind::MemoryPurge => "MEMORY",
+      RedisCommandKind::MemoryStats => "MEMORY",
+      RedisCommandKind::MemoryUsage => "MEMORY",
+      RedisCommandKind::Mget => "MGET",
+      RedisCommandKind::Migrate => "MIGRATE",
+      RedisCommandKind::Monitor => "MONITOR",
+      RedisCommandKind::Move => "MOVE",
+      RedisCommandKind::Mset => "MSET",
+      RedisCommandKind::Msetnx => "MSETNX",
+      RedisCommandKind::Multi => "MULTI",
+      RedisCommandKind::Object => "OBJECT",
+      RedisCommandKind::Persist => "PERSIST",
+      RedisCommandKind::Pexpire => "PEXPIRE",
+      RedisCommandKind::Pexpireat => "PEXPIREAT",
+      RedisCommandKind::Pfadd => "PFADD",
+      RedisCommandKind::Pfcount => "PFCOUNT",
+      RedisCommandKind::Pfmerge => "PFMERGE",
+      RedisCommandKind::Ping => "PING",
+      RedisCommandKind::Psetex => "PSETEX",
+      RedisCommandKind::Psubscribe => "PSUBSCRIBE",
+      RedisCommandKind::Pttl => "PTTL",
+      RedisCommandKind::Publish => "PUBLISH",
+      RedisCommandKind::Punsubscribe => "PUNSUBSCRIBE",
+      RedisCommandKind::Quit => "QUIT",
+      RedisCommandKind::Randomkey => "RANDOMKEY",
+      RedisCommandKind::Readonly => "READONLY",
+      RedisCommandKind::Readwrite => "READWRITE",
+      RedisCommandKind::Rename => "RENAME",
+      RedisCommandKind::Renamenx => "RENAMENX",
+      RedisCommandKind::Restore => "RESTORE",
+      RedisCommandKind::Role => "ROLE",
+      RedisCommandKind::Rpop => "RPOP",
+      RedisCommandKind::Rpoplpush => "RPOPLPUSH",
+      RedisCommandKind::Rpush => "RPUSH",
+      RedisCommandKind::Rpushx => "RPUSHX",
+      RedisCommandKind::Sadd => "SADD",
+      RedisCommandKind::Save => "SAVE",
+      RedisCommandKind::Scard => "SCARD",
+      RedisCommandKind::Sdiff => "SDIFF",
+      RedisCommandKind::Sdiffstore => "SDIFFSTORE",
+      RedisCommandKind::Select => "SELECT",
+      RedisCommandKind::Sentinel => "SENTINEL",
+      RedisCommandKind::Set => "SET",
+      RedisCommandKind::Setbit => "SETBIT",
+      RedisCommandKind::Setex => "SETEX",
+      RedisCommandKind::Setnx => "SETNX",
+      RedisCommandKind::Setrange => "SETRANGE",
+      RedisCommandKind::Shutdown => "SHUTDOWN",
+      RedisCommandKind::Sinter => "SINTER",
+      RedisCommandKind::Sinterstore => "SINTERSTORE",
+      RedisCommandKind::Sismember => "SISMEMBER",
+      RedisCommandKind::Replicaof => "REPLICAOF",
+      RedisCommandKind::Slowlog => "SLOWLOG",
+      RedisCommandKind::Smembers => "SMEMBERS",
+      RedisCommandKind::Smismember => "SMISMEMBER",
+      RedisCommandKind::Smove => "SMOVE",
+      RedisCommandKind::Sort => "SORT",
+      RedisCommandKind::SortRo => "SORT_RO",
+      RedisCommandKind::Spop => "SPOP",
+      RedisCommandKind::Srandmember => "SRANDMEMBER",
+      RedisCommandKind::Srem => "SREM",
+      RedisCommandKind::Strlen => "STRLEN",
+      RedisCommandKind::Subscribe => "SUBSCRIBE",
+      RedisCommandKind::Sunion => "SUNION",
+      RedisCommandKind::Sunionstore => "SUNIONSTORE",
+      RedisCommandKind::Swapdb => "SWAPDB",
+      RedisCommandKind::Sync => "SYNC",
+      RedisCommandKind::Time => "TIME",
+      RedisCommandKind::Touch => "TOUCH",
+      RedisCommandKind::Ttl => "TTL",
+      RedisCommandKind::Type => "TYPE",
+      RedisCommandKind::Unsubscribe => "UNSUBSCRIBE",
+      RedisCommandKind::Unlink => "UNLINK",
+      RedisCommandKind::Unwatch => "UNWATCH",
+      RedisCommandKind::Wait => "WAIT",
+      RedisCommandKind::Watch => "WATCH",
+      RedisCommandKind::XinfoConsumers | RedisCommandKind::XinfoGroups | RedisCommandKind::XinfoStream => "XINFO",
+      RedisCommandKind::Xadd => "XADD",
+      RedisCommandKind::Xtrim => "XTRIM",
+      RedisCommandKind::Xdel => "XDEL",
+      RedisCommandKind::Xrange => "XRANGE",
+      RedisCommandKind::Xrevrange => "XREVRANGE",
+      RedisCommandKind::Xlen => "XLEN",
+      RedisCommandKind::Xread => "XREAD",
+      RedisCommandKind::Xgroupcreate
+      | RedisCommandKind::XgroupCreateConsumer
+      | RedisCommandKind::XgroupDelConsumer
+      | RedisCommandKind::XgroupDestroy
+      | RedisCommandKind::XgroupSetId => "XGROUP",
+      RedisCommandKind::Xreadgroup => "XREADGROUP",
+      RedisCommandKind::Xack => "XACK",
+      RedisCommandKind::Xclaim => "XCLAIM",
+      RedisCommandKind::Xautoclaim => "XAUTOCLAIM",
+      RedisCommandKind::Xpending => "XPENDING",
+      RedisCommandKind::Zadd => "ZADD",
+      RedisCommandKind::Zcard => "ZCARD",
+      RedisCommandKind::Zcount => "ZCOUNT",
+      RedisCommandKind::Zdiff => "ZDIFF",
+      RedisCommandKind::Zdiffstore => "ZDIFFSTORE",
+      RedisCommandKind::Zincrby => "ZINCRBY",
+      RedisCommandKind::Zinter => "ZINTER",
+      RedisCommandKind::Zinterstore => "ZINTERSTORE",
+      RedisCommandKind::Zlexcount => "ZLEXCOUNT",
+      RedisCommandKind::Zrandmember => "ZRANDMEMBER",
+      RedisCommandKind::Zrange => "ZRANGE",
+      RedisCommandKind::Zrangestore => "ZRANGESTORE",
+      RedisCommandKind::Zrangebylex => "ZRANGEBYLEX",
+      RedisCommandKind::Zrangebyscore => "ZRANGEBYSCORE",
+      RedisCommandKind::Zrank => "ZRANK",
+      RedisCommandKind::Zrem => "ZREM",
+      RedisCommandKind::Zremrangebylex => "ZREMRANGEBYLEX",
+      RedisCommandKind::Zremrangebyrank => "ZREMRANGEBYRANK",
+      RedisCommandKind::Zremrangebyscore => "ZREMRANGEBYSCORE",
+      RedisCommandKind::Zrevrange => "ZREVRANGE",
+      RedisCommandKind::Zrevrangebylex => "ZREVRANGEBYLEX",
+      RedisCommandKind::Zrevrangebyscore => "ZREVRANGEBYSCORE",
+      RedisCommandKind::Zrevrank => "ZREVRANK",
+      RedisCommandKind::Zscore => "ZSCORE",
+      RedisCommandKind::Zmscore => "ZMSCORE",
+      RedisCommandKind::Zunion => "ZUNION",
+      RedisCommandKind::Zunionstore => "ZUNIONSTORE",
+      RedisCommandKind::Zpopmax => "ZPOPMAX",
+      RedisCommandKind::Zpopmin => "ZPOPMIN",
+      RedisCommandKind::Zmpop => "ZMPOP",
+      RedisCommandKind::ScriptDebug
+      | RedisCommandKind::ScriptExists
+      | RedisCommandKind::ScriptFlush
+      | RedisCommandKind::ScriptKill
+      | RedisCommandKind::ScriptLoad
+      | RedisCommandKind::_ScriptFlushCluster
+      | RedisCommandKind::_ScriptKillCluster
+      | RedisCommandKind::_ScriptLoadCluster => "SCRIPT",
+      RedisCommandKind::Spublish => "SPUBLISH",
+      RedisCommandKind::Ssubscribe => "SSUBSCRIBE",
+      RedisCommandKind::Sunsubscribe => "SUNSUBSCRIBE",
+      RedisCommandKind::Scan => "SCAN",
+      RedisCommandKind::Sscan => "SSCAN",
+      RedisCommandKind::Hscan => "HSCAN",
+      RedisCommandKind::Zscan => "ZSCAN",
+      RedisCommandKind::Fcall => "FCALL",
+      RedisCommandKind::FcallRO => "FCALL_RO",
+      RedisCommandKind::FunctionDelete
+      | RedisCommandKind::FunctionDump
+      | RedisCommandKind::FunctionFlush
+      | RedisCommandKind::FunctionKill
+      | RedisCommandKind::FunctionList
+      | RedisCommandKind::FunctionLoad
+      | RedisCommandKind::FunctionRestore
+      | RedisCommandKind::FunctionStats
+      | RedisCommandKind::_FunctionFlushCluster
+      | RedisCommandKind::_FunctionRestoreCluster
+      | RedisCommandKind::_FunctionDeleteCluster
+      | RedisCommandKind::_FunctionLoadCluster => "FUNCTION",
+      RedisCommandKind::PubsubChannels
+      | RedisCommandKind::PubsubNumpat
+      | RedisCommandKind::PubsubNumsub
+      | RedisCommandKind::PubsubShardchannels
+      | RedisCommandKind::PubsubShardnumsub => "PUBSUB",
+      RedisCommandKind::_AuthAllCluster => "AUTH",
+      RedisCommandKind::_HelloAllCluster(_) => "HELLO",
+      RedisCommandKind::_ClientTrackingCluster => "CLIENT",
+      RedisCommandKind::JsonArrAppend => "JSON.ARRAPPEND",
+      RedisCommandKind::JsonArrIndex => "JSON.ARRINDEX",
+      RedisCommandKind::JsonArrInsert => "JSON.ARRINSERT",
+      RedisCommandKind::JsonArrLen => "JSON.ARRLEN",
+      RedisCommandKind::JsonArrPop => "JSON.ARRPOP",
+      RedisCommandKind::JsonArrTrim => "JSON.ARRTRIM",
+      RedisCommandKind::JsonClear => "JSON.CLEAR",
+      RedisCommandKind::JsonDebugMemory => "JSON.DEBUG",
+      RedisCommandKind::JsonDel => "JSON.DEL",
+      RedisCommandKind::JsonGet => "JSON.GET",
+      RedisCommandKind::JsonMerge => "JSON.MERGE",
+      RedisCommandKind::JsonMGet => "JSON.MGET",
+      RedisCommandKind::JsonMSet => "JSON.MSET",
+      RedisCommandKind::JsonNumIncrBy => "JSON.NUMINCRBY",
+      RedisCommandKind::JsonObjKeys => "JSON.OBJKEYS",
+      RedisCommandKind::JsonObjLen => "JSON.OBJLEN",
+      RedisCommandKind::JsonResp => "JSON.RESP",
+      RedisCommandKind::JsonSet => "JSON.SET",
+      RedisCommandKind::JsonStrAppend => "JSON.STRAPPEND",
+      RedisCommandKind::JsonStrLen => "JSON.STRLEN",
+      RedisCommandKind::JsonToggle => "JSON.TOGGLE",
+      RedisCommandKind::JsonType => "JSON.TYPE",
+      RedisCommandKind::TsAdd => "TS.ADD",
+      RedisCommandKind::TsAlter => "TS.ALTER",
+      RedisCommandKind::TsCreate => "TS.CREATE",
+      RedisCommandKind::TsCreateRule => "TS.CREATERULE",
+      RedisCommandKind::TsDecrBy => "TS.DECRBY",
+      RedisCommandKind::TsDel => "TS.DEL",
+      RedisCommandKind::TsDeleteRule => "TS.DELETERULE",
+      RedisCommandKind::TsGet => "TS.GET",
+      RedisCommandKind::TsIncrBy => "TS.INCRBY",
+      RedisCommandKind::TsInfo => "TS.INFO",
+      RedisCommandKind::TsMAdd => "TS.MADD",
+      RedisCommandKind::TsMGet => "TS.MGET",
+      RedisCommandKind::TsMRange => "TS.MRANGE",
+      RedisCommandKind::TsMRevRange => "TS.MREVRANGE",
+      RedisCommandKind::TsQueryIndex => "TS.QUERYINDEX",
+      RedisCommandKind::TsRange => "TS.RANGE",
+      RedisCommandKind::TsRevRange => "TS.REVRANGE",
+      RedisCommandKind::FtList => "FT._LIST",
+      RedisCommandKind::FtAggregate => "FT.AGGREGATE",
+      RedisCommandKind::FtSearch => "FT.SEARCH",
+      RedisCommandKind::FtCreate => "FT.CREATE",
+      RedisCommandKind::FtAlter => "FT.ALTER",
+      RedisCommandKind::FtAliasAdd => "FT.ALIASADD",
+      RedisCommandKind::FtAliasDel => "FT.ALIASDEL",
+      RedisCommandKind::FtAliasUpdate => "FT.ALIASUPDATE",
+      RedisCommandKind::FtConfigGet => "FT.CONFIG",
+      RedisCommandKind::FtConfigSet => "FT.CONFIG",
+      RedisCommandKind::FtCursorDel => "FT.CURSOR",
+      RedisCommandKind::FtCursorRead => "FT.CURSOR",
+      RedisCommandKind::FtDictAdd => "FT.DICTADD",
+      RedisCommandKind::FtDictDel => "FT.DICTDEL",
+      RedisCommandKind::FtDictDump => "FT.DICTDUMP",
+      RedisCommandKind::FtDropIndex => "FT.DROPINDEX",
+      RedisCommandKind::FtExplain => "FT.EXPLAIN",
+      RedisCommandKind::FtInfo => "FT.INFO",
+      RedisCommandKind::FtSpellCheck => "FT.SPELLCHECK",
+      RedisCommandKind::FtSugAdd => "FT.SUGADD",
+      RedisCommandKind::FtSugDel => "FT.SUGDEL",
+      RedisCommandKind::FtSugGet => "FT.SUGGET",
+      RedisCommandKind::FtSugLen => "FT.SUGLEN",
+      RedisCommandKind::FtSynDump => "FT.SYNDUMP",
+      RedisCommandKind::FtSynUpdate => "FT.SYNUPDATE",
+      RedisCommandKind::FtTagVals => "FT.TAGVALS",
+      RedisCommandKind::_Custom(ref kind) => return kind.cmd.clone(),
+    };
+
+    client_utils::static_str(s)
+  }
+
+  /// Read the optional subcommand string for a command.
+  pub fn subcommand_str(&self) -> Option<Str> {
+    let s = match *self {
+      RedisCommandKind::ScriptDebug => "DEBUG",
+      RedisCommandKind::ScriptLoad => "LOAD",
+      RedisCommandKind::ScriptKill => "KILL",
+      RedisCommandKind::ScriptFlush => "FLUSH",
+      RedisCommandKind::ScriptExists => "EXISTS",
+      RedisCommandKind::_ScriptFlushCluster => "FLUSH",
+      RedisCommandKind::_ScriptLoadCluster => "LOAD",
+      RedisCommandKind::_ScriptKillCluster => "KILL",
+      RedisCommandKind::AclLoad => "LOAD",
+      RedisCommandKind::AclSave => "SAVE",
+      RedisCommandKind::AclList => "LIST",
+      RedisCommandKind::AclUsers => "USERS",
+      RedisCommandKind::AclGetUser => "GETUSER",
+      RedisCommandKind::AclSetUser => "SETUSER",
+      RedisCommandKind::AclDelUser => "DELUSER",
+      RedisCommandKind::AclCat => "CAT",
+      RedisCommandKind::AclGenPass => "GENPASS",
+      RedisCommandKind::AclWhoAmI => "WHOAMI",
+      RedisCommandKind::AclLog => "LOG",
+      RedisCommandKind::AclHelp => "HELP",
+      RedisCommandKind::ClusterAddSlots => "ADDSLOTS",
+      RedisCommandKind::ClusterCountFailureReports => "COUNT-FAILURE-REPORTS",
+      RedisCommandKind::ClusterCountKeysInSlot => "COUNTKEYSINSLOT",
+      RedisCommandKind::ClusterDelSlots => "DELSLOTS",
+      RedisCommandKind::ClusterFailOver => "FAILOVER",
+      RedisCommandKind::ClusterForget => "FORGET",
+      RedisCommandKind::ClusterGetKeysInSlot => "GETKEYSINSLOT",
+      RedisCommandKind::ClusterInfo => "INFO",
+      RedisCommandKind::ClusterKeySlot => "KEYSLOT",
+      RedisCommandKind::ClusterMeet => "MEET",
+      RedisCommandKind::ClusterNodes => "NODES",
+      RedisCommandKind::ClusterReplicate => "REPLICATE",
+      RedisCommandKind::ClusterReset => "RESET",
+      RedisCommandKind::ClusterSaveConfig => "SAVECONFIG",
+      RedisCommandKind::ClusterSetConfigEpoch => "SET-CONFIG-EPOCH",
+      RedisCommandKind::ClusterSetSlot => "SETSLOT",
+      RedisCommandKind::ClusterReplicas => "REPLICAS",
+      RedisCommandKind::ClusterSlots => "SLOTS",
+      RedisCommandKind::ClusterBumpEpoch => "BUMPEPOCH",
+      RedisCommandKind::ClusterFlushSlots => "FLUSHSLOTS",
+      RedisCommandKind::ClusterMyID => "MYID",
+      RedisCommandKind::ClientID => "ID",
+      RedisCommandKind::ClientInfo => "INFO",
+      RedisCommandKind::ClientKill => "KILL",
+      RedisCommandKind::ClientList => "LIST",
+      RedisCommandKind::ClientGetName => "GETNAME",
+      RedisCommandKind::ClientPause => "PAUSE",
+      RedisCommandKind::ClientUnpause => "UNPAUSE",
+      RedisCommandKind::ClientUnblock => "UNBLOCK",
+      RedisCommandKind::ClientReply => "REPLY",
+      RedisCommandKind::ClientSetname => "SETNAME",
+      RedisCommandKind::ConfigGet => "GET",
+      RedisCommandKind::ConfigRewrite => "REWRITE",
+      RedisCommandKind::ClientGetRedir => "GETREDIR",
+      RedisCommandKind::ClientTracking => "TRACKING",
+      RedisCommandKind::ClientTrackingInfo => "TRACKINGINFO",
+      RedisCommandKind::ClientCaching => "CACHING",
+      RedisCommandKind::ConfigSet => "SET",
+      RedisCommandKind::ConfigResetStat => "RESETSTAT",
+      RedisCommandKind::MemoryDoctor => "DOCTOR",
+      RedisCommandKind::MemoryHelp => "HELP",
+      RedisCommandKind::MemoryUsage => "USAGE",
+      RedisCommandKind::MemoryMallocStats => "MALLOC-STATS",
+      RedisCommandKind::MemoryStats => "STATS",
+      RedisCommandKind::MemoryPurge => "PURGE",
+      RedisCommandKind::XinfoConsumers => "CONSUMERS",
+      RedisCommandKind::XinfoGroups => "GROUPS",
+      RedisCommandKind::XinfoStream => "STREAM",
+      RedisCommandKind::Xgroupcreate => "CREATE",
+      RedisCommandKind::XgroupCreateConsumer => "CREATECONSUMER",
+      RedisCommandKind::XgroupDelConsumer => "DELCONSUMER",
+      RedisCommandKind::XgroupDestroy => "DESTROY",
+      RedisCommandKind::XgroupSetId => "SETID",
+      RedisCommandKind::FunctionDelete => "DELETE",
+      RedisCommandKind::FunctionDump => "DUMP",
+      RedisCommandKind::FunctionFlush => "FLUSH",
+      RedisCommandKind::FunctionKill => "KILL",
+      RedisCommandKind::FunctionList => "LIST",
+      RedisCommandKind::FunctionLoad => "LOAD",
+      RedisCommandKind::FunctionRestore => "RESTORE",
+      RedisCommandKind::FunctionStats => "STATS",
+      RedisCommandKind::PubsubChannels => "CHANNELS",
+      RedisCommandKind::PubsubNumpat => "NUMPAT",
+      RedisCommandKind::PubsubNumsub => "NUMSUB",
+      RedisCommandKind::PubsubShardchannels => "SHARDCHANNELS",
+      RedisCommandKind::PubsubShardnumsub => "SHARDNUMSUB",
+      RedisCommandKind::_FunctionLoadCluster => "LOAD",
+      RedisCommandKind::_FunctionFlushCluster => "FLUSH",
+      RedisCommandKind::_FunctionDeleteCluster => "DELETE",
+      RedisCommandKind::_FunctionRestoreCluster => "RESTORE",
+      RedisCommandKind::_ClientTrackingCluster => "TRACKING",
+      RedisCommandKind::JsonDebugMemory => "MEMORY",
+      RedisCommandKind::FtConfigGet => "GET",
+      RedisCommandKind::FtConfigSet => "SET",
+      RedisCommandKind::FtCursorDel => "DEL",
+      RedisCommandKind::FtCursorRead => "READ",
+      _ => return None,
+    };
+
+    Some(utils::static_str(s))
+  }
+
+  pub fn use_random_cluster_node(&self) -> bool {
+    matches!(
+      *self,
+      RedisCommandKind::Publish
+        | RedisCommandKind::Ping
+        | RedisCommandKind::Info
+        | RedisCommandKind::Scan
+        | RedisCommandKind::FlushAll
+        | RedisCommandKind::FlushDB
+    )
+  }
+
+  pub fn is_blocking(&self) -> bool {
+    match *self {
+      RedisCommandKind::BlPop
+      | RedisCommandKind::BrPop
+      | RedisCommandKind::BrPopLPush
+      | RedisCommandKind::BlMove
+      | RedisCommandKind::BzPopMin
+      | RedisCommandKind::BzPopMax
+      | RedisCommandKind::BlmPop
+      | RedisCommandKind::BzmPop
+      | RedisCommandKind::Fcall
+      | RedisCommandKind::FcallRO
+      | RedisCommandKind::Wait => true,
+      // default is false, but can be changed by the BLOCKING args. the RedisCommand::can_pipeline function checks the
+      // args too.
+      RedisCommandKind::Xread | RedisCommandKind::Xreadgroup => false,
+      RedisCommandKind::_Custom(ref kind) => kind.blocking,
+      _ => false,
+    }
+  }
+
+  pub fn force_all_cluster_nodes(&self) -> bool {
+    matches!(
+      *self,
+      RedisCommandKind::_FlushAllCluster
+        | RedisCommandKind::_AuthAllCluster
+        | RedisCommandKind::_ScriptFlushCluster
+        | RedisCommandKind::_ScriptKillCluster
+        | RedisCommandKind::_HelloAllCluster(_)
+        | RedisCommandKind::_ClientTrackingCluster
+        | RedisCommandKind::_ScriptLoadCluster
+        | RedisCommandKind::_FunctionFlushCluster
+        | RedisCommandKind::_FunctionDeleteCluster
+        | RedisCommandKind::_FunctionRestoreCluster
+        | RedisCommandKind::_FunctionLoadCluster
+    )
+  }
+
+  pub fn should_flush(&self) -> bool {
+    matches!(
+      *self,
+      RedisCommandKind::Quit
+        | RedisCommandKind::Shutdown
+        | RedisCommandKind::Ping
+        | RedisCommandKind::Auth
+        | RedisCommandKind::_Hello(_)
+        | RedisCommandKind::Exec
+        | RedisCommandKind::Discard
+        | RedisCommandKind::Eval
+        | RedisCommandKind::EvalSha
+        | RedisCommandKind::Fcall
+        | RedisCommandKind::FcallRO
+        | RedisCommandKind::_Custom(_)
+    )
+  }
+
+  pub fn can_pipeline(&self) -> bool {
+    if self.is_blocking() || self.closes_connection() {
+      false
+    } else {
+      match self {
+        // make it easier to handle multiple potentially out-of-band responses
+        RedisCommandKind::Subscribe
+        | RedisCommandKind::Unsubscribe
+        | RedisCommandKind::Psubscribe
+        | RedisCommandKind::Punsubscribe
+        | RedisCommandKind::Ssubscribe
+        | RedisCommandKind::Sunsubscribe
+        // https://redis.io/commands/eval#evalsha-in-the-context-of-pipelining
+        | RedisCommandKind::Eval
+        | RedisCommandKind::EvalSha
+        | RedisCommandKind::Auth
+        | RedisCommandKind::Fcall
+        | RedisCommandKind::FcallRO
+        // makes it easier to avoid decoding in-flight responses with the wrong codec logic
+        | RedisCommandKind::_Hello(_) => false,
+        _ => true,
+      }
+    }
+  }
+
+  pub fn is_eval(&self) -> bool {
+    matches!(
+      *self,
+      RedisCommandKind::EvalSha | RedisCommandKind::Eval | RedisCommandKind::Fcall | RedisCommandKind::FcallRO
+    )
+  }
+}
+
+pub struct RedisCommand {
+  /// The command and optional subcommand name.
+  pub kind:                   RedisCommandKind,
+  /// The policy to apply when handling the response.
+  pub response:               ResponseKind,
+  /// The policy to use when hashing the arguments for cluster routing.
+  pub hasher:                 ClusterHash,
+  /// The provided arguments.
+  ///
+  /// Some commands store arguments differently. Callers should use `self.args()` to account for this.
+  pub arguments:              Vec<RedisValue>,
+  /// A oneshot sender used to communicate with the router.
+  pub router_tx:              RefCount<Mutex<Option<RouterSender>>>,
+  /// The number of times the command has been written to a socket.
+  pub write_attempts:         u32,
+  /// The number of write attempts remaining.
+  pub attempts_remaining:     u32,
+  /// The number of cluster redirections remaining.
+  pub redirections_remaining: u32,
+  /// Whether the command can be pipelined.
+  ///
+  /// Also used for commands like XREAD that block based on an argument.
+  pub can_pipeline:           bool,
+  /// Whether to skip backpressure checks.
+  pub skip_backpressure:      bool,
+  /// Whether to fail fast without retries if the connection ever closes unexpectedly.
+  pub fail_fast:              bool,
+  /// The internal ID of a transaction.
+  pub transaction_id:         Option<u64>,
+  /// The timeout duration provided by the `with_options` interface.
+  pub timeout_dur:            Option<Duration>,
+  /// Whether the command has timed out from the perspective of the caller.
+  pub timed_out:              RefCount<AtomicBool>,
+  /// A timestamp of when the command was last written to the socket.
+  pub network_start:          Option<Instant>,
+  /// Whether to route the command to a replica, if possible.
+  pub use_replica:            bool,
+  /// Only send the command to the provided server.
+  pub cluster_node:           Option<Server>,
+  /// A timestamp of when the command was first created from the public interface.
+  #[cfg(feature = "metrics")]
+  pub created:                Instant,
+  /// Tracing state that has to carry over across writer/reader tasks to track certain fields (response size, etc).
+  #[cfg(feature = "partial-tracing")]
+  pub traces:                 CommandTraces,
+  /// A counter to differentiate unique commands.
+  #[cfg(feature = "debug-ids")]
+  pub counter:                usize,
+  /// Whether to send a `CLIENT CACHING yes|no` before the command.
+  #[cfg(feature = "i-tracking")]
+  pub caching:                Option<bool>,
+}
+
+impl fmt::Debug for RedisCommand {
+  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    let mut formatter = f.debug_struct("RedisCommand");
+    formatter
+      .field("command", &self.kind.to_str_debug())
+      .field("attempts_remaining", &self.attempts_remaining)
+      .field("redirections_remaining", &self.redirections_remaining)
+      .field("can_pipeline", &self.can_pipeline)
+      .field("write_attempts", &self.write_attempts)
+      .field("timeout_dur", &self.timeout_dur)
+      .field("no_backpressure", &self.skip_backpressure)
+      .field("cluster_node", &self.cluster_node)
+      .field("cluster_hash", &self.hasher)
+      .field("use_replica", &self.use_replica)
+      .field("fail_fast", &self.fail_fast);
+
+    #[cfg(feature = "network-logs")]
+    formatter.field("arguments", &self.args());
+
+    formatter.finish()
+  }
+}
+
+impl fmt::Display for RedisCommand {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    write!(f, "{}", self.kind.to_str_debug())
+  }
+}
+
+impl From<RedisCommandKind> for RedisCommand {
+  fn from(kind: RedisCommandKind) -> Self {
+    (kind, Vec::new()).into()
+  }
+}
+
+impl From<(RedisCommandKind, Vec<RedisValue>)> for RedisCommand {
+  fn from((kind, arguments): (RedisCommandKind, Vec<RedisValue>)) -> Self {
+    RedisCommand {
+      kind,
+      arguments,
+      timed_out: RefCount::new(AtomicBool::new(false)),
+      timeout_dur: None,
+      response: ResponseKind::Respond(None),
+      hasher: ClusterHash::default(),
+      router_tx: RefCount::new(Mutex::new(None)),
+      attempts_remaining: 0,
+      redirections_remaining: 0,
+      can_pipeline: true,
+      skip_backpressure: false,
+      transaction_id: None,
+      use_replica: false,
+      cluster_node: None,
+      network_start: None,
+      write_attempts: 0,
+      fail_fast: false,
+      #[cfg(feature = "metrics")]
+      created: Instant::now(),
+      #[cfg(feature = "partial-tracing")]
+      traces: CommandTraces::default(),
+      #[cfg(feature = "debug-ids")]
+      counter: command_counter(),
+      #[cfg(feature = "i-tracking")]
+      caching: None,
+    }
+  }
+}
+
+impl From<(RedisCommandKind, Vec<RedisValue>, ResponseSender)> for RedisCommand {
+  fn from((kind, arguments, tx): (RedisCommandKind, Vec<RedisValue>, ResponseSender)) -> Self {
+    RedisCommand {
+      kind,
+      arguments,
+      response: ResponseKind::Respond(Some(tx)),
+      timed_out: RefCount::new(AtomicBool::new(false)),
+      timeout_dur: None,
+      hasher: ClusterHash::default(),
+      router_tx: RefCount::new(Mutex::new(None)),
+      attempts_remaining: 0,
+      redirections_remaining: 0,
+      can_pipeline: true,
+      skip_backpressure: false,
+      transaction_id: None,
+      use_replica: false,
+      cluster_node: None,
+      network_start: None,
+      write_attempts: 0,
+      fail_fast: false,
+      #[cfg(feature = "metrics")]
+      created: Instant::now(),
+      #[cfg(feature = "partial-tracing")]
+      traces: CommandTraces::default(),
+      #[cfg(feature = "debug-ids")]
+      counter: command_counter(),
+      #[cfg(feature = "i-tracking")]
+      caching: None,
+    }
+  }
+}
+
+impl From<(RedisCommandKind, Vec<RedisValue>, ResponseKind)> for RedisCommand {
+  fn from((kind, arguments, response): (RedisCommandKind, Vec<RedisValue>, ResponseKind)) -> Self {
+    RedisCommand {
+      kind,
+      arguments,
+      response,
+      timed_out: RefCount::new(AtomicBool::new(false)),
+      timeout_dur: None,
+      hasher: ClusterHash::default(),
+      router_tx: RefCount::new(Mutex::new(None)),
+      attempts_remaining: 0,
+      redirections_remaining: 0,
+      can_pipeline: true,
+      skip_backpressure: false,
+      transaction_id: None,
+      use_replica: false,
+      cluster_node: None,
+      network_start: None,
+      write_attempts: 0,
+      fail_fast: false,
+      #[cfg(feature = "metrics")]
+      created: Instant::now(),
+      #[cfg(feature = "partial-tracing")]
+      traces: CommandTraces::default(),
+      #[cfg(feature = "debug-ids")]
+      counter: command_counter(),
+      #[cfg(feature = "i-tracking")]
+      caching: None,
+    }
+  }
+}
+
+impl RedisCommand {
+  /// Create a new command without a response handling policy.
+  pub fn new(kind: RedisCommandKind, arguments: Vec<RedisValue>) -> Self {
+    RedisCommand {
+      kind,
+      arguments,
+      timed_out: RefCount::new(AtomicBool::new(false)),
+      timeout_dur: None,
+      response: ResponseKind::Respond(None),
+      hasher: ClusterHash::default(),
+      router_tx: RefCount::new(Mutex::new(None)),
+      attempts_remaining: 0,
+      redirections_remaining: 0,
+      can_pipeline: true,
+      skip_backpressure: false,
+      transaction_id: None,
+      use_replica: false,
+      cluster_node: None,
+      network_start: None,
+      write_attempts: 0,
+      fail_fast: false,
+      #[cfg(feature = "metrics")]
+      created: Instant::now(),
+      #[cfg(feature = "partial-tracing")]
+      traces: CommandTraces::default(),
+      #[cfg(feature = "debug-ids")]
+      counter: command_counter(),
+      #[cfg(feature = "i-tracking")]
+      caching: None,
+    }
+  }
+
+  /// Create a new empty `ASKING` command.
+  pub fn new_asking(hash_slot: u16) -> Self {
+    RedisCommand {
+      kind:                                       RedisCommandKind::Asking,
+      hasher:                                     ClusterHash::Custom(hash_slot),
+      arguments:                                  Vec::new(),
+      timed_out:                                  RefCount::new(AtomicBool::new(false)),
+      timeout_dur:                                None,
+      response:                                   ResponseKind::Respond(None),
+      router_tx:                                  RefCount::new(Mutex::new(None)),
+      attempts_remaining:                         0,
+      redirections_remaining:                     0,
+      can_pipeline:                               true,
+      skip_backpressure:                          false,
+      transaction_id:                             None,
+      use_replica:                                false,
+      cluster_node:                               None,
+      network_start:                              None,
+      write_attempts:                             0,
+      fail_fast:                                  false,
+      #[cfg(feature = "metrics")]
+      created:                                    Instant::now(),
+      #[cfg(feature = "partial-tracing")]
+      traces:                                     CommandTraces::default(),
+      #[cfg(feature = "debug-ids")]
+      counter:                                    command_counter(),
+      #[cfg(feature = "i-tracking")]
+      caching:                                    None,
+    }
+  }
+
+  /// Whether to pipeline the command.
+  pub fn should_auto_pipeline(&self, inner: &RefCount<RedisClientInner>, force: bool) -> bool {
+    let should_pipeline = force
+      || (inner.is_pipelined()
+      && self.can_pipeline
+      && self.kind.can_pipeline()
+      && !self.blocks_connection()
+      && !self.is_all_cluster_nodes()
+      // disable pipelining for transactions to handle ASK errors or support the `abort_on_error` logic
+      && self.transaction_id.is_none());
+
+    _trace!(
+      inner,
+      "Pipeline check {}: {}",
+      self.kind.to_str_debug(),
+      should_pipeline
+    );
+    should_pipeline
+  }
+
+  /// Whether the command should be sent to all cluster nodes concurrently.
+  pub fn is_all_cluster_nodes(&self) -> bool {
+    self.kind.force_all_cluster_nodes()
+      || match self.kind {
+        // since we don't know the hash slot we send this to all nodes
+        RedisCommandKind::Sunsubscribe => self.arguments.is_empty(),
+        _ => false,
+      }
+  }
+
+  /// Whether errors writing the command should be returned to the caller.
+  pub fn should_finish_with_error(&self, inner: &RefCount<RedisClientInner>) -> bool {
+    self.fail_fast || self.attempts_remaining == 0 || inner.policy.read().is_none()
+  }
+
+  /// Increment and check the number of write attempts.
+  pub fn decr_check_attempted(&mut self) -> Result<(), RedisError> {
+    if self.attempts_remaining == 0 {
+      Err(RedisError::new(
+        RedisErrorKind::Unknown,
+        "Too many failed write attempts.",
+      ))
+    } else {
+      self.attempts_remaining -= 1;
+      Ok(())
+    }
+  }
+
+  pub fn decr_check_redirections(&mut self) -> Result<(), RedisError> {
+    if self.redirections_remaining == 0 {
+      Err(RedisError::new(RedisErrorKind::Unknown, "Too many redirections."))
+    } else {
+      self.redirections_remaining -= 1;
+      Ok(())
+    }
+  }
+
+  /// Read the arguments associated with the command.
+  pub fn args(&self) -> &Vec<RedisValue> {
+    match self.response {
+      ResponseKind::ValueScan(ref inner) => &inner.args,
+      ResponseKind::KeyScan(ref inner) => &inner.args,
+      _ => &self.arguments,
+    }
+  }
+
+  /// Whether the command blocks the connection.
+  pub fn blocks_connection(&self) -> bool {
+    self.transaction_id.is_none()
+      && (self.kind.is_blocking()
+        || match self.kind {
+          RedisCommandKind::Xread | RedisCommandKind::Xreadgroup => !self.can_pipeline,
+          _ => false,
+        })
+  }
+
+  /// Whether the command may receive response frames.
+  ///
+  /// Currently, the pubsub subscription commands (other than `SSUBSCRIBE`) all fall into this category since their
+  /// responses arrive out-of-band.
+  // `SSUBSCRIBE` is not included here so that we can follow cluster redirections. this works as long as we never
+  // pipeline `SSUBSCRIBE`.
+  pub fn has_no_responses(&self) -> bool {
+    matches!(
+      self.kind,
+      RedisCommandKind::Subscribe
+        | RedisCommandKind::Unsubscribe
+        | RedisCommandKind::Psubscribe
+        | RedisCommandKind::Punsubscribe
+        | RedisCommandKind::Sunsubscribe
+    )
+  }
+
+  /// Take the arguments from this command.
+  pub fn take_args(&mut self) -> Vec<RedisValue> {
+    match self.response {
+      ResponseKind::ValueScan(ref mut inner) => inner.args.drain(..).collect(),
+      ResponseKind::KeyScan(ref mut inner) => inner.args.drain(..).collect(),
+      _ => self.arguments.drain(..).collect(),
+    }
+  }
+
+  /// Take the response handler, replacing it with `ResponseKind::Skip`.
+  pub fn take_response(&mut self) -> ResponseKind {
+    mem::replace(&mut self.response, ResponseKind::Skip)
+  }
+
+  /// Create a channel on which to block the router, returning the receiver.
+  pub fn create_router_channel(&self) -> OneshotReceiver<RouterResponse> {
+    let (tx, rx) = oneshot_channel();
+    let mut guard = self.router_tx.lock();
+    *guard = Some(tx);
+    rx
+  }
+
+  /// Send a message to unblock the router loop, if necessary.
+  pub fn respond_to_router(&self, inner: &RefCount<RedisClientInner>, cmd: RouterResponse) {
+    #[allow(unused_mut)]
+    if let Some(mut tx) = self.router_tx.lock().take() {
+      if tx.send(cmd).is_err() {
+        _debug!(inner, "Failed to unblock router loop.");
+      }
+    }
+  }
+
+  /// Take the router sender from the command.
+  pub fn take_router_tx(&self) -> Option<RouterSender> {
+    self.router_tx.lock().take()
+  }
+
+  /// Whether the command has a channel to the router.
+  pub fn has_router_channel(&self) -> bool {
+    self.router_tx.lock().is_some()
+  }
+
+  /// Clone the command, supporting commands with shared response state.
+  ///
+  /// Note: this will **not** clone the router channel.
+  pub fn duplicate(&self, response: ResponseKind) -> Self {
+    RedisCommand {
+      timed_out: RefCount::new(AtomicBool::new(false)),
+      kind: self.kind.clone(),
+      arguments: self.arguments.clone(),
+      hasher: self.hasher.clone(),
+      transaction_id: self.transaction_id,
+      attempts_remaining: self.attempts_remaining,
+      redirections_remaining: self.redirections_remaining,
+      timeout_dur: self.timeout_dur,
+      can_pipeline: self.can_pipeline,
+      skip_backpressure: self.skip_backpressure,
+      router_tx: self.router_tx.clone(),
+      cluster_node: self.cluster_node.clone(),
+      fail_fast: self.fail_fast,
+      response,
+      use_replica: self.use_replica,
+      write_attempts: self.write_attempts,
+      network_start: self.network_start,
+      #[cfg(feature = "metrics")]
+      created: Instant::now(),
+      #[cfg(feature = "partial-tracing")]
+      traces: CommandTraces::default(),
+      #[cfg(feature = "debug-ids")]
+      counter: command_counter(),
+      #[cfg(feature = "i-tracking")]
+      caching: self.caching,
+    }
+  }
+
+  /// Inherit connection and perf settings from the client.
+  pub fn inherit_options(&mut self, inner: &RefCount<RedisClientInner>) {
+    if self.attempts_remaining == 0 {
+      self.attempts_remaining = inner.connection.max_command_attempts;
+    }
+    if self.redirections_remaining == 0 {
+      self.redirections_remaining = inner.connection.max_redirections;
+    }
+    if self.timeout_dur.is_none() {
+      let default_dur = inner.default_command_timeout();
+      if !default_dur.is_zero() {
+        self.timeout_dur = Some(default_dur);
+      }
+    }
+  }
+
+  /// Take the command tracing state for the `queued` span.
+  #[cfg(feature = "full-tracing")]
+  pub fn take_queued_span(&mut self) -> Option<trace::Span> {
+    self.traces.queued.take()
+  }
+
+  /// Take the command tracing state for the `queued` span.
+  #[cfg(not(feature = "full-tracing"))]
+  pub fn take_queued_span(&mut self) -> Option<trace::disabled::Span> {
+    None
+  }
+
+  /// Take the response sender from the command.
+  ///
+  /// Usually used for responding early without sending the command.
+  pub fn take_responder(&mut self) -> Option<ResponseSender> {
+    match self.response {
+      ResponseKind::Respond(ref mut tx) => tx.take(),
+      ResponseKind::Buffer { ref mut tx, .. } => tx.lock().take(),
+      _ => None,
+    }
+  }
+
+  /// Whether the command has a channel for sending responses to the caller.
+  pub fn has_response_tx(&self) -> bool {
+    match self.response {
+      ResponseKind::Respond(ref r) => r.is_some(),
+      ResponseKind::Buffer { ref tx, .. } => tx.lock().is_some(),
+      _ => false,
+    }
+  }
+
+  /// Respond to the caller, taking the response channel in the process.
+  pub fn respond_to_caller(&mut self, result: Result<Resp3Frame, RedisError>) {
+    #[allow(unused_mut)]
+    if let Some(mut tx) = self.take_responder() {
+      let _ = tx.send(result);
+    }
+  }
+
+  /// Finish the command, responding to both the caller and router.
+  pub fn finish(mut self, inner: &RefCount<RedisClientInner>, result: Result<Resp3Frame, RedisError>) {
+    self.respond_to_caller(result);
+    self.respond_to_router(inner, RouterResponse::Continue);
+  }
+
+  /// Read the first key in the arguments according to the `FirstKey` cluster hash policy.
+  pub fn first_key(&self) -> Option<&[u8]> {
+    ClusterHash::FirstKey.find_key(self.args())
+  }
+
+  /// Hash the arguments according to the command's cluster hash policy.
+  pub fn cluster_hash(&self) -> Option<u16> {
+    self
+      .kind
+      .custom_hash_slot()
+      .or(self.scan_hash_slot())
+      .or(self.hasher.hash(self.args()))
+  }
+
+  /// Read the custom hash slot assigned to a scan operation.
+  pub fn scan_hash_slot(&self) -> Option<u16> {
+    match self.response {
+      ResponseKind::KeyScan(ref inner) => inner.hash_slot,
+      _ => None,
+    }
+  }
+
+  /// Convert to a single frame with an array of bulk strings (or null).
+  pub fn to_frame(&self, is_resp3: bool) -> Result<ProtocolFrame, RedisError> {
+    protocol_utils::command_to_frame(self, is_resp3)
+  }
+
+  /// Convert to a single frame with an array of bulk strings (or null), using a blocking task.
+  #[cfg(all(feature = "blocking-encoding", not(feature = "glommio")))]
+  pub fn to_frame_blocking(&self, is_resp3: bool, blocking_threshold: usize) -> Result<ProtocolFrame, RedisError> {
+    let cmd_size = protocol_utils::args_size(self.args());
+
+    if cmd_size >= blocking_threshold {
+      trace!("Using blocking task to convert command to frame with size {}", cmd_size);
+      tokio::task::block_in_place(|| protocol_utils::command_to_frame(self, is_resp3))
+    } else {
+      protocol_utils::command_to_frame(self, is_resp3)
+    }
+  }
+
+  #[cfg(feature = "mocks")]
+  pub fn to_mocked(&self) -> MockCommand {
+    MockCommand {
+      cmd:        self.kind.cmd_str(),
+      subcommand: self.kind.subcommand_str(),
+      args:       self.args().clone(),
+    }
+  }
+
+  #[cfg(not(feature = "debug-ids"))]
+  pub fn debug_id(&self) -> usize {
+    0
+  }
+
+  #[cfg(feature = "debug-ids")]
+  pub fn debug_id(&self) -> usize {
+    self.counter
+  }
+}
+
+/// A message sent from the front-end client to the router.
+pub enum RouterCommand {
+  /// Send a command to the server.
+  Command(RedisCommand),
+  /// Send a pipelined series of commands to the server.
+  Pipeline { commands: Vec<RedisCommand> },
+  /// Send a transaction to the server.
+  // Notes:
+  // * The inner command buffer will not contain the trailing `EXEC` command.
+  // * Transactions are never pipelined in order to handle ASK responses.
+  // * IDs must be unique w/r/t other transactions buffered in memory.
+  //
+  // There is one special failure mode that must be considered:
+  // 1. The client sends `MULTI` and we receive an `OK` response.
+  // 2. The caller sends `GET foo{1}` and we receive a `QUEUED` response.
+  // 3. The caller sends `GET bar{1}` and we receive an `ASK` response.
+  //
+  // According to the cluster spec the client should retry the entire transaction against the node in the `ASK`
+  // response, but with an `ASKING` command before `MULTI`. However, the future returned to the caller from `GET
+  // foo{1}` will have already finished at this point. To account for this the client will never pipeline
+  // transactions against a cluster, and may clone commands before sending them in order to replay them later with
+  // a different cluster node mapping.
+  #[cfg(feature = "transactions")]
+  Transaction {
+    id:             u64,
+    commands:       Vec<RedisCommand>,
+    watched:        Option<RedisCommand>,
+    abort_on_error: bool,
+    tx:             ResponseSender,
+  },
+  /// Retry a command after a `MOVED` error.
+  // This will trigger a call to `CLUSTER SLOTS` before the command is retried.
+  Moved {
+    slot:    u16,
+    server:  Server,
+    command: RedisCommand,
+  },
+  /// Retry a command after an `ASK` error.
+  // This is typically used instead of `RouterResponse::Ask` when a command was pipelined.
+  Ask {
+    slot:    u16,
+    server:  Server,
+    command: RedisCommand,
+  },
+  /// Initiate a reconnection to the provided server, or all servers.
+  // The client may not perform a reconnection if a healthy connection exists to `server`, unless `force` is `true`.
+  Reconnect {
+    server:  Option<Server>,
+    force:   bool,
+    tx:      Option<ResponseSender>,
+    #[cfg(feature = "replicas")]
+    replica: bool,
+  },
+  /// Sync the cached cluster state with the server via `CLUSTER SLOTS`.
+  SyncCluster { tx: OneshotSender<Result<(), RedisError>> },
+  /// Read the set of active connections managed by the client.
+  Connections { tx: OneshotSender<Vec<Server>> },
+  /// Force sync the replica routing table with the server(s).
+  #[cfg(feature = "replicas")]
+  SyncReplicas {
+    tx:    OneshotSender<Result<(), RedisError>>,
+    reset: bool,
+  },
+}
+
+impl RouterCommand {
+  /// Whether the client should skip backpressure on the command buffer when sending this command.
+  pub fn should_skip_backpressure(&self) -> bool {
+    matches!(
+      *self,
+      RouterCommand::Moved { .. }
+        | RouterCommand::Ask { .. }
+        | RouterCommand::SyncCluster { .. }
+        | RouterCommand::Connections { .. }
+    )
+  }
+
+  /// Whether the command should check the health of the backing connections before being used.
+  pub fn should_check_fail_fast(&self) -> bool {
+    match self {
+      RouterCommand::Command(command) => command.fail_fast,
+      RouterCommand::Pipeline { commands, .. } => commands.first().map(|c| c.fail_fast).unwrap_or(false),
+      #[cfg(feature = "transactions")]
+      RouterCommand::Transaction { commands, .. } => commands.first().map(|c| c.fail_fast).unwrap_or(false),
+      _ => false,
+    }
+  }
+
+  /// Finish the command early with the provided error.
+  #[allow(unused_mut)]
+  pub fn finish_with_error(self, error: RedisError) {
+    match self {
+      RouterCommand::Command(mut command) => {
+        command.respond_to_caller(Err(error));
+      },
+      RouterCommand::Pipeline { commands } => {
+        for mut command in commands.into_iter() {
+          command.respond_to_caller(Err(error.clone()));
+        }
+      },
+      #[cfg(feature = "transactions")]
+      RouterCommand::Transaction { mut tx, .. } => {
+        if let Err(_) = tx.send(Err(error)) {
+          warn!("Error responding early to transaction.");
+        }
+      },
+      RouterCommand::Reconnect { tx: Some(mut tx), .. } => {
+        if let Err(_) = tx.send(Err(error)) {
+          warn!("Error responding early to reconnect command.");
+        }
+      },
+      _ => {},
+    }
+  }
+
+  /// Inherit settings from the configuration structs on `inner`.
+  pub fn inherit_options(&mut self, inner: &RefCount<RedisClientInner>) {
+    match self {
+      RouterCommand::Command(ref mut cmd) => {
+        cmd.inherit_options(inner);
+      },
+      RouterCommand::Pipeline { ref mut commands, .. } => {
+        for cmd in commands.iter_mut() {
+          cmd.inherit_options(inner);
+        }
+      },
+      #[cfg(feature = "transactions")]
+      RouterCommand::Transaction { ref mut commands, .. } => {
+        for cmd in commands.iter_mut() {
+          cmd.inherit_options(inner);
+        }
+      },
+      _ => {},
+    };
+  }
+
+  /// Apply a timeout to the response channel receiver based on the command and `inner` context.
+  pub fn timeout_dur(&self) -> Option<Duration> {
+    match self {
+      RouterCommand::Command(ref command) => command.timeout_dur,
+      RouterCommand::Pipeline { ref commands, .. } => commands.first().and_then(|c| c.timeout_dur),
+      #[cfg(feature = "transactions")]
+      RouterCommand::Transaction { ref commands, .. } => commands.first().and_then(|c| c.timeout_dur),
+      _ => None,
+    }
+  }
+}
+
+impl fmt::Debug for RouterCommand {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    let mut formatter = f.debug_struct("RouterCommand");
+
+    match self {
+      RouterCommand::Ask { server, slot, command } => {
+        formatter
+          .field("kind", &"Ask")
+          .field("server", &server)
+          .field("slot", &slot)
+          .field("command", &command.kind.to_str_debug());
+      },
+      RouterCommand::Moved { server, slot, command } => {
+        formatter
+          .field("kind", &"Moved")
+          .field("server", &server)
+          .field("slot", &slot)
+          .field("command", &command.kind.to_str_debug());
+      },
+      #[cfg(not(feature = "replicas"))]
+      RouterCommand::Reconnect { server, force, .. } => {
+        formatter
+          .field("kind", &"Reconnect")
+          .field("server", &server)
+          .field("force", &force);
+      },
+      #[cfg(feature = "replicas")]
+      RouterCommand::Reconnect {
+        server, force, replica, ..
+      } => {
+        formatter
+          .field("kind", &"Reconnect")
+          .field("server", &server)
+          .field("replica", &replica)
+          .field("force", &force);
+      },
+      RouterCommand::SyncCluster { .. } => {
+        formatter.field("kind", &"Sync Cluster");
+      },
+      #[cfg(feature = "transactions")]
+      RouterCommand::Transaction { .. } => {
+        formatter.field("kind", &"Transaction");
+      },
+      RouterCommand::Pipeline { .. } => {
+        formatter.field("kind", &"Pipeline");
+      },
+      RouterCommand::Connections { .. } => {
+        formatter.field("kind", &"Connections");
+      },
+      RouterCommand::Command(command) => {
+        formatter
+          .field("kind", &"Command")
+          .field("command", &command.kind.to_str_debug());
+      },
+      #[cfg(feature = "replicas")]
+      RouterCommand::SyncReplicas { reset, .. } => {
+        formatter.field("kind", &"Sync Replicas");
+        formatter.field("reset", &reset);
+      },
+    };
+
+    formatter.finish()
+  }
+}
+
+impl From<RedisCommand> for RouterCommand {
+  fn from(cmd: RedisCommand) -> Self {
+    RouterCommand::Command(cmd)
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/protocol/connection.rs.html b/doc/src/fred/protocol/connection.rs.html new file mode 100644 index 00000000..2fee7326 --- /dev/null +++ b/doc/src/fred/protocol/connection.rs.html @@ -0,0 +1,2427 @@ +connection.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  modules::inner::RedisClientInner,
+  protocol::{
+    codec::RedisCodec,
+    command::{RedisCommand, RedisCommandKind, RouterResponse},
+    types::{ProtocolFrame, Server},
+    utils as protocol_utils,
+  },
+  runtime::{AtomicBool, AtomicUsize, JoinHandle, RefCount},
+  types::InfoKind,
+  utils as client_utils,
+  utils,
+};
+use bytes_utils::Str;
+use crossbeam_queue::SegQueue;
+use futures::{
+  sink::SinkExt,
+  stream::{SplitSink, SplitStream, StreamExt},
+  Sink,
+  Stream,
+};
+use redis_protocol::resp3::types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame, RespVersion};
+use semver::Version;
+use std::{
+  fmt,
+  net::SocketAddr,
+  pin::Pin,
+  str,
+  task::{Context, Poll},
+  time::Duration,
+};
+use tokio_util::codec::Framed;
+
+#[cfg(not(feature = "glommio"))]
+use socket2::SockRef;
+
+#[cfg(feature = "glommio")]
+use glommio::net::TcpStream as BaseTcpStream;
+#[cfg(feature = "glommio")]
+pub type TcpStream = crate::glommio::io_compat::TokioIO<BaseTcpStream>;
+
+#[cfg(not(feature = "glommio"))]
+use tokio::net::TcpStream;
+#[cfg(not(feature = "glommio"))]
+use tokio::net::TcpStream as BaseTcpStream;
+
+#[cfg(feature = "unix-sockets")]
+use crate::prelude::ServerConfig;
+#[cfg(any(
+  feature = "enable-native-tls",
+  feature = "enable-rustls",
+  feature = "enable-rustls-ring"
+))]
+use crate::protocol::tls::TlsConnector;
+#[cfg(feature = "replicas")]
+use crate::runtime::oneshot_channel;
+#[cfg(feature = "replicas")]
+use crate::{protocol::responders::ResponseKind, types::RedisValue};
+#[cfg(feature = "unix-sockets")]
+use std::path::Path;
+#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+use std::{convert::TryInto, ops::Deref};
+#[cfg(feature = "unix-sockets")]
+use tokio::net::UnixStream;
+#[cfg(feature = "enable-native-tls")]
+use tokio_native_tls::TlsStream as NativeTlsStream;
+#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+use tokio_rustls::client::TlsStream as RustlsStream;
+
+/// The contents of a simplestring OK response.
+pub const OK: &str = "OK";
+/// The timeout duration used when dropping the split sink and waiting on the split stream to close.
+pub const CONNECTION_CLOSE_TIMEOUT_MS: u64 = 5_000;
+
+pub type CommandBuffer = Vec<RedisCommand>;
+
+/// A shared buffer across tasks.
+#[derive(Clone, Debug)]
+pub struct SharedBuffer {
+  inner:   RefCount<SegQueue<RedisCommand>>,
+  blocked: RefCount<AtomicBool>,
+}
+
+impl SharedBuffer {
+  pub fn new() -> Self {
+    SharedBuffer {
+      inner:   RefCount::new(SegQueue::new()),
+      blocked: RefCount::new(AtomicBool::new(false)),
+    }
+  }
+
+  pub fn push(&self, cmd: RedisCommand) {
+    self.inner.push(cmd);
+  }
+
+  pub fn pop(&self) -> Option<RedisCommand> {
+    self.inner.pop()
+  }
+
+  pub fn len(&self) -> usize {
+    self.inner.len()
+  }
+
+  pub fn set_blocked(&self) {
+    utils::set_bool_atomic(&self.blocked, true);
+  }
+
+  pub fn set_unblocked(&self) {
+    utils::set_bool_atomic(&self.blocked, false);
+  }
+
+  pub fn is_blocked(&self) -> bool {
+    utils::read_bool_atomic(&self.blocked)
+  }
+
+  pub fn drain(&self) -> Vec<RedisCommand> {
+    utils::set_bool_atomic(&self.blocked, false);
+    let mut out = Vec::with_capacity(self.inner.len());
+    while let Some(cmd) = self.inner.pop() {
+      out.push(cmd);
+    }
+    out
+  }
+}
+
+pub type SplitRedisSink<T> = SplitSink<Framed<T, RedisCodec>, ProtocolFrame>;
+pub type SplitRedisStream<T> = SplitStream<Framed<T, RedisCodec>>;
+
+/// Connect to each socket addr and return the first successful connection.
+async fn tcp_connect_any(
+  inner: &RefCount<RedisClientInner>,
+  server: &Server,
+  addrs: &Vec<SocketAddr>,
+) -> Result<(TcpStream, SocketAddr), RedisError> {
+  let mut last_error: Option<RedisError> = None;
+
+  for addr in addrs.iter() {
+    _debug!(
+      inner,
+      "Creating TCP connection to {} at {}:{}",
+      server.host,
+      addr.ip(),
+      addr.port()
+    );
+    let socket = match BaseTcpStream::connect(addr).await {
+      Ok(socket) => socket,
+      Err(e) => {
+        _debug!(inner, "Error connecting to {}: {:?}", addr, e);
+        last_error = Some(e.into());
+        continue;
+      },
+    };
+    if let Some(val) = inner.connection.tcp.nodelay {
+      socket.set_nodelay(val)?;
+    }
+    if let Some(_dur) = inner.connection.tcp.linger {
+      #[cfg(not(feature = "glommio"))]
+      socket.set_linger(Some(_dur))?;
+      #[cfg(feature = "glommio")]
+      _warn!(inner, "TCP Linger is not yet supported with Glommio features.");
+    }
+    if let Some(ttl) = inner.connection.tcp.ttl {
+      socket.set_ttl(ttl)?;
+    }
+    if let Some(ref _keepalive) = inner.connection.tcp.keepalive {
+      #[cfg(not(feature = "glommio"))]
+      SockRef::from(&socket).set_tcp_keepalive(_keepalive)?;
+      #[cfg(feature = "glommio")]
+      _warn!(inner, "TCP keepalive is not yet supported with Glommio features.");
+    }
+
+    #[cfg(feature = "glommio")]
+    let socket = crate::glommio::io_compat::TokioIO(socket);
+    return Ok((socket, *addr));
+  }
+
+  _trace!(inner, "Failed to connect to any of {:?}.", addrs);
+  Err(last_error.unwrap_or(RedisError::new(RedisErrorKind::IO, "Failed to connect.")))
+}
+
+pub enum ConnectionKind {
+  Tcp(Framed<TcpStream, RedisCodec>),
+  #[cfg(feature = "unix-sockets")]
+  Unix(Framed<UnixStream, RedisCodec>),
+  #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+  Rustls(Framed<RustlsStream<TcpStream>, RedisCodec>),
+  #[cfg(feature = "enable-native-tls")]
+  NativeTls(Framed<NativeTlsStream<TcpStream>, RedisCodec>),
+}
+
+impl ConnectionKind {
+  /// Split the connection.
+  pub fn split(self) -> (SplitSinkKind, SplitStreamKind) {
+    match self {
+      ConnectionKind::Tcp(conn) => {
+        let (sink, stream) = conn.split();
+        (SplitSinkKind::Tcp(sink), SplitStreamKind::Tcp(stream))
+      },
+      #[cfg(feature = "unix-sockets")]
+      ConnectionKind::Unix(conn) => {
+        let (sink, stream) = conn.split();
+        (SplitSinkKind::Unix(sink), SplitStreamKind::Unix(stream))
+      },
+      #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+      ConnectionKind::Rustls(conn) => {
+        let (sink, stream) = conn.split();
+        (SplitSinkKind::Rustls(sink), SplitStreamKind::Rustls(stream))
+      },
+      #[cfg(feature = "enable-native-tls")]
+      ConnectionKind::NativeTls(conn) => {
+        let (sink, stream) = conn.split();
+        (SplitSinkKind::NativeTls(sink), SplitStreamKind::NativeTls(stream))
+      },
+    }
+  }
+}
+
+impl Stream for ConnectionKind {
+  type Item = Result<ProtocolFrame, RedisError>;
+
+  fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+    match self.get_mut() {
+      ConnectionKind::Tcp(ref mut conn) => Pin::new(conn).poll_next(cx),
+      #[cfg(feature = "unix-sockets")]
+      ConnectionKind::Unix(ref mut conn) => Pin::new(conn).poll_next(cx),
+      #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+      ConnectionKind::Rustls(ref mut conn) => Pin::new(conn).poll_next(cx),
+      #[cfg(feature = "enable-native-tls")]
+      ConnectionKind::NativeTls(ref mut conn) => Pin::new(conn).poll_next(cx),
+    }
+  }
+
+  fn size_hint(&self) -> (usize, Option<usize>) {
+    match self {
+      ConnectionKind::Tcp(ref conn) => conn.size_hint(),
+      #[cfg(feature = "unix-sockets")]
+      ConnectionKind::Unix(ref conn) => conn.size_hint(),
+      #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+      ConnectionKind::Rustls(ref conn) => conn.size_hint(),
+      #[cfg(feature = "enable-native-tls")]
+      ConnectionKind::NativeTls(ref conn) => conn.size_hint(),
+    }
+  }
+}
+
+impl Sink<ProtocolFrame> for ConnectionKind {
+  type Error = RedisError;
+
+  fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+    match self.get_mut() {
+      ConnectionKind::Tcp(ref mut conn) => Pin::new(conn).poll_ready(cx),
+      #[cfg(feature = "unix-sockets")]
+      ConnectionKind::Unix(ref mut conn) => Pin::new(conn).poll_ready(cx),
+      #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+      ConnectionKind::Rustls(ref mut conn) => Pin::new(conn).poll_ready(cx),
+      #[cfg(feature = "enable-native-tls")]
+      ConnectionKind::NativeTls(ref mut conn) => Pin::new(conn).poll_ready(cx),
+    }
+  }
+
+  fn start_send(self: Pin<&mut Self>, item: ProtocolFrame) -> Result<(), Self::Error> {
+    match self.get_mut() {
+      ConnectionKind::Tcp(ref mut conn) => Pin::new(conn).start_send(item),
+      #[cfg(feature = "unix-sockets")]
+      ConnectionKind::Unix(ref mut conn) => Pin::new(conn).start_send(item),
+      #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+      ConnectionKind::Rustls(ref mut conn) => Pin::new(conn).start_send(item),
+      #[cfg(feature = "enable-native-tls")]
+      ConnectionKind::NativeTls(ref mut conn) => Pin::new(conn).start_send(item),
+    }
+  }
+
+  fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+    match self.get_mut() {
+      ConnectionKind::Tcp(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e),
+      #[cfg(feature = "unix-sockets")]
+      ConnectionKind::Unix(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e),
+      #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+      ConnectionKind::Rustls(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e),
+      #[cfg(feature = "enable-native-tls")]
+      ConnectionKind::NativeTls(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e),
+    }
+  }
+
+  fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+    match self.get_mut() {
+      ConnectionKind::Tcp(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e),
+      #[cfg(feature = "unix-sockets")]
+      ConnectionKind::Unix(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e),
+      #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+      ConnectionKind::Rustls(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e),
+      #[cfg(feature = "enable-native-tls")]
+      ConnectionKind::NativeTls(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e),
+    }
+  }
+}
+
+pub enum SplitStreamKind {
+  Tcp(SplitRedisStream<TcpStream>),
+  #[cfg(feature = "unix-sockets")]
+  Unix(SplitRedisStream<UnixStream>),
+  #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+  Rustls(SplitRedisStream<RustlsStream<TcpStream>>),
+  #[cfg(feature = "enable-native-tls")]
+  NativeTls(SplitRedisStream<NativeTlsStream<TcpStream>>),
+}
+
+impl Stream for SplitStreamKind {
+  type Item = Result<ProtocolFrame, RedisError>;
+
+  fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+    match self.get_mut() {
+      SplitStreamKind::Tcp(ref mut conn) => Pin::new(conn).poll_next(cx),
+      #[cfg(feature = "unix-sockets")]
+      SplitStreamKind::Unix(ref mut conn) => Pin::new(conn).poll_next(cx),
+      #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+      SplitStreamKind::Rustls(ref mut conn) => Pin::new(conn).poll_next(cx),
+      #[cfg(feature = "enable-native-tls")]
+      SplitStreamKind::NativeTls(ref mut conn) => Pin::new(conn).poll_next(cx),
+    }
+  }
+
+  fn size_hint(&self) -> (usize, Option<usize>) {
+    match self {
+      SplitStreamKind::Tcp(ref conn) => conn.size_hint(),
+      #[cfg(feature = "unix-sockets")]
+      SplitStreamKind::Unix(ref conn) => conn.size_hint(),
+      #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+      SplitStreamKind::Rustls(ref conn) => conn.size_hint(),
+      #[cfg(feature = "enable-native-tls")]
+      SplitStreamKind::NativeTls(ref conn) => conn.size_hint(),
+    }
+  }
+}
+
+pub enum SplitSinkKind {
+  Tcp(SplitRedisSink<TcpStream>),
+  #[cfg(feature = "unix-sockets")]
+  Unix(SplitRedisSink<UnixStream>),
+  #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+  Rustls(SplitRedisSink<RustlsStream<TcpStream>>),
+  #[cfg(feature = "enable-native-tls")]
+  NativeTls(SplitRedisSink<NativeTlsStream<TcpStream>>),
+}
+
+impl Sink<ProtocolFrame> for SplitSinkKind {
+  type Error = RedisError;
+
+  fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+    match self.get_mut() {
+      SplitSinkKind::Tcp(ref mut conn) => Pin::new(conn).poll_ready(cx),
+      #[cfg(feature = "unix-sockets")]
+      SplitSinkKind::Unix(ref mut conn) => Pin::new(conn).poll_ready(cx),
+      #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+      SplitSinkKind::Rustls(ref mut conn) => Pin::new(conn).poll_ready(cx),
+      #[cfg(feature = "enable-native-tls")]
+      SplitSinkKind::NativeTls(ref mut conn) => Pin::new(conn).poll_ready(cx),
+    }
+  }
+
+  fn start_send(self: Pin<&mut Self>, item: ProtocolFrame) -> Result<(), Self::Error> {
+    match self.get_mut() {
+      SplitSinkKind::Tcp(ref mut conn) => Pin::new(conn).start_send(item),
+      #[cfg(feature = "unix-sockets")]
+      SplitSinkKind::Unix(ref mut conn) => Pin::new(conn).start_send(item),
+      #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+      SplitSinkKind::Rustls(ref mut conn) => Pin::new(conn).start_send(item),
+      #[cfg(feature = "enable-native-tls")]
+      SplitSinkKind::NativeTls(ref mut conn) => Pin::new(conn).start_send(item),
+    }
+  }
+
+  fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+    match self.get_mut() {
+      SplitSinkKind::Tcp(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e),
+      #[cfg(feature = "unix-sockets")]
+      SplitSinkKind::Unix(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e),
+      #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+      SplitSinkKind::Rustls(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e),
+      #[cfg(feature = "enable-native-tls")]
+      SplitSinkKind::NativeTls(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e),
+    }
+  }
+
+  fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+    match self.get_mut() {
+      SplitSinkKind::Tcp(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e),
+      #[cfg(feature = "unix-sockets")]
+      SplitSinkKind::Unix(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e),
+      #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+      SplitSinkKind::Rustls(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e),
+      #[cfg(feature = "enable-native-tls")]
+      SplitSinkKind::NativeTls(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e),
+    }
+  }
+}
+
+/// Atomic counters stored with connection state.
+// TODO with glommio these don't need to be atomics
+#[derive(Clone, Debug)]
+pub struct Counters {
+  pub cmd_buffer_len: RefCount<AtomicUsize>,
+  pub in_flight:      RefCount<AtomicUsize>,
+  pub feed_count:     RefCount<AtomicUsize>,
+}
+
+impl Counters {
+  pub fn new(cmd_buffer_len: &RefCount<AtomicUsize>) -> Self {
+    Counters {
+      cmd_buffer_len: cmd_buffer_len.clone(),
+      in_flight:      RefCount::new(AtomicUsize::new(0)),
+      feed_count:     RefCount::new(AtomicUsize::new(0)),
+    }
+  }
+
+  /// Flush the sink if the max feed count is reached or no commands are queued following the current command.
+  pub fn should_send(&self, inner: &RefCount<RedisClientInner>) -> bool {
+    client_utils::read_atomic(&self.feed_count) as u64 > inner.max_feed_count()
+      || client_utils::read_atomic(&self.cmd_buffer_len) == 0
+  }
+
+  pub fn incr_feed_count(&self) -> usize {
+    client_utils::incr_atomic(&self.feed_count)
+  }
+
+  pub fn incr_in_flight(&self) -> usize {
+    client_utils::incr_atomic(&self.in_flight)
+  }
+
+  pub fn decr_in_flight(&self) -> usize {
+    client_utils::decr_atomic(&self.in_flight)
+  }
+
+  pub fn reset_feed_count(&self) {
+    client_utils::set_atomic(&self.feed_count, 0);
+  }
+
+  pub fn reset_in_flight(&self) {
+    client_utils::set_atomic(&self.in_flight, 0);
+  }
+}
+
+pub struct RedisTransport {
+  /// An identifier for the connection, usually `<host>|<ip>:<port>`.
+  pub server:       Server,
+  /// The parsed `SocketAddr` for the connection.
+  pub addr:         Option<SocketAddr>,
+  /// The hostname used to initialize the connection.
+  pub default_host: Str,
+  /// The network connection.
+  pub transport:    ConnectionKind,
+  /// The connection/client ID from the CLIENT ID command.
+  pub id:           Option<i64>,
+  /// The server version.
+  pub version:      Option<Version>,
+  /// Counters for the connection state.
+  pub counters:     Counters,
+}
+
+impl RedisTransport {
+  pub async fn new_tcp(inner: &RefCount<RedisClientInner>, server: &Server) -> Result<RedisTransport, RedisError> {
+    let counters = Counters::new(&inner.counters.cmd_buffer_len);
+    let (id, version) = (None, None);
+    let default_host = server.host.clone();
+    let codec = RedisCodec::new(inner, server);
+    let addrs = inner
+      .get_resolver()
+      .await
+      .resolve(server.host.clone(), server.port)
+      .await?;
+    let (socket, addr) = tcp_connect_any(inner, server, &addrs).await?;
+    let transport = ConnectionKind::Tcp(Framed::new(socket, codec));
+
+    Ok(RedisTransport {
+      server: server.clone(),
+      addr: Some(addr),
+      default_host,
+      counters,
+      id,
+      version,
+      transport,
+    })
+  }
+
+  #[cfg(feature = "unix-sockets")]
+  pub async fn new_unix(inner: &RefCount<RedisClientInner>, path: &Path) -> Result<RedisTransport, RedisError> {
+    _debug!(inner, "Connecting via unix socket to {}", utils::path_to_string(path));
+    let server = Server::new(utils::path_to_string(path), 0);
+    let counters = Counters::new(&inner.counters.cmd_buffer_len);
+    let (id, version) = (None, None);
+    let default_host = server.host.clone();
+    let codec = RedisCodec::new(inner, &server);
+    let socket = UnixStream::connect(path).await?;
+    let transport = ConnectionKind::Unix(Framed::new(socket, codec));
+
+    Ok(RedisTransport {
+      addr: None,
+      server,
+      default_host,
+      counters,
+      id,
+      version,
+      transport,
+    })
+  }
+
+  #[cfg(feature = "enable-native-tls")]
+  #[allow(unreachable_patterns)]
+  pub async fn new_native_tls(
+    inner: &RefCount<RedisClientInner>,
+    server: &Server,
+  ) -> Result<RedisTransport, RedisError> {
+    let connector = match inner.config.tls {
+      Some(ref config) => match config.connector {
+        TlsConnector::Native(ref connector) => connector.clone(),
+        _ => return Err(RedisError::new(RedisErrorKind::Tls, "Invalid TLS configuration.")),
+      },
+      None => return RedisTransport::new_tcp(inner, server).await,
+    };
+
+    let counters = Counters::new(&inner.counters.cmd_buffer_len);
+    let (id, version) = (None, None);
+    let tls_server_name = server.tls_server_name.as_ref().cloned().unwrap_or(server.host.clone());
+
+    let default_host = server.host.clone();
+    let codec = RedisCodec::new(inner, server);
+    let addrs = inner
+      .get_resolver()
+      .await
+      .resolve(server.host.clone(), server.port)
+      .await?;
+    let (socket, addr) = tcp_connect_any(inner, server, &addrs).await?;
+
+    _debug!(inner, "native-tls handshake with server name/host: {}", tls_server_name);
+    let socket = connector.clone().connect(&tls_server_name, socket).await?;
+    let transport = ConnectionKind::NativeTls(Framed::new(socket, codec));
+
+    Ok(RedisTransport {
+      server: server.clone(),
+      addr: Some(addr),
+      default_host,
+      counters,
+      id,
+      version,
+      transport,
+    })
+  }
+
+  #[cfg(not(feature = "enable-native-tls"))]
+  pub async fn new_native_tls(
+    inner: &RefCount<RedisClientInner>,
+    server: &Server,
+  ) -> Result<RedisTransport, RedisError> {
+    RedisTransport::new_tcp(inner, server).await
+  }
+
+  #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+  #[allow(unreachable_patterns)]
+  pub async fn new_rustls(inner: &RefCount<RedisClientInner>, server: &Server) -> Result<RedisTransport, RedisError> {
+    use rustls::pki_types::ServerName;
+
+    let connector = match inner.config.tls {
+      Some(ref config) => match config.connector {
+        TlsConnector::Rustls(ref connector) => connector.clone(),
+        _ => return Err(RedisError::new(RedisErrorKind::Tls, "Invalid TLS configuration.")),
+      },
+      None => return RedisTransport::new_tcp(inner, server).await,
+    };
+
+    let counters = Counters::new(&inner.counters.cmd_buffer_len);
+    let (id, version) = (None, None);
+    let tls_server_name = server.tls_server_name.as_ref().cloned().unwrap_or(server.host.clone());
+
+    let default_host = server.host.clone();
+    let codec = RedisCodec::new(inner, server);
+    let addrs = inner
+      .get_resolver()
+      .await
+      .resolve(server.host.clone(), server.port)
+      .await?;
+    let (socket, addr) = tcp_connect_any(inner, server, &addrs).await?;
+    let server_name: ServerName = tls_server_name.deref().try_into()?;
+
+    _debug!(inner, "rustls handshake with server name/host: {:?}", tls_server_name);
+    let socket = connector.clone().connect(server_name.to_owned(), socket).await?;
+    let transport = ConnectionKind::Rustls(Framed::new(socket, codec));
+
+    Ok(RedisTransport {
+      server: server.clone(),
+      addr: Some(addr),
+      counters,
+      default_host,
+      id,
+      version,
+      transport,
+    })
+  }
+
+  #[cfg(not(any(feature = "enable-rustls", feature = "enable-rustls-ring")))]
+  pub async fn new_rustls(inner: &RefCount<RedisClientInner>, server: &Server) -> Result<RedisTransport, RedisError> {
+    RedisTransport::new_tcp(inner, server).await
+  }
+
+  /// Send a command to the server.
+  pub async fn request_response(&mut self, cmd: RedisCommand, is_resp3: bool) -> Result<Resp3Frame, RedisError> {
+    let frame = cmd.to_frame(is_resp3)?;
+    self.transport.send(frame).await?;
+
+    match self.transport.next().await {
+      Some(result) => result.map(|f| f.into_resp3()),
+      None => Ok(Resp3Frame::Null),
+    }
+  }
+
+  /// Set the client name with `CLIENT SETNAME`.
+  pub async fn set_client_name(&mut self, inner: &RefCount<RedisClientInner>) -> Result<(), RedisError> {
+    _debug!(inner, "Setting client name.");
+    let name = &inner.id;
+    let command = RedisCommand::new(RedisCommandKind::ClientSetname, vec![name.clone().into()]);
+    let response = self.request_response(command, inner.is_resp3()).await?;
+
+    if protocol_utils::is_ok(&response) {
+      debug!("{}: Successfully set Redis client name.", name);
+      Ok(())
+    } else {
+      error!("{} Failed to set client name with error {:?}", name, response);
+      Err(RedisError::new(RedisErrorKind::Protocol, "Failed to set client name."))
+    }
+  }
+
+  /// Read and cache the server version.
+  pub async fn cache_server_version(&mut self, inner: &RefCount<RedisClientInner>) -> Result<(), RedisError> {
+    let command = RedisCommand::new(RedisCommandKind::Info, vec![InfoKind::Server.to_str().into()]);
+    let result = self.request_response(command, inner.is_resp3()).await?;
+    let result = match result {
+      Resp3Frame::SimpleString { data, .. } => String::from_utf8(data.to_vec())?,
+      Resp3Frame::BlobString { data, .. } | Resp3Frame::VerbatimString { data, .. } => {
+        String::from_utf8(data.to_vec())?
+      },
+      Resp3Frame::SimpleError { data, .. } => {
+        _warn!(inner, "Failed to read server version: {:?}", data);
+        return Ok(());
+      },
+      Resp3Frame::BlobError { data, .. } => {
+        let parsed = String::from_utf8_lossy(&data);
+        _warn!(inner, "Failed to read server version: {:?}", parsed);
+        return Ok(());
+      },
+      _ => {
+        _warn!(inner, "Invalid INFO response: {:?}", result.kind());
+        return Ok(());
+      },
+    };
+
+    self.version = result.lines().find_map(|line| {
+      let parts: Vec<&str> = line.split(':').collect();
+      if parts.len() < 2 {
+        return None;
+      }
+
+      if parts[0] == "redis_version" {
+        Version::parse(parts[1]).ok()
+      } else {
+        None
+      }
+    });
+
+    _debug!(inner, "Read server version {:?}", self.version);
+    Ok(())
+  }
+
+  /// Authenticate via AUTH, then set the client name.
+  pub async fn authenticate(
+    &mut self,
+    name: &str,
+    username: Option<String>,
+    password: Option<String>,
+    is_resp3: bool,
+  ) -> Result<(), RedisError> {
+    if let Some(password) = password {
+      let args = if let Some(username) = username {
+        vec![username.into(), password.into()]
+      } else {
+        vec![password.into()]
+      };
+      let command = RedisCommand::new(RedisCommandKind::Auth, args);
+
+      debug!("{}: Authenticating Redis client...", name);
+      let frame = self.request_response(command, is_resp3).await?;
+
+      if !protocol_utils::is_ok(&frame) {
+        let error = protocol_utils::frame_into_string(frame)?;
+        return Err(protocol_utils::pretty_error(&error));
+      }
+    } else {
+      trace!("{}: Skip authentication without credentials.", name);
+    }
+
+    Ok(())
+  }
+
+  /// Authenticate via HELLO in RESP3 mode or AUTH in RESP2 mode, then set the client name.
+  pub async fn switch_protocols_and_authenticate(
+    &mut self,
+    inner: &RefCount<RedisClientInner>,
+  ) -> Result<(), RedisError> {
+    // reset the protocol version to the one specified by the config when we create new connections
+    inner.reset_protocol_version();
+    let username = inner.config.username.clone();
+    let password = inner.config.password.clone();
+
+    if inner.is_resp3() {
+      _debug!(inner, "Switching to RESP3 protocol with HELLO...");
+      let args = if let Some(password) = password {
+        if let Some(username) = username {
+          vec![username.into(), password.into()]
+        } else {
+          vec!["default".into(), password.into()]
+        }
+      } else {
+        vec![]
+      };
+
+      let cmd = RedisCommand::new(RedisCommandKind::_Hello(RespVersion::RESP3), args);
+      let response = self.request_response(cmd, true).await?;
+      let response = protocol_utils::frame_to_results(response)?;
+      inner.switch_protocol_versions(RespVersion::RESP3);
+      _trace!(inner, "Recv HELLO response {:?}", response);
+
+      Ok(())
+    } else {
+      self.authenticate(&inner.id, username, password, false).await
+    }
+  }
+
+  /// Read and cache the connection ID.
+  pub async fn cache_connection_id(&mut self, inner: &RefCount<RedisClientInner>) -> Result<(), RedisError> {
+    let command = (RedisCommandKind::ClientID, vec![]).into();
+    let result = self.request_response(command, inner.is_resp3()).await;
+    _debug!(inner, "Read client ID: {:?}", result);
+    self.id = match result {
+      Ok(Resp3Frame::Number { data, .. }) => Some(data),
+      _ => None,
+    };
+
+    Ok(())
+  }
+
+  /// Send `PING` to the server.
+  pub async fn ping(&mut self, inner: &RefCount<RedisClientInner>) -> Result<(), RedisError> {
+    let command = RedisCommandKind::Ping.into();
+    let response = self.request_response(command, inner.is_resp3()).await?;
+
+    if let Some(e) = protocol_utils::frame_to_error(&response) {
+      Err(e)
+    } else {
+      Ok(())
+    }
+  }
+
+  /// Send `QUIT` and close the connection.
+  pub async fn disconnect(&mut self, inner: &RefCount<RedisClientInner>) -> Result<(), RedisError> {
+    if let Err(e) = self.transport.close().await {
+      _warn!(inner, "Error closing connection to {}: {:?}", self.server, e);
+    }
+    Ok(())
+  }
+
+  /// Select the database provided in the `RedisConfig`.
+  pub async fn select_database(&mut self, inner: &RefCount<RedisClientInner>) -> Result<(), RedisError> {
+    if inner.config.server.is_clustered() {
+      return Ok(());
+    }
+
+    let db = match inner.config.database {
+      Some(db) => db,
+      None => return Ok(()),
+    };
+
+    _trace!(inner, "Selecting database {} after connecting.", db);
+    let command = RedisCommand::new(RedisCommandKind::Select, vec![db.into()]);
+    let response = self.request_response(command, inner.is_resp3()).await?;
+
+    if let Some(error) = protocol_utils::frame_to_error(&response) {
+      Err(error)
+    } else {
+      Ok(())
+    }
+  }
+
+  /// Check the `cluster_state` via `CLUSTER INFO`.
+  ///
+  /// Returns an error if the state is not `ok`.
+  pub async fn check_cluster_state(&mut self, inner: &RefCount<RedisClientInner>) -> Result<(), RedisError> {
+    if !inner.config.server.is_clustered() {
+      return Ok(());
+    }
+
+    _trace!(inner, "Checking cluster info for {}", self.server);
+    let command = RedisCommand::new(RedisCommandKind::ClusterInfo, vec![]);
+    let response = self.request_response(command, inner.is_resp3()).await?;
+    let response: String = protocol_utils::frame_to_results(response)?.convert()?;
+
+    for line in response.lines() {
+      let parts: Vec<&str> = line.split(':').collect();
+      if parts.len() == 2 && parts[0] == "cluster_state" && parts[1] == "ok" {
+        return Ok(());
+      }
+    }
+
+    Err(RedisError::new(
+      RedisErrorKind::Protocol,
+      "Invalid or missing cluster state.",
+    ))
+  }
+
+  /// Authenticate, set the protocol version, set the client name, select the provided database, cache the
+  /// connection ID and server version, and check the cluster state (if applicable).
+  pub async fn setup(
+    &mut self,
+    inner: &RefCount<RedisClientInner>,
+    timeout: Option<Duration>,
+  ) -> Result<(), RedisError> {
+    let timeout = timeout.unwrap_or(inner.internal_command_timeout());
+
+    utils::timeout(
+      async {
+        if inner.config.password.is_some() || inner.config.version == RespVersion::RESP3 {
+          self.switch_protocols_and_authenticate(inner).await?;
+        } else {
+          self.ping(inner).await?;
+        }
+        self.select_database(inner).await?;
+        if inner.connection.auto_client_setname {
+          self.set_client_name(inner).await?;
+        }
+        self.cache_connection_id(inner).await?;
+        self.cache_server_version(inner).await?;
+        if !inner.connection.disable_cluster_health_check {
+          self.check_cluster_state(inner).await?;
+        }
+
+        Ok::<_, RedisError>(())
+      },
+      timeout,
+    )
+    .await
+  }
+
+  /// Send `READONLY` to the server.
+  #[cfg(feature = "replicas")]
+  pub async fn readonly(
+    &mut self,
+    inner: &RefCount<RedisClientInner>,
+    timeout: Option<Duration>,
+  ) -> Result<(), RedisError> {
+    if !inner.config.server.is_clustered() {
+      return Ok(());
+    }
+    let timeout = timeout.unwrap_or(inner.internal_command_timeout());
+
+    utils::timeout(
+      async {
+        _debug!(inner, "Sending READONLY to {}", self.server);
+        let command = RedisCommand::new(RedisCommandKind::Readonly, vec![]);
+        let response = self.request_response(command, inner.is_resp3()).await?;
+        let _ = protocol_utils::frame_to_results(response)?;
+
+        Ok::<_, RedisError>(())
+      },
+      timeout,
+    )
+    .await
+  }
+
+  /// Send the `ROLE` command to the server.
+  #[cfg(feature = "replicas")]
+  pub async fn role(
+    &mut self,
+    inner: &RefCount<RedisClientInner>,
+    timeout: Option<Duration>,
+  ) -> Result<RedisValue, RedisError> {
+    let timeout = timeout.unwrap_or(inner.internal_command_timeout());
+    let command = RedisCommand::new(RedisCommandKind::Role, vec![]);
+
+    utils::timeout(
+      async {
+        self
+          .request_response(command, inner.is_resp3())
+          .await
+          .and_then(protocol_utils::frame_to_results)
+      },
+      timeout,
+    )
+    .await
+  }
+
+  /// Discover connected replicas via the ROLE command.
+  #[cfg(feature = "replicas")]
+  pub async fn discover_replicas(&mut self, inner: &RefCount<RedisClientInner>) -> Result<Vec<Server>, RedisError> {
+    self
+      .role(inner, None)
+      .await
+      .and_then(protocol_utils::parse_master_role_replicas)
+  }
+
+  /// Discover connected replicas via the ROLE command.
+  #[cfg(not(feature = "replicas"))]
+  pub async fn discover_replicas(&mut self, _: &RefCount<RedisClientInner>) -> Result<Vec<Server>, RedisError> {
+    Ok(Vec::new())
+  }
+
+  /// Split the transport into reader/writer halves.
+  pub fn split(self) -> (RedisWriter, RedisReader) {
+    let buffer = SharedBuffer::new();
+    let (server, addr, default_host) = (self.server, self.addr, self.default_host);
+    let (sink, stream) = self.transport.split();
+    let (id, version, counters) = (self.id, self.version, self.counters);
+
+    let writer = RedisWriter {
+      sink,
+      id,
+      version,
+      default_host,
+      counters: counters.clone(),
+      server: server.clone(),
+      addr,
+      buffer: buffer.clone(),
+      reader: None,
+    };
+    let reader = RedisReader {
+      stream: Some(stream),
+      task: None,
+      server,
+      buffer,
+      counters,
+    };
+    (writer, reader)
+  }
+}
+
+pub struct RedisReader {
+  pub stream:   Option<SplitStreamKind>,
+  pub server:   Server,
+  pub buffer:   SharedBuffer,
+  pub counters: Counters,
+  pub task:     Option<JoinHandle<Result<(), RedisError>>>,
+}
+
+impl RedisReader {
+  pub async fn wait(&mut self) -> Result<(), RedisError> {
+    if let Some(ref mut task) = self.task {
+      task.await?
+    } else {
+      Ok(())
+    }
+  }
+
+  pub fn is_connected(&self) -> bool {
+    self.task.is_some() || self.stream.is_some()
+  }
+
+  pub fn is_running(&self) -> bool {
+    self.task.is_some()
+  }
+
+  pub fn stop(&mut self, abort: bool) {
+    if abort && self.task.is_some() {
+      self.task.take().unwrap().abort();
+    } else {
+      self.task = None;
+    }
+    self.stream = None;
+  }
+}
+
+pub struct RedisWriter {
+  pub sink:         SplitSinkKind,
+  pub server:       Server,
+  pub default_host: Str,
+  pub addr:         Option<SocketAddr>,
+  pub buffer:       SharedBuffer,
+  pub version:      Option<Version>,
+  pub id:           Option<i64>,
+  pub counters:     Counters,
+  pub reader:       Option<RedisReader>,
+}
+
+impl fmt::Debug for RedisWriter {
+  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    f.debug_struct("Connection")
+      .field("server", &self.server)
+      .field("id", &self.id)
+      .field("default_host", &self.default_host)
+      .field("version", &self.version)
+      .finish()
+  }
+}
+
+impl RedisWriter {
+  /// Flush the sink and reset the feed counter.
+  pub async fn flush(&mut self) -> Result<(), RedisError> {
+    trace!("Flushing socket to {}", self.server);
+    self.sink.flush().await?;
+    trace!("Flushed socket to {}", self.server);
+    self.counters.reset_feed_count();
+    Ok(())
+  }
+
+  #[cfg(feature = "replicas")]
+  pub async fn discover_replicas(&mut self, inner: &RefCount<RedisClientInner>) -> Result<Vec<Server>, RedisError> {
+    let command = RedisCommand::new(RedisCommandKind::Role, vec![]);
+    let role = request_response(inner, self, command, None)
+      .await
+      .and_then(protocol_utils::frame_to_results)?;
+
+    protocol_utils::parse_master_role_replicas(role)
+  }
+
+  /// Check if the reader task is still running or awaiting frames.
+  pub fn is_working(&self) -> bool {
+    self
+      .reader
+      .as_ref()
+      .and_then(|reader| reader.task.as_ref())
+      .map(|task| !task.is_finished())
+      .unwrap_or(false)
+  }
+
+  /// Send a command to the server without waiting on the response.
+  pub async fn write_frame(
+    &mut self,
+    frame: ProtocolFrame,
+    should_flush: bool,
+    no_incr: bool,
+  ) -> Result<(), RedisError> {
+    if should_flush {
+      trace!("Writing and flushing {}", self.server);
+      if let Err(e) = self.sink.send(frame).await {
+        // the more useful error appears on the reader half but we'll log this just in case
+        debug!("{}: Error sending frame to socket: {:?}", self.server, e);
+        return Err(e);
+      }
+      self.counters.reset_feed_count();
+    } else {
+      trace!("Writing without flushing {}", self.server);
+      if let Err(e) = self.sink.feed(frame).await {
+        // the more useful error appears on the reader half but we'll log this just in case
+        debug!("{}: Error feeding frame to socket: {:?}", self.server, e);
+        return Err(e);
+      }
+      self.counters.incr_feed_count();
+    };
+    if !no_incr {
+      self.counters.incr_in_flight();
+    }
+
+    Ok(())
+  }
+
+  /// Put a command at the back of the command queue.
+  pub fn push_command(&self, inner: &RefCount<RedisClientInner>, mut cmd: RedisCommand) {
+    if cmd.has_no_responses() {
+      _trace!(
+        inner,
+        "Skip adding `{}` command to response buffer (no expected responses).",
+        cmd.kind.to_str_debug()
+      );
+
+      cmd.respond_to_router(inner, RouterResponse::Continue);
+      cmd.respond_to_caller(Ok(Resp3Frame::Null));
+      return;
+    }
+
+    if cmd.blocks_connection() {
+      self.buffer.set_blocked();
+    }
+    self.buffer.push(cmd);
+  }
+
+  /// Force close the connection.
+  ///
+  /// Returns the in-flight commands that had not received a response.
+  pub fn force_close(self, abort_reader: bool) -> CommandBuffer {
+    if abort_reader && self.reader.is_some() {
+      self.reader.unwrap().stop(true);
+    }
+    self.buffer.drain()
+  }
+
+  /// Gracefully close the connection and wait for the reader task to finish.
+  ///
+  /// Returns the in-flight commands that had not received a response.
+  pub async fn graceful_close(mut self) -> CommandBuffer {
+    let _ = utils::timeout(
+      async {
+        let _ = self.sink.close().await;
+        if let Some(mut reader) = self.reader {
+          let _ = reader.wait().await;
+        }
+
+        Ok::<_, RedisError>(())
+      },
+      Duration::from_millis(CONNECTION_CLOSE_TIMEOUT_MS),
+    )
+    .await;
+
+    self.buffer.drain()
+  }
+}
+
+/// Create a connection to the specified `host` and `port` with the provided timeout, in ms.
+///
+/// The returned connection will not be initialized.
+pub async fn create(
+  inner: &RefCount<RedisClientInner>,
+  server: &Server,
+  timeout: Option<Duration>,
+) -> Result<RedisTransport, RedisError> {
+  let timeout = timeout.unwrap_or(inner.connection_timeout());
+
+  _trace!(
+    inner,
+    "Checking connection type. Native-tls: {}, Rustls: {}",
+    inner.config.uses_native_tls(),
+    inner.config.uses_rustls(),
+  );
+  if inner.config.uses_native_tls() {
+    utils::timeout(RedisTransport::new_native_tls(inner, server), timeout).await
+  } else if inner.config.uses_rustls() {
+    utils::timeout(RedisTransport::new_rustls(inner, server), timeout).await
+  } else {
+    match inner.config.server {
+      #[cfg(feature = "unix-sockets")]
+      ServerConfig::Unix { ref path } => utils::timeout(RedisTransport::new_unix(inner, path), timeout).await,
+      _ => utils::timeout(RedisTransport::new_tcp(inner, server), timeout).await,
+    }
+  }
+}
+
+/// Split a connection, spawn a reader task, and link the reader and writer halves.
+pub fn split<F>(
+  inner: &RefCount<RedisClientInner>,
+  transport: RedisTransport,
+  is_replica: bool,
+  func: F,
+) -> Result<(Server, RedisWriter), RedisError>
+where
+  F: FnOnce(
+    &RefCount<RedisClientInner>,
+    SplitStreamKind,
+    &Server,
+    &SharedBuffer,
+    &Counters,
+    bool,
+  ) -> JoinHandle<Result<(), RedisError>>,
+{
+  let server = transport.server.clone();
+  let (mut writer, mut reader) = transport.split();
+  let reader_stream = match reader.stream.take() {
+    Some(stream) => stream,
+    None => {
+      return Err(RedisError::new(
+        RedisErrorKind::Unknown,
+        "Missing clustered connection reader stream.",
+      ))
+    },
+  };
+  reader.task = Some(func(
+    inner,
+    reader_stream,
+    &writer.server,
+    &writer.buffer,
+    &writer.counters,
+    is_replica,
+  ));
+  writer.reader = Some(reader);
+
+  Ok((server, writer))
+}
+
+/// Send a command to the server and wait for a response.
+#[cfg(feature = "replicas")]
+pub async fn request_response(
+  inner: &RefCount<RedisClientInner>,
+  writer: &mut RedisWriter,
+  mut command: RedisCommand,
+  timeout: Option<Duration>,
+) -> Result<Resp3Frame, RedisError> {
+  let (tx, rx) = oneshot_channel();
+  command.response = ResponseKind::Respond(Some(tx));
+  let timeout_dur = timeout
+    .or(command.timeout_dur)
+    .unwrap_or_else(|| inner.default_command_timeout());
+
+  _trace!(
+    inner,
+    "Sending {} ({}) to {}",
+    command.kind.to_str_debug(),
+    command.debug_id(),
+    writer.server
+  );
+  let frame = protocol_utils::encode_frame(inner, &command)?;
+
+  if !writer.is_working() {
+    return Err(RedisError::new(RedisErrorKind::IO, "Connection closed."));
+  }
+
+  writer.push_command(inner, command);
+  writer.write_frame(frame, true, false).await?;
+  utils::timeout(async { rx.await? }, timeout_dur).await
+}
+
\ No newline at end of file diff --git a/doc/src/fred/protocol/debug.rs.html b/doc/src/fred/protocol/debug.rs.html new file mode 100644 index 00000000..8da2a268 --- /dev/null +++ b/doc/src/fred/protocol/debug.rs.html @@ -0,0 +1,223 @@ +debug.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+
use redis_protocol::{resp2::types::BytesFrame as Resp2Frame, resp3::types::BytesFrame as Resp3Frame};
+use std::{
+  collections::{HashMap, HashSet},
+  hash::{Hash, Hasher},
+  str,
+};
+
+#[derive(Debug)]
+enum DebugFrame {
+  String(String),
+  Bytes(Vec<u8>),
+  Integer(i64),
+  Double(f64),
+  #[allow(dead_code)]
+  Array(Vec<DebugFrame>),
+  // TODO add support for maps in network logs
+  #[allow(dead_code)]
+  Map(HashMap<DebugFrame, DebugFrame>),
+  #[allow(dead_code)]
+  Set(HashSet<DebugFrame>),
+}
+
+impl Hash for DebugFrame {
+  fn hash<H: Hasher>(&self, state: &mut H) {
+    match self {
+      DebugFrame::String(ref s) => {
+        's'.hash(state);
+        s.hash(state)
+      },
+      DebugFrame::Bytes(ref b) => {
+        'b'.hash(state);
+        b.hash(state)
+      },
+      DebugFrame::Integer(ref i) => {
+        'i'.hash(state);
+        i.hash(state)
+      },
+      DebugFrame::Double(ref f) => {
+        'd'.hash(state);
+        f.to_be_bytes().hash(state)
+      },
+      _ => panic!("Cannot hash network log debug frame {:?}", self),
+    }
+  }
+}
+
+fn bytes_or_string(b: &[u8]) -> DebugFrame {
+  match str::from_utf8(b) {
+    Ok(s) => DebugFrame::String(s.to_owned()),
+    Err(_) => DebugFrame::Bytes(b.to_vec()),
+  }
+}
+
+impl<'a> From<&'a Resp2Frame> for DebugFrame {
+  fn from(f: &'a Resp2Frame) -> Self {
+    match f {
+      Resp2Frame::Error(s) => DebugFrame::String(s.to_string()),
+      Resp2Frame::SimpleString(s) => bytes_or_string(s),
+      Resp2Frame::Integer(i) => DebugFrame::Integer(*i),
+      Resp2Frame::BulkString(b) => bytes_or_string(b),
+      Resp2Frame::Null => DebugFrame::String("nil".into()),
+      Resp2Frame::Array(frames) => DebugFrame::Array(frames.iter().map(|f| f.into()).collect()),
+    }
+  }
+}
+
+impl<'a> From<&'a Resp3Frame> for DebugFrame {
+  fn from(frame: &'a Resp3Frame) -> Self {
+    match frame {
+      Resp3Frame::Map { ref data, .. } => DebugFrame::Array(data.iter().fold(vec![], |mut memo, (key, value)| {
+        memo.push(key.into());
+        memo.push(value.into());
+        memo
+      })),
+      Resp3Frame::Set { ref data, .. } => DebugFrame::Array(data.iter().map(|d| d.into()).collect()),
+      Resp3Frame::Array { ref data, .. } => DebugFrame::Array(data.iter().map(|d| d.into()).collect()),
+      Resp3Frame::BlobError { ref data, .. } => bytes_or_string(data),
+      Resp3Frame::BlobString { ref data, .. } => bytes_or_string(data),
+      Resp3Frame::SimpleString { ref data, .. } => bytes_or_string(data),
+      Resp3Frame::SimpleError { ref data, .. } => DebugFrame::String(data.to_string()),
+      Resp3Frame::Double { ref data, .. } => DebugFrame::Double(*data),
+      Resp3Frame::BigNumber { ref data, .. } => bytes_or_string(data),
+      Resp3Frame::Number { ref data, .. } => DebugFrame::Integer(*data),
+      Resp3Frame::Boolean { ref data, .. } => DebugFrame::String(data.to_string()),
+      Resp3Frame::Null => DebugFrame::String("nil".into()),
+      Resp3Frame::Push { ref data, .. } => DebugFrame::Array(data.iter().map(|d| d.into()).collect()),
+      Resp3Frame::ChunkedString(ref data) => bytes_or_string(data),
+      Resp3Frame::VerbatimString { ref data, .. } => bytes_or_string(data),
+      Resp3Frame::Hello {
+        ref version, ref auth, ..
+      } => {
+        let mut values = vec![DebugFrame::Integer(version.to_byte() as i64)];
+        if let Some((ref username, ref password)) = auth {
+          values.push(DebugFrame::String(username.to_string()));
+          values.push(DebugFrame::String(password.to_string()));
+        }
+        DebugFrame::Array(values)
+      },
+    }
+  }
+}
+
+pub fn log_resp2_frame(name: &str, frame: &Resp2Frame, encode: bool) {
+  let prefix = if encode { "Encoded" } else { "Decoded" };
+  trace!("{}: {} {:?}", name, prefix, DebugFrame::from(frame))
+}
+
+pub fn log_resp3_frame(name: &str, frame: &Resp3Frame, encode: bool) {
+  let prefix = if encode { "Encoded" } else { "Decoded" };
+  trace!("{}: {} {:?}", name, prefix, DebugFrame::from(frame))
+}
+
\ No newline at end of file diff --git a/doc/src/fred/protocol/hashers.rs.html b/doc/src/fred/protocol/hashers.rs.html new file mode 100644 index 00000000..c4b8a64f --- /dev/null +++ b/doc/src/fred/protocol/hashers.rs.html @@ -0,0 +1,195 @@ +hashers.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+
use crate::types::RedisValue;
+use redis_protocol::redis_keyslot;
+
+pub fn hash_value(value: &RedisValue) -> Option<u16> {
+  Some(match value {
+    RedisValue::String(s) => redis_keyslot(s.as_bytes()),
+    RedisValue::Bytes(b) => redis_keyslot(b),
+    RedisValue::Integer(i) => redis_keyslot(i.to_string().as_bytes()),
+    RedisValue::Double(f) => redis_keyslot(f.to_string().as_bytes()),
+    RedisValue::Null => redis_keyslot(b"nil"),
+    RedisValue::Boolean(b) => redis_keyslot(b.to_string().as_bytes()),
+    _ => return None,
+  })
+}
+
+pub fn read_redis_key(value: &RedisValue) -> Option<&[u8]> {
+  match value {
+    RedisValue::String(s) => Some(s.as_bytes()),
+    RedisValue::Bytes(b) => Some(b),
+    _ => None,
+  }
+}
+
+fn hash_key(value: &RedisValue) -> Option<u16> {
+  read_redis_key(value).map(redis_keyslot)
+}
+
+/// A cluster hashing policy.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClusterHash {
+  /// Hash the first string or bytes value in the arguments. (Default)
+  FirstKey,
+  /// Hash the first argument regardless of type.
+  FirstValue,
+  /// Use a random node in the cluster.
+  Random,
+  /// Hash the value with the provided offset in the arguments array.
+  Offset(usize),
+  /// Provide a custom hash slot value.
+  Custom(u16),
+}
+
+impl Default for ClusterHash {
+  fn default() -> Self {
+    ClusterHash::FirstKey
+  }
+}
+
+impl From<Option<u16>> for ClusterHash {
+  fn from(hash_slot: Option<u16>) -> Self {
+    match hash_slot {
+      Some(slot) => ClusterHash::Custom(slot),
+      None => ClusterHash::FirstKey,
+    }
+  }
+}
+
+impl From<u16> for ClusterHash {
+  fn from(slot: u16) -> Self {
+    ClusterHash::Custom(slot)
+  }
+}
+
+impl From<&str> for ClusterHash {
+  fn from(d: &str) -> Self {
+    ClusterHash::Custom(redis_keyslot(d.as_bytes()))
+  }
+}
+
+impl From<&[u8]> for ClusterHash {
+  fn from(d: &[u8]) -> Self {
+    ClusterHash::Custom(redis_keyslot(d))
+  }
+}
+
+impl ClusterHash {
+  /// Hash the provided arguments.
+  pub fn hash(&self, args: &[RedisValue]) -> Option<u16> {
+    match self {
+      ClusterHash::FirstValue => args.first().and_then(hash_value),
+      ClusterHash::FirstKey => args.iter().find_map(hash_key),
+      ClusterHash::Random => None,
+      ClusterHash::Offset(idx) => args.get(*idx).and_then(hash_value),
+      ClusterHash::Custom(val) => Some(*val),
+    }
+  }
+
+  /// Find the key to hash with the provided arguments.
+  pub fn find_key<'a>(&self, args: &'a [RedisValue]) -> Option<&'a [u8]> {
+    match self {
+      ClusterHash::FirstValue => args.first().and_then(read_redis_key),
+      ClusterHash::FirstKey => args.iter().find_map(read_redis_key),
+      ClusterHash::Offset(idx) => args.get(*idx).and_then(read_redis_key),
+      ClusterHash::Random | ClusterHash::Custom(_) => None,
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/protocol/mod.rs.html b/doc/src/fred/protocol/mod.rs.html new file mode 100644 index 00000000..dfae0d71 --- /dev/null +++ b/doc/src/fred/protocol/mod.rs.html @@ -0,0 +1,35 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
pub mod cluster;
+pub mod codec;
+pub mod command;
+pub mod connection;
+#[cfg(feature = "network-logs")]
+pub mod debug;
+pub mod hashers;
+pub mod responders;
+/// TLS configuration types.
+#[cfg(any(
+  feature = "enable-rustls",
+  feature = "enable-native-tls",
+  feature = "enable-rustls-ring"
+))]
+pub mod tls;
+pub mod types;
+pub mod utils;
+
\ No newline at end of file diff --git a/doc/src/fred/protocol/responders.rs.html b/doc/src/fred/protocol/responders.rs.html new file mode 100644 index 00000000..56b07a71 --- /dev/null +++ b/doc/src/fred/protocol/responders.rs.html @@ -0,0 +1,1213 @@ +responders.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  interfaces::Resp3Frame,
+  modules::inner::RedisClientInner,
+  protocol::{
+    command::{RedisCommand, RedisCommandKind, ResponseSender, RouterResponse},
+    types::{KeyScanInner, Server, ValueScanInner, ValueScanResult},
+    utils as protocol_utils,
+  },
+  runtime::{AtomicUsize, Mutex, RefCount},
+  types::{HScanResult, RedisKey, RedisValue, SScanResult, ScanResult, ZScanResult},
+  utils as client_utils,
+};
+use bytes_utils::Str;
+use redis_protocol::resp3::types::{FrameKind, Resp3Frame as _Resp3Frame};
+use std::{fmt, fmt::Formatter, iter::repeat, mem, ops::DerefMut};
+
+#[cfg(feature = "metrics")]
+use crate::modules::metrics::MovingStats;
+#[cfg(feature = "metrics")]
+use crate::runtime::RwLock;
+#[cfg(feature = "metrics")]
+use std::{cmp, time::Instant};
+
+const LAST_CURSOR: &str = "0";
+
+pub enum ResponseKind {
+  /// Throw away the response frame and last command in the command buffer.
+  ///
+  /// Note: The reader task will still unblock the router, if specified.
+  ///
+  /// Equivalent to `Respond(None)`.
+  Skip,
+  /// Respond to the caller of the last command with the response frame.
+  Respond(Option<ResponseSender>),
+  /// Buffer multiple response frames until the expected number of frames are received, then respond with an array to
+  /// the caller.
+  ///
+  /// Typically used in `*_cluster` commands or to handle concurrent responses in a `Pipeline` that may span multiple
+  /// cluster connections.
+  Buffer {
+    /// A shared buffer for response frames.
+    frames:      RefCount<Mutex<Vec<Resp3Frame>>>,
+    /// The expected number of response frames.
+    expected:    usize,
+    /// The number of response frames received.
+    received:    RefCount<AtomicUsize>,
+    /// A shared oneshot channel to the caller.
+    tx:          RefCount<Mutex<Option<ResponseSender>>>,
+    /// A local field for tracking the expected index of the response in the `frames` array.
+    index:       usize,
+    /// Whether errors should be returned early to the caller.
+    error_early: bool,
+  },
+  /// Handle the response as a page of key/value pairs from a HSCAN, SSCAN, ZSCAN command.
+  ValueScan(ValueScanInner),
+  /// Handle the response as a page of keys from a SCAN command.
+  KeyScan(KeyScanInner),
+}
+
+impl fmt::Debug for ResponseKind {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    write!(f, "{}", match self {
+      ResponseKind::Skip => "Skip",
+      ResponseKind::Buffer { .. } => "Buffer",
+      ResponseKind::Respond(_) => "Respond",
+      ResponseKind::KeyScan(_) => "KeyScan",
+      ResponseKind::ValueScan(_) => "ValueScan",
+    })
+  }
+}
+
+impl ResponseKind {
+  /// Attempt to clone the response channel.
+  ///
+  /// If the channel cannot be shared or cloned (since it contains a oneshot channel) this will fall back to a `Skip`
+  /// policy.
+  pub fn duplicate(&self) -> Option<Self> {
+    Some(match self {
+      ResponseKind::Skip => ResponseKind::Skip,
+      ResponseKind::Respond(_) => ResponseKind::Respond(None),
+      ResponseKind::Buffer {
+        frames,
+        tx,
+        received,
+        index,
+        expected,
+        error_early,
+      } => ResponseKind::Buffer {
+        frames:      frames.clone(),
+        tx:          tx.clone(),
+        received:    received.clone(),
+        index:       *index,
+        expected:    *expected,
+        error_early: *error_early,
+      },
+      ResponseKind::KeyScan(_) | ResponseKind::ValueScan(_) => return None,
+    })
+  }
+
+  pub fn set_expected_index(&mut self, idx: usize) {
+    if let ResponseKind::Buffer { ref mut index, .. } = self {
+      *index = idx;
+    }
+  }
+
+  pub fn set_error_early(&mut self, _error_early: bool) {
+    if let ResponseKind::Buffer {
+      ref mut error_early, ..
+    } = self
+    {
+      *error_early = _error_early;
+    }
+  }
+
+  pub fn new_buffer(tx: ResponseSender) -> Self {
+    ResponseKind::Buffer {
+      frames:      RefCount::new(Mutex::new(vec![])),
+      tx:          RefCount::new(Mutex::new(Some(tx))),
+      received:    RefCount::new(AtomicUsize::new(0)),
+      index:       0,
+      expected:    0,
+      error_early: true,
+    }
+  }
+
+  pub fn new_buffer_with_size(expected: usize, tx: ResponseSender) -> Self {
+    let frames = repeat(Resp3Frame::Null).take(expected).collect();
+    ResponseKind::Buffer {
+      frames: RefCount::new(Mutex::new(frames)),
+      tx: RefCount::new(Mutex::new(Some(tx))),
+      received: RefCount::new(AtomicUsize::new(0)),
+      index: 0,
+      error_early: true,
+      expected,
+    }
+  }
+
+  /// Take the oneshot response sender.
+  pub fn take_response_tx(&mut self) -> Option<ResponseSender> {
+    match self {
+      ResponseKind::Respond(tx) => tx.take(),
+      ResponseKind::Buffer { tx, .. } => tx.lock().take(),
+      _ => None,
+    }
+  }
+
+  /// Clone the shared response sender for `Buffer` or `Multiple` variants.
+  pub fn clone_shared_response_tx(&self) -> Option<RefCount<Mutex<Option<ResponseSender>>>> {
+    match self {
+      ResponseKind::Buffer { tx, .. } => Some(tx.clone()),
+      _ => None,
+    }
+  }
+
+  /// Respond with an error to the caller.
+  pub fn respond_with_error(&mut self, error: RedisError) {
+    if let Some(tx) = self.take_response_tx() {
+      let _ = tx.send(Err(error));
+    }
+  }
+
+  /// Read the number of expected response frames.
+  pub fn expected_response_frames(&self) -> usize {
+    match self {
+      ResponseKind::Skip | ResponseKind::Respond(_) => 1,
+      ResponseKind::Buffer { ref expected, .. } => *expected,
+      ResponseKind::ValueScan(_) | ResponseKind::KeyScan(_) => 1,
+    }
+  }
+}
+
+#[cfg(feature = "metrics")]
+fn sample_latency(latency_stats: &RwLock<MovingStats>, sent: Instant) {
+  let dur = Instant::now().duration_since(sent);
+  let dur_ms = cmp::max(0, (dur.as_secs() * 1000) + dur.subsec_millis() as u64) as i64;
+  latency_stats.write().sample(dur_ms);
+}
+
+/// Sample overall and network latency values for a command.
+#[cfg(feature = "metrics")]
+fn sample_command_latencies(inner: &RefCount<RedisClientInner>, command: &mut RedisCommand) {
+  if let Some(sent) = command.network_start.take() {
+    sample_latency(&inner.network_latency_stats, sent);
+  }
+  sample_latency(&inner.latency_stats, command.created);
+}
+
+#[cfg(not(feature = "metrics"))]
+fn sample_command_latencies(_: &RefCount<RedisClientInner>, _: &mut RedisCommand) {}
+
+/// Update the client's protocol version codec version after receiving a non-error response to HELLO.
+fn update_protocol_version(inner: &RefCount<RedisClientInner>, command: &RedisCommand, frame: &Resp3Frame) {
+  if !matches!(frame.kind(), FrameKind::SimpleError | FrameKind::BlobError) {
+    let version = match command.kind {
+      RedisCommandKind::_Hello(ref version) => version,
+      RedisCommandKind::_HelloAllCluster(ref version) => version,
+      _ => return,
+    };
+
+    _debug!(inner, "Changing RESP version to {:?}", version);
+    // HELLO cannot be pipelined so this is safe
+    inner.switch_protocol_versions(version.clone());
+  }
+}
+
+fn respond_locked(
+  inner: &RefCount<RedisClientInner>,
+  tx: &RefCount<Mutex<Option<ResponseSender>>>,
+  result: Result<Resp3Frame, RedisError>,
+) {
+  if let Some(tx) = tx.lock().take() {
+    if let Err(_) = tx.send(result) {
+      _debug!(inner, "Error responding to caller.");
+    }
+  }
+}
+
+fn add_buffered_frame(
+  server: &Server,
+  buffer: &RefCount<Mutex<Vec<Resp3Frame>>>,
+  index: usize,
+  frame: Resp3Frame,
+) -> Result<(), RedisError> {
+  let mut guard = buffer.lock();
+  let buffer_ref = guard.deref_mut();
+
+  if index >= buffer_ref.len() {
+    debug!(
+      "({}) Unexpected buffer response array index: {}, len: {}",
+      server,
+      index,
+      buffer_ref.len()
+    );
+    return Err(RedisError::new(
+      RedisErrorKind::Unknown,
+      "Invalid buffer response index.",
+    ));
+  }
+
+  trace!(
+    "({}) Add buffered frame {:?} at index {} with length {}",
+    server,
+    frame.kind(),
+    index,
+    buffer_ref.len()
+  );
+  buffer_ref[index] = frame;
+  Ok(())
+}
+
+/// Check for errors while merging the provided frames into one Array frame.
+fn merge_multiple_frames(frames: &mut Vec<Resp3Frame>, error_early: bool) -> Resp3Frame {
+  if error_early {
+    for frame in frames.iter() {
+      if matches!(frame.kind(), FrameKind::SimpleError | FrameKind::BlobError) {
+        return frame.clone();
+      }
+    }
+  }
+
+  Resp3Frame::Array {
+    data:       mem::take(frames),
+    attributes: None,
+  }
+}
+
+/// Parse the output of a command that scans keys.
+fn parse_key_scan_frame(frame: Resp3Frame) -> Result<(Str, Vec<RedisKey>), RedisError> {
+  if let Resp3Frame::Array { mut data, .. } = frame {
+    if data.len() == 2 {
+      let cursor = match protocol_utils::frame_to_str(&data[0]) {
+        Some(s) => s,
+        None => {
+          return Err(RedisError::new(
+            RedisErrorKind::Protocol,
+            "Expected first SCAN result element to be a bulk string.",
+          ))
+        },
+      };
+
+      if let Some(Resp3Frame::Array { data, .. }) = data.pop() {
+        let mut keys = Vec::with_capacity(data.len());
+
+        for frame in data.into_iter() {
+          let key = match protocol_utils::frame_to_bytes(&frame) {
+            Some(s) => s,
+            None => {
+              return Err(RedisError::new(
+                RedisErrorKind::Protocol,
+                "Expected an array of strings from second SCAN result.",
+              ))
+            },
+          };
+
+          keys.push(key.into());
+        }
+
+        Ok((cursor, keys))
+      } else {
+        Err(RedisError::new(
+          RedisErrorKind::Protocol,
+          "Expected second SCAN result element to be an array.",
+        ))
+      }
+    } else {
+      Err(RedisError::new(
+        RedisErrorKind::Protocol,
+        "Expected two-element bulk string array from SCAN.",
+      ))
+    }
+  } else {
+    Err(RedisError::new(
+      RedisErrorKind::Protocol,
+      "Expected bulk string array from SCAN.",
+    ))
+  }
+}
+
+/// Parse the output of a command that scans values.
+fn parse_value_scan_frame(frame: Resp3Frame) -> Result<(Str, Vec<RedisValue>), RedisError> {
+  if let Resp3Frame::Array { mut data, .. } = frame {
+    if data.len() == 2 {
+      let cursor = match protocol_utils::frame_to_str(&data[0]) {
+        Some(s) => s,
+        None => {
+          return Err(RedisError::new(
+            RedisErrorKind::Protocol,
+            "Expected first result element to be a bulk string.",
+          ))
+        },
+      };
+
+      if let Some(Resp3Frame::Array { data, .. }) = data.pop() {
+        let mut values = Vec::with_capacity(data.len());
+
+        for frame in data.into_iter() {
+          values.push(protocol_utils::frame_to_results(frame)?);
+        }
+
+        Ok((cursor, values))
+      } else {
+        Err(RedisError::new(
+          RedisErrorKind::Protocol,
+          "Expected second result element to be an array.",
+        ))
+      }
+    } else {
+      Err(RedisError::new(
+        RedisErrorKind::Protocol,
+        "Expected two-element bulk string array.",
+      ))
+    }
+  } else {
+    Err(RedisError::new(RedisErrorKind::Protocol, "Expected bulk string array."))
+  }
+}
+
+/// Send the output to the caller of a command that scans values.
+fn send_value_scan_result(
+  inner: &RefCount<RedisClientInner>,
+  scanner: ValueScanInner,
+  command: &RedisCommand,
+  result: Vec<RedisValue>,
+  can_continue: bool,
+) -> Result<(), RedisError> {
+  match command.kind {
+    RedisCommandKind::Zscan => {
+      let tx = scanner.tx.clone();
+      let results = ValueScanInner::transform_zscan_result(result)?;
+
+      let state = ValueScanResult::ZScan(ZScanResult {
+        can_continue,
+        inner: inner.clone(),
+        scan_state: scanner,
+        results: Some(results),
+      });
+
+      if let Err(_) = tx.send(Ok(state)) {
+        _warn!(inner, "Failed to send ZSCAN result to caller");
+      }
+    },
+    RedisCommandKind::Sscan => {
+      let tx = scanner.tx.clone();
+
+      let state = ValueScanResult::SScan(SScanResult {
+        can_continue,
+        inner: inner.clone(),
+        scan_state: scanner,
+        results: Some(result),
+      });
+
+      if let Err(_) = tx.send(Ok(state)) {
+        _warn!(inner, "Failed to send SSCAN result to caller");
+      }
+    },
+    RedisCommandKind::Hscan => {
+      let tx = scanner.tx.clone();
+      let results = ValueScanInner::transform_hscan_result(result)?;
+
+      let state = ValueScanResult::HScan(HScanResult {
+        can_continue,
+        inner: inner.clone(),
+        scan_state: scanner,
+        results: Some(results),
+      });
+
+      if let Err(_) = tx.send(Ok(state)) {
+        _warn!(inner, "Failed to send HSCAN result to caller");
+      }
+    },
+    _ => {
+      return Err(RedisError::new(
+        RedisErrorKind::Unknown,
+        "Invalid redis command. Expected HSCAN, SSCAN, or ZSCAN.",
+      ))
+    },
+  };
+
+  Ok(())
+}
+
+/// Respond to the caller with the default response policy.
+pub fn respond_to_caller(
+  inner: &RefCount<RedisClientInner>,
+  server: &Server,
+  mut command: RedisCommand,
+  tx: ResponseSender,
+  frame: Resp3Frame,
+) -> Result<(), RedisError> {
+  sample_command_latencies(inner, &mut command);
+  _trace!(
+    inner,
+    "Respond to caller from {} for {} with {:?}",
+    server,
+    command.kind.to_str_debug(),
+    frame.kind()
+  );
+  if command.kind.is_hello() {
+    update_protocol_version(inner, &command, &frame);
+  }
+
+  let _ = tx.send(Ok(frame));
+  command.respond_to_router(inner, RouterResponse::Continue);
+  Ok(())
+}
+
+/// Respond to the caller, assuming multiple response frames from the last command, storing intermediate responses in
+/// the shared buffer.
+pub fn respond_buffer(
+  inner: &RefCount<RedisClientInner>,
+  server: &Server,
+  command: RedisCommand,
+  received: RefCount<AtomicUsize>,
+  expected: usize,
+  error_early: bool,
+  frames: RefCount<Mutex<Vec<Resp3Frame>>>,
+  index: usize,
+  tx: RefCount<Mutex<Option<ResponseSender>>>,
+  frame: Resp3Frame,
+) -> Result<(), RedisError> {
+  _trace!(
+    inner,
+    "Handling `buffer` response from {} for {}. kind {:?}, Index: {}, ID: {}",
+    server,
+    command.kind.to_str_debug(),
+    frame.kind(),
+    index,
+    command.debug_id()
+  );
+
+  // errors are buffered like normal frames and are not returned early
+  if let Err(e) = add_buffered_frame(server, &frames, index, frame) {
+    respond_locked(inner, &tx, Err(e));
+    command.respond_to_router(inner, RouterResponse::Continue);
+    _error!(
+      inner,
+      "Exiting early after unexpected buffer response index from {} with command {}, ID {}",
+      server,
+      command.kind.to_str_debug(),
+      command.debug_id()
+    );
+    return Err(RedisError::new(
+      RedisErrorKind::Unknown,
+      "Invalid buffer response index.",
+    ));
+  }
+
+  // this must come after adding the buffered frame. there's a potential race condition if this task is interrupted
+  // due to contention on the frame lock and another parallel task moves past the `received==expected` check before
+  // this task can add the frame to the buffer.
+  let received = client_utils::incr_atomic(&received);
+  if received == expected {
+    _trace!(
+      inner,
+      "Responding to caller after last buffered response from {}, ID: {}",
+      server,
+      command.debug_id()
+    );
+
+    let frame = merge_multiple_frames(&mut frames.lock(), error_early);
+    if matches!(frame.kind(), FrameKind::SimpleError | FrameKind::BlobError) {
+      let err = match frame.as_str() {
+        Some(s) => protocol_utils::pretty_error(s),
+        None => RedisError::new(
+          RedisErrorKind::Unknown,
+          "Unknown or invalid error from buffered frames.",
+        ),
+      };
+
+      respond_locked(inner, &tx, Err(err));
+    } else {
+      respond_locked(inner, &tx, Ok(frame));
+    }
+    command.respond_to_router(inner, RouterResponse::Continue);
+  } else {
+    // more responses are expected
+    _trace!(
+      inner,
+      "Waiting on {} more responses to all nodes command, ID: {}",
+      expected - received,
+      command.debug_id()
+    );
+    // this response type is shared across connections so we do not return the command to be re-queued
+  }
+
+  Ok(())
+}
+
+/// Respond to the caller of a key scanning operation.
+pub fn respond_key_scan(
+  inner: &RefCount<RedisClientInner>,
+  server: &Server,
+  command: RedisCommand,
+  mut scanner: KeyScanInner,
+  frame: Resp3Frame,
+) -> Result<(), RedisError> {
+  _trace!(
+    inner,
+    "Handling `KeyScan` response from {} for {}",
+    server,
+    command.kind.to_str_debug()
+  );
+  let (next_cursor, keys) = match parse_key_scan_frame(frame) {
+    Ok(result) => result,
+    Err(e) => {
+      scanner.send_error(e);
+      command.respond_to_router(inner, RouterResponse::Continue);
+      return Ok(());
+    },
+  };
+  let scan_stream = scanner.tx.clone();
+  let can_continue = next_cursor != LAST_CURSOR;
+  scanner.update_cursor(next_cursor);
+  command.respond_to_router(inner, RouterResponse::Continue);
+
+  let scan_result = ScanResult {
+    scan_state: scanner,
+    inner: inner.clone(),
+    results: Some(keys),
+    can_continue,
+  };
+  if let Err(_) = scan_stream.send(Ok(scan_result)) {
+    _debug!(inner, "Error sending SCAN page.");
+  }
+
+  Ok(())
+}
+
+/// Respond to the caller of a value scanning operation.
+pub fn respond_value_scan(
+  inner: &RefCount<RedisClientInner>,
+  server: &Server,
+  command: RedisCommand,
+  mut scanner: ValueScanInner,
+  frame: Resp3Frame,
+) -> Result<(), RedisError> {
+  _trace!(
+    inner,
+    "Handling `ValueScan` response from {} for {}",
+    server,
+    command.kind.to_str_debug()
+  );
+
+  let (next_cursor, values) = match parse_value_scan_frame(frame) {
+    Ok(result) => result,
+    Err(e) => {
+      scanner.send_error(e);
+      command.respond_to_router(inner, RouterResponse::Continue);
+      return Ok(());
+    },
+  };
+  let scan_stream = scanner.tx.clone();
+  let can_continue = next_cursor != LAST_CURSOR;
+  scanner.update_cursor(next_cursor);
+  command.respond_to_router(inner, RouterResponse::Continue);
+
+  _trace!(inner, "Sending value scan result with {} values", values.len());
+  if let Err(e) = send_value_scan_result(inner, scanner, &command, values, can_continue) {
+    if let Err(_) = scan_stream.send(Err(e)) {
+      _warn!(inner, "Error sending scan result.");
+    }
+  }
+
+  Ok(())
+}
+
\ No newline at end of file diff --git a/doc/src/fred/protocol/tls.rs.html b/doc/src/fred/protocol/tls.rs.html new file mode 100644 index 00000000..3ced03ae --- /dev/null +++ b/doc/src/fred/protocol/tls.rs.html @@ -0,0 +1,477 @@ +tls.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+
use crate::error::RedisError;
+use std::{
+  fmt,
+  fmt::{Debug, Formatter},
+  net::IpAddr,
+  sync::Arc,
+};
+
+#[cfg(feature = "enable-native-tls")]
+use crate::error::RedisErrorKind;
+#[cfg(feature = "enable-native-tls")]
+use std::convert::{TryFrom, TryInto};
+#[cfg(feature = "enable-native-tls")]
+use tokio_native_tls::native_tls::{
+  TlsConnector as NativeTlsConnector,
+  TlsConnectorBuilder as NativeTlsConnectorBuilder,
+};
+#[cfg(feature = "enable-native-tls")]
+use tokio_native_tls::TlsConnector as TokioNativeTlsConnector;
+#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+use tokio_rustls::rustls::{ClientConfig as RustlsClientConfig, RootCertStore};
+#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+use tokio_rustls::TlsConnector as RustlsConnector;
+
+/// A trait used for mapping IP addresses to hostnames when processing the `CLUSTER SLOTS` response.
+#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-native-tls", feature = "enable-rustls"))))]
+pub trait HostMapping: Send + Sync + Debug {
+  /// Map the provided IP address to a hostname that should be used during the TLS handshake.
+  ///
+  /// The `default_host` argument represents the hostname of the node that returned the `CLUSTER SLOTS` response.
+  ///
+  /// If `None` is returned the client will use the IP address as the server name during the TLS handshake.
+  fn map(&self, ip: &IpAddr, default_host: &str) -> Option<String>;
+}
+
+/// An optional enum used to describe how the client should modify or map IP addresses and hostnames in a clustered
+/// deployment.
+///
+/// This is only necessary to use with a clustered deployment. Centralized or sentinel deployments should use `None`.
+///
+/// More information can be found [here](https://github.com/mna/redisc/issues/13) and [here](https://github.com/lettuce-io/lettuce-core/issues/1454#issuecomment-707537384).
+#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-native-tls", feature = "enable-rustls"))))]
+#[derive(Clone, Debug)]
+pub enum TlsHostMapping {
+  /// Do not modify or replace hostnames or IP addresses in the `CLUSTER SLOTS` response.
+  ///
+  /// Default
+  None,
+  /// Replace any IP addresses in the `CLUSTER SLOTS` response with the hostname of the node that returned
+  /// the `CLUSTER SLOTS` response.
+  ///
+  /// If the `CLUSTER SLOTS` response contains hostnames alongside IP addresses (via the `metadata` block) then
+  /// those hostnames will be used instead. However, this is a relatively new Redis feature and it's likely some
+  /// configurations will not expose this information.
+  DefaultHost,
+  /// Provide a custom mapping from IP address to hostname to be used in a manner similar to a reverse DNS lookup.
+  Custom(Arc<dyn HostMapping>),
+}
+
+impl TlsHostMapping {
+  pub(crate) fn map(&self, value: &IpAddr, default_host: &str) -> Option<String> {
+    match self {
+      TlsHostMapping::None => None,
+      TlsHostMapping::DefaultHost => Some(default_host.to_owned()),
+      TlsHostMapping::Custom(ref inner) => inner.map(value, default_host),
+    }
+  }
+}
+
+impl PartialEq for TlsHostMapping {
+  fn eq(&self, other: &Self) -> bool {
+    match self {
+      TlsHostMapping::None => matches!(other, TlsHostMapping::None),
+      TlsHostMapping::DefaultHost => matches!(other, TlsHostMapping::DefaultHost),
+      TlsHostMapping::Custom(_) => matches!(other, TlsHostMapping::Custom(_)),
+    }
+  }
+}
+
+impl Eq for TlsHostMapping {}
+
+/// TLS configuration for a client.
+///
+/// Note: the `hostnames` field is only necessary to use with certain clustered deployments.
+///
+/// ```rust no_run
+/// # use fred::types::*;
+/// let config = TlsConfig {
+///   // or use `TlsConnector::default_rustls()`
+///   connector: TlsConnector::default_native_tls().unwrap(),
+///   hostnames: TlsHostMapping::None
+/// };
+///
+/// // or use the shorthand
+/// let config: TlsConfig = TlsConnector::default_native_tls()?.into();
+/// let config: TlsConfig = TlsConnector::default_rustls()?.into();
+/// ```
+#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-native-tls", feature = "enable-rustls"))))]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TlsConfig {
+  /// The TLS connector from either `native-tls` or `rustls`.
+  pub connector: TlsConnector,
+  /// The hostname modification or mapping policy to use when discovering and connecting to cluster nodes.
+  pub hostnames: TlsHostMapping,
+}
+
+impl<C: Into<TlsConnector>> From<C> for TlsConfig {
+  fn from(connector: C) -> Self {
+    TlsConfig {
+      connector: connector.into(),
+      hostnames: TlsHostMapping::None,
+    }
+  }
+}
+
+/// An enum for interacting with various TLS libraries and interfaces.
+#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-native-tls", feature = "enable-rustls"))))]
+#[derive(Clone)]
+pub enum TlsConnector {
+  #[cfg(feature = "enable-native-tls")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))]
+  Native(TokioNativeTlsConnector),
+  #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+  #[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))]
+  Rustls(RustlsConnector),
+}
+
+impl PartialEq for TlsConnector {
+  fn eq(&self, _: &Self) -> bool {
+    true
+  }
+}
+
+impl Eq for TlsConnector {}
+
+impl Debug for TlsConnector {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    f.debug_struct("TlsConnector")
+      .field("kind", match self {
+        #[cfg(feature = "enable-native-tls")]
+        TlsConnector::Native(_) => &"Native",
+        #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+        TlsConnector::Rustls(_) => &"Rustls",
+      })
+      .finish()
+  }
+}
+
+#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-native-tls", feature = "enable-rustls"))))]
+impl TlsConnector {
+  /// Create a default TLS connector from the `native-tls` module.
+  #[cfg(feature = "enable-native-tls")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))]
+  pub fn default_native_tls() -> Result<Self, RedisError> {
+    NativeTlsConnector::builder().try_into()
+  }
+
+  /// Create a default TLS connector with the `rustls` module with safe defaults and system certs via [rustls-native-certs](https://github.com/rustls/rustls-native-certs).
+  #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+  #[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))]
+  pub fn default_rustls() -> Result<Self, RedisError> {
+    let system_certs = rustls_native_certs::load_native_certs()?;
+    let mut cert_store = RootCertStore::empty();
+    for system_cert in system_certs.into_iter() {
+      cert_store.add(system_cert)?;
+    }
+
+    Ok(
+      RustlsClientConfig::builder()
+        .with_root_certificates(cert_store)
+        .with_no_client_auth()
+        .into(),
+    )
+  }
+}
+
+#[cfg(feature = "enable-native-tls")]
+#[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))]
+impl TryFrom<NativeTlsConnectorBuilder> for TlsConnector {
+  type Error = RedisError;
+
+  fn try_from(builder: NativeTlsConnectorBuilder) -> Result<Self, Self::Error> {
+    let connector = builder
+      .build()
+      .map(TokioNativeTlsConnector::from)
+      .map_err(|e| RedisError::new(RedisErrorKind::Tls, format!("{:?}", e)))?;
+    Ok(TlsConnector::Native(connector))
+  }
+}
+
+#[cfg(feature = "enable-native-tls")]
+#[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))]
+impl From<NativeTlsConnector> for TlsConnector {
+  fn from(connector: NativeTlsConnector) -> Self {
+    TlsConnector::Native(TokioNativeTlsConnector::from(connector))
+  }
+}
+
+#[cfg(feature = "enable-native-tls")]
+#[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))]
+impl From<TokioNativeTlsConnector> for TlsConnector {
+  fn from(connector: TokioNativeTlsConnector) -> Self {
+    TlsConnector::Native(connector)
+  }
+}
+
+#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))]
+impl From<RustlsClientConfig> for TlsConnector {
+  fn from(config: RustlsClientConfig) -> Self {
+    TlsConnector::Rustls(RustlsConnector::from(Arc::new(config)))
+  }
+}
+
+#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))]
+impl From<RustlsConnector> for TlsConnector {
+  fn from(connector: RustlsConnector) -> Self {
+    TlsConnector::Rustls(connector)
+  }
+}
+
+#[cfg(test)]
+mod tests {
+  use super::*;
+
+  #[test]
+  #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+  fn should_create_default_rustls() {
+    let _ = TlsConnector::default_rustls().unwrap();
+  }
+
+  #[test]
+  #[cfg(feature = "enable-native-tls")]
+  fn should_create_default_native_tls() {
+    let _ = TlsConnector::default_native_tls().unwrap();
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/protocol/types.rs.html b/doc/src/fred/protocol/types.rs.html new file mode 100644 index 00000000..cf58a40c --- /dev/null +++ b/doc/src/fred/protocol/types.rs.html @@ -0,0 +1,1365 @@ +types.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+
use super::utils as protocol_utils;
+use crate::{
+  error::{RedisError, RedisErrorKind},
+  modules::inner::RedisClientInner,
+  prelude::RedisResult,
+  protocol::{cluster, utils::server_to_parts},
+  runtime::{RefCount, UnboundedSender},
+  types::*,
+  utils,
+};
+use async_trait::async_trait;
+use bytes_utils::Str;
+use rand::Rng;
+use redis_protocol::{resp2::types::BytesFrame as Resp2Frame, resp3::types::BytesFrame as Resp3Frame};
+use std::{
+  cmp::Ordering,
+  collections::{BTreeMap, BTreeSet, HashMap},
+  convert::TryInto,
+  fmt::{Display, Formatter},
+  hash::{Hash, Hasher},
+  net::{SocketAddr, ToSocketAddrs},
+};
+
+#[cfg(any(
+  feature = "enable-rustls",
+  feature = "enable-native-tls",
+  feature = "enable-rustls-ring"
+))]
+use std::{net::IpAddr, str::FromStr};
+
+/// Any kind of RESP frame.
+#[derive(Debug)]
+pub enum ProtocolFrame {
+  Resp2(Resp2Frame),
+  Resp3(Resp3Frame),
+}
+
+impl ProtocolFrame {
+  /// Convert the frame to RESP3.
+  pub fn into_resp3(self) -> Resp3Frame {
+    // the `RedisValue::convert` logic already accounts for different encodings of maps and sets, so
+    // we can just change everything to RESP3 above the protocol layer
+    match self {
+      ProtocolFrame::Resp2(frame) => frame.into_resp3(),
+      ProtocolFrame::Resp3(frame) => frame,
+    }
+  }
+
+  /// Whether the frame is encoded as a RESP3 frame.
+  pub fn is_resp3(&self) -> bool {
+    matches!(*self, ProtocolFrame::Resp3(_))
+  }
+}
+
+impl From<Resp2Frame> for ProtocolFrame {
+  fn from(frame: Resp2Frame) -> Self {
+    ProtocolFrame::Resp2(frame)
+  }
+}
+
+impl From<Resp3Frame> for ProtocolFrame {
+  fn from(frame: Resp3Frame) -> Self {
+    ProtocolFrame::Resp3(frame)
+  }
+}
+
+/// State necessary to identify or connect to a server.
+#[derive(Debug, Clone)]
+pub struct Server {
+  /// The hostname or IP address for the server.
+  pub host:            Str,
+  /// The port for the server.
+  pub port:            u16,
+  /// The server name used during the TLS handshake.
+  #[cfg(any(
+    feature = "enable-rustls",
+    feature = "enable-native-tls",
+    feature = "enable-rustls-ring"
+  ))]
+  #[cfg_attr(
+    docsrs,
+    doc(cfg(any(
+      feature = "enable-rustls",
+      feature = "enable-native-tls",
+      feature = "enable-rustls-ring"
+    )))
+  )]
+  pub tls_server_name: Option<Str>,
+}
+
+impl Server {
+  /// Create a new `Server` from parts with a TLS server name.
+  #[cfg(any(
+    feature = "enable-rustls",
+    feature = "enable-native-tls",
+    feature = "enable-rustls-ring"
+  ))]
+  #[cfg_attr(
+    docsrs,
+    doc(cfg(any(
+      feature = "enable-rustls",
+      feature = "enable-native-tls",
+      feature = "enable-rustls-ring"
+    )))
+  )]
+  pub fn new_with_tls<S: Into<Str>>(host: S, port: u16, tls_server_name: Option<String>) -> Self {
+    Server {
+      host: host.into(),
+      port,
+      tls_server_name: tls_server_name.map(|s| s.into()),
+    }
+  }
+
+  /// Create a new `Server` from parts.
+  pub fn new<S: Into<Str>>(host: S, port: u16) -> Self {
+    Server {
+      host: host.into(),
+      port,
+      #[cfg(any(
+        feature = "enable-rustls",
+        feature = "enable-native-tls",
+        feature = "enable-rustls-ring"
+      ))]
+      tls_server_name: None,
+    }
+  }
+
+  #[cfg(any(
+    feature = "enable-rustls",
+    feature = "enable-native-tls",
+    feature = "enable-rustls-ring"
+  ))]
+  pub(crate) fn set_tls_server_name(&mut self, policy: &TlsHostMapping, default_host: &str) {
+    if *policy == TlsHostMapping::None {
+      return;
+    }
+
+    let ip = match IpAddr::from_str(&self.host) {
+      Ok(ip) => ip,
+      Err(_) => return,
+    };
+    if let Some(tls_server_name) = policy.map(&ip, default_host) {
+      self.tls_server_name = Some(Str::from(tls_server_name));
+    }
+  }
+
+  /// Attempt to parse a `host:port` string.
+  pub(crate) fn from_str(s: &str) -> Option<Server> {
+    let parts: Vec<&str> = s.trim().split(':').collect();
+    if parts.len() == 2 {
+      if let Ok(port) = parts[1].parse::<u16>() {
+        Some(Server {
+          host: parts[0].into(),
+          port,
+          #[cfg(any(
+            feature = "enable-rustls",
+            feature = "enable-native-tls",
+            feature = "enable-rustls-ring"
+          ))]
+          tls_server_name: None,
+        })
+      } else {
+        None
+      }
+    } else {
+      None
+    }
+  }
+
+  /// Create a new server struct from a `host:port` string and the default host that sent the last command.
+  pub(crate) fn from_parts(server: &str, default_host: &str) -> Option<Server> {
+    server_to_parts(server).ok().map(|(host, port)| {
+      let host = if host.is_empty() {
+        Str::from(default_host)
+      } else {
+        Str::from(host)
+      };
+
+      Server {
+        host,
+        port,
+        #[cfg(any(
+          feature = "enable-rustls",
+          feature = "enable-native-tls",
+          feature = "enable-rustls-ring"
+        ))]
+        tls_server_name: None,
+      }
+    })
+  }
+}
+
+#[cfg(feature = "unix-sockets")]
+#[doc(hidden)]
+impl From<&std::path::Path> for Server {
+  fn from(value: &std::path::Path) -> Self {
+    Server {
+      host:            utils::path_to_string(value).into(),
+      port:            0,
+      #[cfg(any(
+        feature = "enable-rustls",
+        feature = "enable-native-tls",
+        feature = "enable-rustls-ring"
+      ))]
+      tls_server_name: None,
+    }
+  }
+}
+
+impl TryFrom<String> for Server {
+  type Error = RedisError;
+
+  fn try_from(value: String) -> Result<Self, Self::Error> {
+    Server::from_str(&value).ok_or(RedisError::new(RedisErrorKind::Config, "Invalid `host:port` server."))
+  }
+}
+
+impl TryFrom<&str> for Server {
+  type Error = RedisError;
+
+  fn try_from(value: &str) -> Result<Self, Self::Error> {
+    Server::from_str(value).ok_or(RedisError::new(RedisErrorKind::Config, "Invalid `host:port` server."))
+  }
+}
+
+impl From<(String, u16)> for Server {
+  fn from((host, port): (String, u16)) -> Self {
+    Server {
+      host: host.into(),
+      port,
+      #[cfg(any(
+        feature = "enable-native-tls",
+        feature = "enable-rustls",
+        feature = "enable-rustls-ring"
+      ))]
+      tls_server_name: None,
+    }
+  }
+}
+
+impl From<(&str, u16)> for Server {
+  fn from((host, port): (&str, u16)) -> Self {
+    Server {
+      host: host.into(),
+      port,
+      #[cfg(any(
+        feature = "enable-native-tls",
+        feature = "enable-rustls",
+        feature = "enable-rustls-ring"
+      ))]
+      tls_server_name: None,
+    }
+  }
+}
+
+impl From<&Server> for Server {
+  fn from(value: &Server) -> Self {
+    value.clone()
+  }
+}
+
+impl Display for Server {
+  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+    write!(f, "{}:{}", self.host, self.port)
+  }
+}
+
+impl PartialEq for Server {
+  fn eq(&self, other: &Self) -> bool {
+    self.host == other.host && self.port == other.port
+  }
+}
+
+impl Eq for Server {}
+
+impl Hash for Server {
+  fn hash<H: Hasher>(&self, state: &mut H) {
+    self.host.hash(state);
+    self.port.hash(state);
+  }
+}
+
+impl PartialOrd for Server {
+  fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+    Some(self.cmp(other))
+  }
+}
+
+impl Ord for Server {
+  fn cmp(&self, other: &Self) -> Ordering {
+    let host_ord = self.host.cmp(&other.host);
+    if host_ord == Ordering::Equal {
+      self.port.cmp(&other.port)
+    } else {
+      host_ord
+    }
+  }
+}
+
+/// The kind of pubsub message.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum MessageKind {
+  /// A message from a `subscribe` command.
+  Message,
+  /// A message from a pattern `psubscribe` command.
+  PMessage,
+  /// A message from a sharded `ssubscribe` command.
+  SMessage,
+}
+
+impl MessageKind {
+  pub(crate) fn from_str(s: &str) -> Option<MessageKind> {
+    Some(match s {
+      "message" => MessageKind::Message,
+      "pmessage" => MessageKind::PMessage,
+      "smessage" => MessageKind::SMessage,
+      _ => return None,
+    })
+  }
+}
+
+/// A [publish-subscribe](https://redis.io/docs/manual/pubsub/) message.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Message {
+  /// The channel on which the message was sent.
+  pub channel: Str,
+  /// The message contents.
+  pub value:   RedisValue,
+  /// The type of message subscription.
+  pub kind:    MessageKind,
+  /// The server that sent the message.
+  pub server:  Server,
+}
+
+pub struct KeyScanInner {
+  /// The hash slot for the command.
+  pub hash_slot:  Option<u16>,
+  /// An optional server override.
+  pub server:     Option<Server>,
+  /// The index of the cursor in `args`.
+  pub cursor_idx: usize,
+  /// The arguments sent in each scan command.
+  pub args:       Vec<RedisValue>,
+  /// The sender half of the results channel.
+  pub tx:         UnboundedSender<Result<ScanResult, RedisError>>,
+}
+
+impl KeyScanInner {
+  /// Update the cursor in place in the arguments.
+  pub fn update_cursor(&mut self, cursor: Str) {
+    self.args[self.cursor_idx] = cursor.into();
+  }
+
+  /// Send an error on the response stream.
+  pub fn send_error(&self, error: RedisError) {
+    let _ = self.tx.send(Err(error));
+  }
+}
+
+pub enum ValueScanResult {
+  SScan(SScanResult),
+  HScan(HScanResult),
+  ZScan(ZScanResult),
+}
+
+pub struct ValueScanInner {
+  /// The index of the cursor argument in `args`.
+  pub cursor_idx: usize,
+  /// The arguments sent in each scan command.
+  pub args:       Vec<RedisValue>,
+  /// The sender half of the results channel.
+  pub tx:         UnboundedSender<Result<ValueScanResult, RedisError>>,
+}
+
+impl ValueScanInner {
+  /// Update the cursor in place in the arguments.
+  pub fn update_cursor(&mut self, cursor: Str) {
+    self.args[self.cursor_idx] = cursor.into();
+  }
+
+  /// Send an error on the response stream.
+  pub fn send_error(&self, error: RedisError) {
+    let _ = self.tx.send(Err(error));
+  }
+
+  pub fn transform_hscan_result(mut data: Vec<RedisValue>) -> Result<RedisMap, RedisError> {
+    if data.is_empty() {
+      return Ok(RedisMap::new());
+    }
+    if data.len() % 2 != 0 {
+      return Err(RedisError::new(
+        RedisErrorKind::Protocol,
+        "Invalid HSCAN result. Expected array with an even number of elements.",
+      ));
+    }
+
+    let mut out = HashMap::with_capacity(data.len() / 2);
+    while data.len() >= 2 {
+      let value = data.pop().unwrap();
+      let key: RedisKey = match data.pop().unwrap() {
+        RedisValue::String(s) => s.into(),
+        RedisValue::Bytes(b) => b.into(),
+        _ => {
+          return Err(RedisError::new(
+            RedisErrorKind::Protocol,
+            "Invalid HSCAN result. Expected string.",
+          ))
+        },
+      };
+
+      out.insert(key, value);
+    }
+
+    out.try_into()
+  }
+
+  pub fn transform_zscan_result(mut data: Vec<RedisValue>) -> Result<Vec<(RedisValue, f64)>, RedisError> {
+    if data.is_empty() {
+      return Ok(Vec::new());
+    }
+    if data.len() % 2 != 0 {
+      return Err(RedisError::new(
+        RedisErrorKind::Protocol,
+        "Invalid ZSCAN result. Expected array with an even number of elements.",
+      ));
+    }
+
+    let mut out = Vec::with_capacity(data.len() / 2);
+
+    for chunk in data.chunks_exact_mut(2) {
+      let value = chunk[0].take();
+      let score = match chunk[1].take() {
+        RedisValue::String(s) => utils::redis_string_to_f64(&s)?,
+        RedisValue::Integer(i) => i as f64,
+        RedisValue::Double(f) => f,
+        _ => {
+          return Err(RedisError::new(
+            RedisErrorKind::Protocol,
+            "Invalid HSCAN result. Expected a string or number score.",
+          ))
+        },
+      };
+
+      out.push((value, score));
+    }
+
+    Ok(out)
+  }
+}
+
+/// A slot range and associated cluster node information from the `CLUSTER SLOTS` command.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct SlotRange {
+  /// The start of the hash slot range.
+  pub start:    u16,
+  /// The end of the hash slot range.
+  pub end:      u16,
+  /// The primary server owner.
+  pub primary:  Server,
+  /// The internal ID assigned by the server.
+  pub id:       Str,
+  /// Replica node owners.
+  #[cfg(feature = "replicas")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+  pub replicas: Vec<Server>,
+}
+
+/// The cached view of the cluster used by the client to route commands to the correct cluster nodes.
+#[derive(Debug, Clone)]
+pub struct ClusterRouting {
+  data: Vec<SlotRange>,
+}
+
+impl ClusterRouting {
+  /// Create a new empty routing table.
+  pub fn new() -> Self {
+    ClusterRouting { data: Vec::new() }
+  }
+
+  /// Create a new routing table from the result of the `CLUSTER SLOTS` command.
+  ///
+  /// The `default_host` value refers to the server that provided the response.
+  pub fn from_cluster_slots<S: Into<Str>>(value: RedisValue, default_host: S) -> Result<Self, RedisError> {
+    let default_host = default_host.into();
+    let mut data = cluster::parse_cluster_slots(value, &default_host)?;
+    data.sort_by(|a, b| a.start.cmp(&b.start));
+
+    Ok(ClusterRouting { data })
+  }
+
+  /// Read a set of unique hash slots that each map to a different primary/main node in the cluster.
+  pub fn unique_hash_slots(&self) -> Vec<u16> {
+    let mut out = BTreeMap::new();
+
+    for slot in self.data.iter() {
+      out.insert(&slot.primary, slot.start);
+    }
+
+    out.into_iter().map(|(_, v)| v).collect()
+  }
+
+  /// Read the set of unique primary nodes in the cluster.
+  pub fn unique_primary_nodes(&self) -> Vec<Server> {
+    let mut out = BTreeSet::new();
+
+    for slot in self.data.iter() {
+      out.insert(slot.primary.clone());
+    }
+
+    out.into_iter().collect()
+  }
+
+  /// Rebuild the cache in place with the output of a `CLUSTER SLOTS` command.
+  pub(crate) fn rebuild(
+    &mut self,
+    inner: &RefCount<RedisClientInner>,
+    cluster_slots: RedisValue,
+    default_host: &Str,
+  ) -> Result<(), RedisError> {
+    self.data = cluster::parse_cluster_slots(cluster_slots, default_host)?;
+    self.data.sort_by(|a, b| a.start.cmp(&b.start));
+
+    cluster::modify_cluster_slot_hostnames(inner, &mut self.data, default_host);
+    Ok(())
+  }
+
+  /// Calculate the cluster hash slot for the provided key.
+  pub fn hash_key(key: &[u8]) -> u16 {
+    redis_protocol::redis_keyslot(key)
+  }
+
+  /// Find the primary server that owns the provided hash slot.
+  pub fn get_server(&self, slot: u16) -> Option<&Server> {
+    if self.data.is_empty() {
+      return None;
+    }
+
+    protocol_utils::binary_search(&self.data, slot).map(|idx| &self.data[idx].primary)
+  }
+
+  /// Read the replicas associated with the provided primary node based on the cached CLUSTER SLOTS response.
+  #[cfg(feature = "replicas")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+  pub fn replicas(&self, primary: &Server) -> Vec<Server> {
+    self
+      .data
+      .iter()
+      .fold(BTreeSet::new(), |mut replicas, slot| {
+        if slot.primary == *primary {
+          replicas.extend(slot.replicas.clone());
+        }
+
+        replicas
+      })
+      .into_iter()
+      .collect()
+  }
+
+  /// Read the number of hash slot ranges in the cluster.
+  pub fn len(&self) -> usize {
+    self.data.len()
+  }
+
+  /// Read the hash slot ranges in the cluster.
+  pub fn slots(&self) -> &[SlotRange] {
+    &self.data
+  }
+
+  /// Read a random primary node hash slot range from the cluster cache.
+  pub fn random_slot(&self) -> Option<&SlotRange> {
+    if !self.data.is_empty() {
+      let idx = rand::thread_rng().gen_range(0 .. self.data.len());
+      Some(&self.data[idx])
+    } else {
+      None
+    }
+  }
+
+  /// Read a random primary node from the cluster cache.
+  pub fn random_node(&self) -> Option<&Server> {
+    self.random_slot().map(|slot| &slot.primary)
+  }
+
+  /// Print the contents of the routing table as a human-readable map.
+  pub fn pretty(&self) -> BTreeMap<Server, (Vec<(u16, u16)>, BTreeSet<Server>)> {
+    let mut out = BTreeMap::new();
+    for slot_range in self.data.iter() {
+      let entry = out
+        .entry(slot_range.primary.clone())
+        .or_insert((Vec::new(), BTreeSet::new()));
+      entry.0.push((slot_range.start, slot_range.end));
+      #[cfg(feature = "replicas")]
+      entry.1.extend(slot_range.replicas.iter().cloned());
+    }
+
+    out
+  }
+}
+
+/// Default DNS resolver that uses [to_socket_addrs](std::net::ToSocketAddrs::to_socket_addrs).
+#[derive(Clone, Debug)]
+pub struct DefaultResolver {
+  id: Str,
+}
+
+impl DefaultResolver {
+  /// Create a new resolver using the system's default DNS resolution.
+  pub fn new(id: &Str) -> Self {
+    DefaultResolver { id: id.clone() }
+  }
+}
+
+/// A trait that can be used to override DNS resolution logic.
+///
+/// Note: currently this requires [async-trait](https://crates.io/crates/async-trait).
+#[cfg(feature = "glommio")]
+#[async_trait(?Send)]
+#[cfg_attr(docsrs, doc(cfg(feature = "dns")))]
+pub trait Resolve: 'static {
+  /// Resolve a hostname.
+  async fn resolve(&self, host: Str, port: u16) -> RedisResult<Vec<SocketAddr>>;
+}
+
+#[cfg(feature = "glommio")]
+#[async_trait(?Send)]
+impl Resolve for DefaultResolver {
+  async fn resolve(&self, host: Str, port: u16) -> RedisResult<Vec<SocketAddr>> {
+    let client_id = self.id.clone();
+
+    // glommio users should probably use a non-blocking impl such as hickory-dns
+    crate::runtime::spawn(async move {
+      let addr = format!("{}:{}", host, port);
+      let ips: Vec<SocketAddr> = addr.to_socket_addrs()?.collect();
+
+      if ips.is_empty() {
+        Err(RedisError::new(
+          RedisErrorKind::IO,
+          format!("Failed to resolve {}:{}", host, port),
+        ))
+      } else {
+        trace!("{}: Found {} addresses for {}", client_id, ips.len(), addr);
+        Ok(ips)
+      }
+    })
+    .await?
+  }
+}
+
+/// A trait that can be used to override DNS resolution logic.
+///
+/// Note: currently this requires [async-trait](https://crates.io/crates/async-trait).
+#[cfg(not(feature = "glommio"))]
+#[async_trait]
+#[cfg_attr(docsrs, doc(cfg(feature = "dns")))]
+pub trait Resolve: Send + Sync + 'static {
+  /// Resolve a hostname.
+  async fn resolve(&self, host: Str, port: u16) -> RedisResult<Vec<SocketAddr>>;
+}
+
+#[cfg(not(feature = "glommio"))]
+#[async_trait]
+impl Resolve for DefaultResolver {
+  async fn resolve(&self, host: Str, port: u16) -> RedisResult<Vec<SocketAddr>> {
+    let client_id = self.id.clone();
+
+    tokio::task::spawn_blocking(move || {
+      let addr = format!("{}:{}", host, port);
+      let ips: Vec<SocketAddr> = addr.to_socket_addrs()?.collect();
+
+      if ips.is_empty() {
+        Err(RedisError::new(
+          RedisErrorKind::IO,
+          format!("Failed to resolve {}:{}", host, port),
+        ))
+      } else {
+        trace!("{}: Found {} addresses for {}", client_id, ips.len(), addr);
+        Ok(ips)
+      }
+    })
+    .await?
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/protocol/utils.rs.html b/doc/src/fred/protocol/utils.rs.html new file mode 100644 index 00000000..26cbe1d5 --- /dev/null +++ b/doc/src/fred/protocol/utils.rs.html @@ -0,0 +1,2547 @@ +utils.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
+1265
+1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  modules::inner::RedisClientInner,
+  protocol::{
+    codec::RedisCodec,
+    command::{ClusterErrorKind, RedisCommand, RedisCommandKind},
+    connection::OK,
+    types::{ProtocolFrame, *},
+  },
+  runtime::RefCount,
+  types::*,
+  utils,
+};
+use bytes::Bytes;
+use bytes_utils::Str;
+use redis_protocol::{
+  resp2::types::{BytesFrame as Resp2Frame, Resp2Frame as _Resp2Frame},
+  resp3::types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame},
+  types::{PUBSUB_PUSH_PREFIX, REDIS_CLUSTER_SLOTS},
+};
+use std::{borrow::Cow, collections::HashMap, convert::TryInto, ops::Deref, str};
+
+#[cfg(any(feature = "i-lists", feature = "i-sorted-sets"))]
+use redis_protocol::resp3::types::FrameKind;
+#[cfg(feature = "i-hashes")]
+use redis_protocol::resp3::types::FrameMap;
+
+static LEGACY_AUTH_ERROR_BODY: &str = "ERR Client sent AUTH, but no password is set";
+static ACL_AUTH_ERROR_PREFIX: &str =
+  "ERR AUTH <password> called without any password configured for the default user";
+
+pub fn parse_cluster_error(data: &str) -> Result<(ClusterErrorKind, u16, String), RedisError> {
+  let parts: Vec<&str> = data.split(' ').collect();
+  if parts.len() == 3 {
+    let kind: ClusterErrorKind = parts[0].try_into()?;
+    let slot: u16 = parts[1].parse()?;
+    let server = parts[2].to_string();
+
+    Ok((kind, slot, server))
+  } else {
+    Err(RedisError::new(RedisErrorKind::Protocol, "Expected cluster error."))
+  }
+}
+
+pub fn queued_frame() -> Resp3Frame {
+  Resp3Frame::SimpleString {
+    data:       utils::static_bytes(QUEUED.as_bytes()),
+    attributes: None,
+  }
+}
+
+pub fn is_ok(frame: &Resp3Frame) -> bool {
+  match frame {
+    Resp3Frame::SimpleString { ref data, .. } => data == OK,
+    _ => false,
+  }
+}
+
+pub fn server_to_parts(server: &str) -> Result<(&str, u16), RedisError> {
+  let parts: Vec<&str> = server.split(':').collect();
+  if parts.len() < 2 {
+    return Err(RedisError::new(RedisErrorKind::IO, "Invalid server."));
+  }
+  Ok((parts[0], parts[1].parse::<u16>()?))
+}
+
+pub fn binary_search(slots: &[SlotRange], slot: u16) -> Option<usize> {
+  if slot > REDIS_CLUSTER_SLOTS {
+    return None;
+  }
+
+  let (mut low, mut high) = (0, slots.len() - 1);
+  while low <= high {
+    let mid = (low + high) / 2;
+
+    let curr = match slots.get(mid) {
+      Some(slot) => slot,
+      None => {
+        warn!("Failed to find slot range at index {} for hash slot {}", mid, slot);
+        return None;
+      },
+    };
+
+    if slot < curr.start {
+      high = mid - 1;
+    } else if slot > curr.end {
+      low = mid + 1;
+    } else {
+      return Some(mid);
+    }
+  }
+
+  None
+}
+
+pub fn pretty_error(resp: &str) -> RedisError {
+  let kind = {
+    let mut parts = resp.split_whitespace();
+
+    match parts.next().unwrap_or("") {
+      "" => RedisErrorKind::Unknown,
+      "ERR" => {
+        if resp.contains("instance has cluster support disabled") {
+          // Cluster client connecting to non-cluster server.
+          // Returning Config to signal no reconnect will help.
+          RedisErrorKind::Config
+        } else {
+          RedisErrorKind::Unknown
+        }
+      },
+      "WRONGTYPE" => RedisErrorKind::InvalidArgument,
+      "NOAUTH" | "WRONGPASS" => RedisErrorKind::Auth,
+      "MOVED" | "ASK" | "CLUSTERDOWN" => RedisErrorKind::Cluster,
+      "Invalid" => match parts.next().unwrap_or("") {
+        "argument(s)" | "Argument" => RedisErrorKind::InvalidArgument,
+        "command" | "Command" => RedisErrorKind::InvalidCommand,
+        _ => RedisErrorKind::Unknown,
+      },
+      _ => RedisErrorKind::Unknown,
+    }
+  };
+
+  let details = if resp.is_empty() {
+    Cow::Borrowed("No response!")
+  } else {
+    Cow::Owned(resp.to_owned())
+  };
+  RedisError::new(kind, details)
+}
+
+/// Parse the frame as a string, without support for error frames.
+pub fn frame_into_string(frame: Resp3Frame) -> Result<String, RedisError> {
+  match frame {
+    Resp3Frame::SimpleString { data, .. } => Ok(String::from_utf8(data.to_vec())?),
+    Resp3Frame::BlobString { data, .. } => Ok(String::from_utf8(data.to_vec())?),
+    Resp3Frame::Double { data, .. } => Ok(data.to_string()),
+    Resp3Frame::Number { data, .. } => Ok(data.to_string()),
+    Resp3Frame::Boolean { data, .. } => Ok(data.to_string()),
+    Resp3Frame::VerbatimString { data, .. } => Ok(String::from_utf8(data.to_vec())?),
+    Resp3Frame::BigNumber { data, .. } => Ok(String::from_utf8(data.to_vec())?),
+    Resp3Frame::SimpleError { data, .. } => Err(pretty_error(&data)),
+    Resp3Frame::BlobError { data, .. } => Err(pretty_error(str::from_utf8(&data)?)),
+    _ => Err(RedisError::new(RedisErrorKind::Protocol, "Expected string.")),
+  }
+}
+
+/// Parse the frame from a shard pubsub channel.
+// TODO clean this up with the v5 redis_protocol interface
+pub fn parse_shard_pubsub_frame(server: &Server, frame: &Resp3Frame) -> Option<Message> {
+  let value = match frame {
+    Resp3Frame::Array { ref data, .. } | Resp3Frame::Push { ref data, .. } => {
+      if data.len() >= 3 && data.len() <= 5 {
+        // check both resp2 and resp3 formats
+        let has_either_prefix = (data[0].as_str().map(|s| s == PUBSUB_PUSH_PREFIX).unwrap_or(false)
+          && data[1].as_str().map(|s| s == "smessage").unwrap_or(false))
+          || (data[0].as_str().map(|s| s == "smessage").unwrap_or(false));
+
+        if has_either_prefix {
+          let channel = match frame_to_str(&data[data.len() - 2]) {
+            Some(channel) => channel,
+            None => return None,
+          };
+          let message = match frame_to_results(data[data.len() - 1].clone()) {
+            Ok(message) => message,
+            Err(_) => return None,
+          };
+
+          Some((channel, message))
+        } else {
+          None
+        }
+      } else {
+        None
+      }
+    },
+    _ => None,
+  };
+
+  value.map(|(channel, value)| Message {
+    channel,
+    value,
+    kind: MessageKind::SMessage,
+    server: server.clone(),
+  })
+}
+
+/// Parse the kind of pubsub message (pattern, sharded, or regular).
+pub fn parse_message_kind(frame: &Resp3Frame) -> Result<MessageKind, RedisError> {
+  let frames = match frame {
+    Resp3Frame::Array { ref data, .. } => data,
+    Resp3Frame::Push { ref data, .. } => data,
+    _ => return Err(RedisError::new(RedisErrorKind::Protocol, "Invalid pubsub frame type.")),
+  };
+
+  let parsed = if frames.len() == 3 {
+    // resp2 format, normal message
+    frames[0].as_str().and_then(MessageKind::from_str)
+  } else if frames.len() == 4 {
+    // resp3 normal message or resp2 pattern/shard message
+    frames[1]
+      .as_str()
+      .and_then(MessageKind::from_str)
+      .or(frames[0].as_str().and_then(MessageKind::from_str))
+  } else if frames.len() == 5 {
+    // resp3 pattern or shard message
+    frames[1]
+      .as_str()
+      .and_then(MessageKind::from_str)
+      .or(frames[2].as_str().and_then(MessageKind::from_str))
+  } else {
+    None
+  };
+
+  parsed.ok_or(RedisError::new(
+    RedisErrorKind::Protocol,
+    "Invalid pubsub message kind.",
+  ))
+}
+
+/// Parse the channel and value fields from a pubsub frame.
+pub fn parse_message_fields(frame: Resp3Frame) -> Result<(Str, RedisValue), RedisError> {
+  let mut frames = match frame {
+    Resp3Frame::Array { data, .. } => data,
+    Resp3Frame::Push { data, .. } => data,
+    _ => return Err(RedisError::new(RedisErrorKind::Protocol, "Invalid pubsub frame type.")),
+  };
+
+  let value = frames
+    .pop()
+    .ok_or(RedisError::new(RedisErrorKind::Protocol, "Invalid pubsub message."))?;
+  let channel = frames
+    .pop()
+    .ok_or(RedisError::new(RedisErrorKind::Protocol, "Invalid pubsub channel."))?;
+  let channel =
+    frame_to_str(&channel).ok_or(RedisError::new(RedisErrorKind::Protocol, "Failed to parse channel."))?;
+  let value = frame_to_results(value)?;
+
+  Ok((channel, value))
+}
+
+/// Parse the frame as a pubsub message.
+pub fn frame_to_pubsub(server: &Server, frame: Resp3Frame) -> Result<Message, RedisError> {
+  if let Some(message) = parse_shard_pubsub_frame(server, &frame) {
+    return Ok(message);
+  }
+
+  let kind = parse_message_kind(&frame)?;
+  let (channel, value) = parse_message_fields(frame)?;
+
+  Ok(Message {
+    kind,
+    channel,
+    value,
+    server: server.clone(),
+  })
+}
+
+pub fn check_resp2_auth_error(codec: &RedisCodec, frame: Resp2Frame) -> Resp2Frame {
+  let is_auth_error = match frame {
+    Resp2Frame::Error(ref data) => *data == LEGACY_AUTH_ERROR_BODY || data.starts_with(ACL_AUTH_ERROR_PREFIX),
+    _ => false,
+  };
+
+  if is_auth_error {
+    warn!(
+      "{}: [{}] Dropping unused auth warning: {}",
+      codec.name,
+      codec.server,
+      frame.as_str().unwrap_or("")
+    );
+    Resp2Frame::SimpleString(utils::static_bytes(OK.as_bytes()))
+  } else {
+    frame
+  }
+}
+
+pub fn check_resp3_auth_error(codec: &RedisCodec, frame: Resp3Frame) -> Resp3Frame {
+  let is_auth_error = match frame {
+    Resp3Frame::SimpleError { ref data, .. } => {
+      *data == LEGACY_AUTH_ERROR_BODY || data.starts_with(ACL_AUTH_ERROR_PREFIX)
+    },
+    _ => false,
+  };
+
+  if is_auth_error {
+    warn!(
+      "{}: [{}] Dropping unused auth warning: {}",
+      codec.name,
+      codec.server,
+      frame.as_str().unwrap_or("")
+    );
+    Resp3Frame::SimpleString {
+      data:       utils::static_bytes(OK.as_bytes()),
+      attributes: None,
+    }
+  } else {
+    frame
+  }
+}
+
+/// Try to parse the data as a string, and failing that return a byte slice.
+pub fn string_or_bytes(data: Bytes) -> RedisValue {
+  if let Ok(s) = Str::from_inner(data.clone()) {
+    RedisValue::String(s)
+  } else {
+    RedisValue::Bytes(data)
+  }
+}
+
+pub fn frame_to_bytes(frame: &Resp3Frame) -> Option<Bytes> {
+  match frame {
+    Resp3Frame::BigNumber { data, .. } => Some(data.clone()),
+    Resp3Frame::VerbatimString { data, .. } => Some(data.clone()),
+    Resp3Frame::BlobString { data, .. } => Some(data.clone()),
+    Resp3Frame::SimpleString { data, .. } => Some(data.clone()),
+    Resp3Frame::BlobError { data, .. } => Some(data.clone()),
+    Resp3Frame::SimpleError { data, .. } => Some(data.inner().clone()),
+    _ => None,
+  }
+}
+
+pub fn frame_to_str(frame: &Resp3Frame) -> Option<Str> {
+  match frame {
+    Resp3Frame::BigNumber { data, .. } => Str::from_inner(data.clone()).ok(),
+    Resp3Frame::VerbatimString { data, .. } => Str::from_inner(data.clone()).ok(),
+    Resp3Frame::BlobString { data, .. } => Str::from_inner(data.clone()).ok(),
+    Resp3Frame::SimpleString { data, .. } => Str::from_inner(data.clone()).ok(),
+    Resp3Frame::BlobError { data, .. } => Str::from_inner(data.clone()).ok(),
+    Resp3Frame::SimpleError { data, .. } => Some(data.clone()),
+    _ => None,
+  }
+}
+
+#[cfg(feature = "i-hashes")]
+fn parse_nested_map(data: FrameMap<Resp3Frame, Resp3Frame>) -> Result<RedisMap, RedisError> {
+  let mut out = HashMap::with_capacity(data.len());
+
+  for (key, value) in data.into_iter() {
+    let key: RedisKey = frame_to_results(key)?.try_into()?;
+    let value = frame_to_results(value)?;
+
+    out.insert(key, value);
+  }
+
+  Ok(RedisMap { inner: out })
+}
+
+/// Convert `nil` responses to a generic `Timeout` error.
+#[cfg(any(feature = "i-lists", feature = "i-sorted-sets"))]
+pub fn check_null_timeout(frame: &Resp3Frame) -> Result<(), RedisError> {
+  if frame.kind() == FrameKind::Null {
+    Err(RedisError::new(RedisErrorKind::Timeout, "Request timed out."))
+  } else {
+    Ok(())
+  }
+}
+
+/// Parse the protocol frame into a redis value, with support for arbitrarily nested arrays.
+pub fn frame_to_results(frame: Resp3Frame) -> Result<RedisValue, RedisError> {
+  let value = match frame {
+    Resp3Frame::Null => RedisValue::Null,
+    Resp3Frame::SimpleString { data, .. } => {
+      let value = string_or_bytes(data);
+
+      if value.as_str().map(|s| s == QUEUED).unwrap_or(false) {
+        RedisValue::Queued
+      } else {
+        value
+      }
+    },
+    Resp3Frame::SimpleError { data, .. } => return Err(pretty_error(&data)),
+    Resp3Frame::BlobString { data, .. } => string_or_bytes(data),
+    Resp3Frame::BlobError { data, .. } => {
+      let parsed = String::from_utf8_lossy(&data);
+      return Err(pretty_error(parsed.as_ref()));
+    },
+    Resp3Frame::VerbatimString { data, .. } => string_or_bytes(data),
+    Resp3Frame::Number { data, .. } => data.into(),
+    Resp3Frame::Double { data, .. } => data.into(),
+    Resp3Frame::BigNumber { data, .. } => string_or_bytes(data),
+    Resp3Frame::Boolean { data, .. } => data.into(),
+    Resp3Frame::Array { data, .. } | Resp3Frame::Push { data, .. } => RedisValue::Array(
+      data
+        .into_iter()
+        .map(frame_to_results)
+        .collect::<Result<Vec<RedisValue>, _>>()?,
+    ),
+    Resp3Frame::Set { data, .. } => RedisValue::Array(
+      data
+        .into_iter()
+        .map(frame_to_results)
+        .collect::<Result<Vec<RedisValue>, _>>()?,
+    ),
+    Resp3Frame::Map { data, .. } => {
+      let mut out = HashMap::with_capacity(data.len());
+      for (key, value) in data.into_iter() {
+        let key: RedisKey = frame_to_results(key)?.try_into()?;
+        let value = frame_to_results(value)?;
+
+        out.insert(key, value);
+      }
+
+      RedisValue::Map(RedisMap { inner: out })
+    },
+    _ => {
+      return Err(RedisError::new(
+        RedisErrorKind::Protocol,
+        "Invalid response frame type.",
+      ))
+    },
+  };
+
+  Ok(value)
+}
+
+/// Flatten a single nested layer of arrays or sets into one array.
+#[cfg(feature = "i-hashes")]
+pub fn flatten_frame(frame: Resp3Frame) -> Resp3Frame {
+  match frame {
+    Resp3Frame::Array { data, .. } => {
+      let count = data.iter().fold(0, |c, f| {
+        c + match f {
+          Resp3Frame::Push { ref data, .. } => data.len(),
+          Resp3Frame::Array { ref data, .. } => data.len(),
+          Resp3Frame::Set { ref data, .. } => data.len(),
+          _ => 1,
+        }
+      });
+
+      let mut out = Vec::with_capacity(count);
+      for frame in data.into_iter() {
+        match frame {
+          Resp3Frame::Push { data, .. } => out.extend(data),
+          Resp3Frame::Array { data, .. } => out.extend(data),
+          Resp3Frame::Set { data, .. } => out.extend(data),
+          _ => out.push(frame),
+        };
+      }
+
+      Resp3Frame::Array {
+        data:       out,
+        attributes: None,
+      }
+    },
+    Resp3Frame::Set { data, .. } => {
+      let count = data.iter().fold(0, |c, f| {
+        c + match f {
+          Resp3Frame::Array { ref data, .. } => data.len(),
+          Resp3Frame::Set { ref data, .. } => data.len(),
+          _ => 1,
+        }
+      });
+
+      let mut out = Vec::with_capacity(count);
+      for frame in data.into_iter() {
+        match frame {
+          Resp3Frame::Array { data, .. } => out.extend(data),
+          Resp3Frame::Set { data, .. } => out.extend(data),
+          _ => out.push(frame),
+        };
+      }
+
+      Resp3Frame::Array {
+        data:       out,
+        attributes: None,
+      }
+    },
+    _ => frame,
+  }
+}
+
+#[cfg(feature = "i-hashes")]
+/// Convert a frame to a nested RedisMap.
+pub fn frame_to_map(frame: Resp3Frame) -> Result<RedisMap, RedisError> {
+  match frame {
+    Resp3Frame::Array { mut data, .. } => {
+      if data.is_empty() {
+        return Ok(RedisMap::new());
+      }
+      if data.len() % 2 != 0 {
+        return Err(RedisError::new(
+          RedisErrorKind::Protocol,
+          "Expected an even number of frames.",
+        ));
+      }
+
+      let mut inner = HashMap::with_capacity(data.len() / 2);
+      while data.len() >= 2 {
+        let value = frame_to_results(data.pop().unwrap())?;
+        let key = frame_to_results(data.pop().unwrap())?.try_into()?;
+
+        inner.insert(key, value);
+      }
+
+      Ok(RedisMap { inner })
+    },
+    Resp3Frame::Map { data, .. } => parse_nested_map(data),
+    Resp3Frame::SimpleError { data, .. } => Err(pretty_error(&data)),
+    Resp3Frame::BlobError { data, .. } => {
+      let parsed = String::from_utf8_lossy(&data);
+      Err(pretty_error(&parsed))
+    },
+    _ => Err(RedisError::new(
+      RedisErrorKind::Protocol,
+      "Expected array or map frames.",
+    )),
+  }
+}
+
+/// Convert a frame to a `RedisError`.
+pub fn frame_to_error(frame: &Resp3Frame) -> Option<RedisError> {
+  match frame {
+    Resp3Frame::SimpleError { ref data, .. } => Some(pretty_error(data)),
+    Resp3Frame::BlobError { ref data, .. } => {
+      let parsed = String::from_utf8_lossy(data);
+      Some(pretty_error(parsed.as_ref()))
+    },
+    _ => None,
+  }
+}
+
+pub fn value_to_outgoing_resp2_frame(value: &RedisValue) -> Result<Resp2Frame, RedisError> {
+  let frame = match value {
+    RedisValue::Double(ref f) => Resp2Frame::BulkString(f.to_string().into()),
+    RedisValue::Boolean(ref b) => Resp2Frame::BulkString(b.to_string().into()),
+    RedisValue::Integer(ref i) => Resp2Frame::BulkString(i.to_string().into()),
+    RedisValue::String(ref s) => Resp2Frame::BulkString(s.inner().clone()),
+    RedisValue::Bytes(ref b) => Resp2Frame::BulkString(b.clone()),
+    RedisValue::Queued => Resp2Frame::BulkString(Bytes::from_static(QUEUED.as_bytes())),
+    RedisValue::Null => Resp2Frame::Null,
+    _ => {
+      return Err(RedisError::new(
+        RedisErrorKind::InvalidArgument,
+        format!("Invalid argument type: {}", value.kind()),
+      ))
+    },
+  };
+
+  Ok(frame)
+}
+
+pub fn value_to_outgoing_resp3_frame(value: &RedisValue) -> Result<Resp3Frame, RedisError> {
+  let frame = match value {
+    RedisValue::Double(ref f) => Resp3Frame::BlobString {
+      data:       f.to_string().into(),
+      attributes: None,
+    },
+    RedisValue::Boolean(ref b) => Resp3Frame::BlobString {
+      data:       b.to_string().into(),
+      attributes: None,
+    },
+    RedisValue::Integer(ref i) => Resp3Frame::BlobString {
+      data:       i.to_string().into(),
+      attributes: None,
+    },
+    RedisValue::String(ref s) => Resp3Frame::BlobString {
+      data:       s.inner().clone(),
+      attributes: None,
+    },
+    RedisValue::Bytes(ref b) => Resp3Frame::BlobString {
+      data:       b.clone(),
+      attributes: None,
+    },
+    RedisValue::Queued => Resp3Frame::BlobString {
+      data:       Bytes::from_static(QUEUED.as_bytes()),
+      attributes: None,
+    },
+    RedisValue::Null => Resp3Frame::Null,
+    _ => {
+      return Err(RedisError::new(
+        RedisErrorKind::InvalidArgument,
+        format!("Invalid argument type: {}", value.kind()),
+      ))
+    },
+  };
+
+  Ok(frame)
+}
+
+#[cfg(feature = "mocks")]
+pub fn mocked_value_to_frame(value: RedisValue) -> Resp3Frame {
+  match value {
+    RedisValue::Array(values) => Resp3Frame::Array {
+      data:       values.into_iter().map(mocked_value_to_frame).collect(),
+      attributes: None,
+    },
+    RedisValue::Map(values) => Resp3Frame::Map {
+      data:       values
+        .inner()
+        .into_iter()
+        .map(|(key, value)| (mocked_value_to_frame(key.into()), mocked_value_to_frame(value)))
+        .collect(),
+      attributes: None,
+    },
+    RedisValue::Null => Resp3Frame::Null,
+    RedisValue::Queued => Resp3Frame::SimpleString {
+      data:       Bytes::from_static(QUEUED.as_bytes()),
+      attributes: None,
+    },
+    RedisValue::Bytes(value) => Resp3Frame::BlobString {
+      data:       value,
+      attributes: None,
+    },
+    RedisValue::Boolean(value) => Resp3Frame::Boolean {
+      data:       value,
+      attributes: None,
+    },
+    RedisValue::Integer(value) => Resp3Frame::Number {
+      data:       value,
+      attributes: None,
+    },
+    RedisValue::Double(value) => Resp3Frame::Double {
+      data:       value,
+      attributes: None,
+    },
+    RedisValue::String(value) => Resp3Frame::BlobString {
+      data:       value.into_inner(),
+      attributes: None,
+    },
+  }
+}
+
+pub fn expect_ok(value: &RedisValue) -> Result<(), RedisError> {
+  match *value {
+    RedisValue::String(ref resp) => {
+      if resp.deref() == OK || resp.deref() == QUEUED {
+        Ok(())
+      } else {
+        Err(RedisError::new(
+          RedisErrorKind::Unknown,
+          format!("Expected OK, found {}", resp),
+        ))
+      }
+    },
+    _ => Err(RedisError::new(
+      RedisErrorKind::Unknown,
+      format!("Expected OK, found {:?}.", value),
+    )),
+  }
+}
+
+/// Parse the replicas from the ROLE response returned from a master/primary node.
+#[cfg(feature = "replicas")]
+pub fn parse_master_role_replicas(data: RedisValue) -> Result<Vec<Server>, RedisError> {
+  let mut role: Vec<RedisValue> = data.convert()?;
+
+  if role.len() == 3 {
+    if role[0].as_str().map(|s| s == "master").unwrap_or(false) {
+      let replicas: Vec<RedisValue> = role[2].take().convert()?;
+
+      Ok(
+        replicas
+          .into_iter()
+          .filter_map(|value| {
+            value
+              .convert::<(String, u16, String)>()
+              .ok()
+              .map(|(host, port, _)| Server::new(host, port))
+          })
+          .collect(),
+      )
+    } else {
+      Ok(Vec::new())
+    }
+  } else {
+    // we're talking to a replica or sentinel node
+    Ok(Vec::new())
+  }
+}
+
+#[cfg(feature = "i-geo")]
+pub fn assert_array_len<T>(data: &[T], len: usize) -> Result<(), RedisError> {
+  if data.len() == len {
+    Ok(())
+  } else {
+    Err(RedisError::new(
+      RedisErrorKind::Parse,
+      format!("Expected {} values.", len),
+    ))
+  }
+}
+
+/// Flatten a nested array of values into one array.
+pub fn flatten_redis_value(value: RedisValue) -> RedisValue {
+  if let RedisValue::Array(values) = value {
+    let mut out = Vec::with_capacity(values.len());
+    for value in values.into_iter() {
+      let flattened = flatten_redis_value(value);
+      if let RedisValue::Array(flattened) = flattened {
+        out.extend(flattened);
+      } else {
+        out.push(flattened);
+      }
+    }
+
+    RedisValue::Array(out)
+  } else {
+    value
+  }
+}
+
+/// Convert a redis value to an array of (value, score) tuples.
+pub fn value_to_zset_result(value: RedisValue) -> Result<Vec<(RedisValue, f64)>, RedisError> {
+  let value = flatten_redis_value(value);
+
+  if let RedisValue::Array(mut values) = value {
+    if values.is_empty() {
+      return Ok(Vec::new());
+    }
+    if values.len() % 2 != 0 {
+      return Err(RedisError::new(
+        RedisErrorKind::Unknown,
+        "Expected an even number of redis values.",
+      ));
+    }
+
+    let mut out = Vec::with_capacity(values.len() / 2);
+    while values.len() >= 2 {
+      let score = match values.pop().unwrap().as_f64() {
+        Some(f) => f,
+        None => {
+          return Err(RedisError::new(
+            RedisErrorKind::Protocol,
+            "Could not convert value to floating point number.",
+          ))
+        },
+      };
+      let value = values.pop().unwrap();
+
+      out.push((value, score));
+    }
+
+    Ok(out)
+  } else {
+    Err(RedisError::new(
+      RedisErrorKind::Unknown,
+      "Expected array of redis values.",
+    ))
+  }
+}
+
+#[cfg(any(feature = "blocking-encoding", feature = "partial-tracing", feature = "full-tracing"))]
+fn i64_size(i: i64) -> usize {
+  if i < 0 {
+    1 + redis_protocol::digits_in_number(-i as usize)
+  } else {
+    redis_protocol::digits_in_number(i as usize)
+  }
+}
+
+#[cfg(any(feature = "blocking-encoding", feature = "partial-tracing", feature = "full-tracing"))]
+pub fn arg_size(value: &RedisValue) -> usize {
+  match value {
+    // use the RESP2 size
+    RedisValue::Boolean(_) => 5,
+    // TODO try digits_in_number(f.trunc()) + 1 + digits_in_number(f.fract())
+    // but don't forget the negative sign byte
+    RedisValue::Double(_) => 10,
+    RedisValue::Null => 3,
+    RedisValue::Integer(ref i) => i64_size(*i),
+    RedisValue::String(ref s) => s.inner().len(),
+    RedisValue::Bytes(ref b) => b.len(),
+    RedisValue::Array(ref arr) => args_size(arr),
+    RedisValue::Map(ref map) => map
+      .inner
+      .iter()
+      .fold(0, |c, (k, v)| c + k.as_bytes().len() + arg_size(v)),
+    RedisValue::Queued => 0,
+  }
+}
+
+#[cfg(any(feature = "blocking-encoding", feature = "partial-tracing", feature = "full-tracing"))]
+pub fn args_size(args: &[RedisValue]) -> usize {
+  args.iter().fold(0, |c, arg| c + arg_size(arg))
+}
+
+fn serialize_hello(command: &RedisCommand, version: &RespVersion) -> Result<Resp3Frame, RedisError> {
+  let args = command.args();
+
+  let (auth, setname) = if args.len() == 3 {
+    // has auth and setname
+    let username = match args[0].as_bytes_str() {
+      Some(username) => username,
+      None => {
+        return Err(RedisError::new(
+          RedisErrorKind::InvalidArgument,
+          "Invalid username. Expected string.",
+        ));
+      },
+    };
+    let password = match args[1].as_bytes_str() {
+      Some(password) => password,
+      None => {
+        return Err(RedisError::new(
+          RedisErrorKind::InvalidArgument,
+          "Invalid password. Expected string.",
+        ));
+      },
+    };
+    let name = match args[2].as_bytes_str() {
+      Some(val) => val,
+      None => {
+        return Err(RedisError::new(
+          RedisErrorKind::InvalidArgument,
+          "Invalid setname value. Expected string.",
+        ));
+      },
+    };
+
+    (Some((username, password)), Some(name))
+  } else if args.len() == 2 {
+    // has auth but no setname
+    let username = match args[0].as_bytes_str() {
+      Some(username) => username,
+      None => {
+        return Err(RedisError::new(
+          RedisErrorKind::InvalidArgument,
+          "Invalid username. Expected string.",
+        ));
+      },
+    };
+    let password = match args[1].as_bytes_str() {
+      Some(password) => password,
+      None => {
+        return Err(RedisError::new(
+          RedisErrorKind::InvalidArgument,
+          "Invalid password. Expected string.",
+        ));
+      },
+    };
+
+    (Some((username, password)), None)
+  } else if args.len() == 1 {
+    // has setname but no auth
+    let name = match args[0].as_bytes_str() {
+      Some(val) => val,
+      None => {
+        return Err(RedisError::new(
+          RedisErrorKind::InvalidArgument,
+          "Invalid setname value. Expected string.",
+        ));
+      },
+    };
+
+    (None, Some(name))
+  } else {
+    (None, None)
+  };
+
+  Ok(Resp3Frame::Hello {
+    version: version.clone(),
+    auth,
+    setname,
+  })
+}
+
+pub fn command_to_resp3_frame(command: &RedisCommand) -> Result<Resp3Frame, RedisError> {
+  let args = command.args();
+
+  match command.kind {
+    RedisCommandKind::_Custom(ref kind) => {
+      let parts: Vec<&str> = kind.cmd.trim().split(' ').collect();
+      let mut bulk_strings = Vec::with_capacity(parts.len() + args.len());
+
+      for part in parts.into_iter() {
+        bulk_strings.push(Resp3Frame::BlobString {
+          data:       part.as_bytes().to_vec().into(),
+          attributes: None,
+        });
+      }
+      for value in args.iter() {
+        bulk_strings.push(value_to_outgoing_resp3_frame(value)?);
+      }
+
+      Ok(Resp3Frame::Array {
+        data:       bulk_strings,
+        attributes: None,
+      })
+    },
+    RedisCommandKind::_HelloAllCluster(ref version) | RedisCommandKind::_Hello(ref version) => {
+      serialize_hello(command, version)
+    },
+    _ => {
+      let mut bulk_strings = Vec::with_capacity(args.len() + 2);
+
+      bulk_strings.push(Resp3Frame::BlobString {
+        data:       command.kind.cmd_str().into_inner(),
+        attributes: None,
+      });
+
+      if let Some(subcommand) = command.kind.subcommand_str() {
+        bulk_strings.push(Resp3Frame::BlobString {
+          data:       subcommand.into_inner(),
+          attributes: None,
+        });
+      }
+      for value in args.iter() {
+        bulk_strings.push(value_to_outgoing_resp3_frame(value)?);
+      }
+
+      Ok(Resp3Frame::Array {
+        data:       bulk_strings,
+        attributes: None,
+      })
+    },
+  }
+}
+
+pub fn command_to_resp2_frame(command: &RedisCommand) -> Result<Resp2Frame, RedisError> {
+  let args = command.args();
+
+  match command.kind {
+    RedisCommandKind::_Custom(ref kind) => {
+      let parts: Vec<&str> = kind.cmd.trim().split(' ').collect();
+      let mut bulk_strings = Vec::with_capacity(parts.len() + args.len());
+
+      for part in parts.into_iter() {
+        bulk_strings.push(Resp2Frame::BulkString(part.as_bytes().to_vec().into()));
+      }
+      for value in args.iter() {
+        bulk_strings.push(value_to_outgoing_resp2_frame(value)?);
+      }
+
+      Ok(Resp2Frame::Array(bulk_strings))
+    },
+    _ => {
+      let mut bulk_strings = Vec::with_capacity(args.len() + 2);
+
+      bulk_strings.push(Resp2Frame::BulkString(command.kind.cmd_str().into_inner()));
+      if let Some(subcommand) = command.kind.subcommand_str() {
+        bulk_strings.push(Resp2Frame::BulkString(subcommand.into_inner()));
+      }
+      for value in args.iter() {
+        bulk_strings.push(value_to_outgoing_resp2_frame(value)?);
+      }
+
+      Ok(Resp2Frame::Array(bulk_strings))
+    },
+  }
+}
+
+/// Serialize the command as a protocol frame.
+pub fn command_to_frame(command: &RedisCommand, is_resp3: bool) -> Result<ProtocolFrame, RedisError> {
+  if is_resp3 || command.kind.is_hello() {
+    command_to_resp3_frame(command).map(|c| c.into())
+  } else {
+    command_to_resp2_frame(command).map(|c| c.into())
+  }
+}
+
+pub fn encode_frame(inner: &RefCount<RedisClientInner>, command: &RedisCommand) -> Result<ProtocolFrame, RedisError> {
+  #[cfg(all(feature = "blocking-encoding", not(feature = "glommio")))]
+  return command.to_frame_blocking(
+    inner.is_resp3(),
+    inner.with_perf_config(|c| c.blocking_encode_threshold),
+  );
+
+  #[cfg(any(
+    not(feature = "blocking-encoding"),
+    all(feature = "blocking-encoding", feature = "glommio")
+  ))]
+  return command.to_frame(inner.is_resp3());
+}
+
+#[cfg(test)]
+mod tests {
+  #![allow(dead_code)]
+  #![allow(unused_imports)]
+  use super::*;
+  use std::{collections::HashMap, time::Duration};
+
+  fn str_to_f(s: &str) -> Resp3Frame {
+    Resp3Frame::SimpleString {
+      data:       s.to_owned().into(),
+      attributes: None,
+    }
+  }
+
+  fn str_to_bs(s: &str) -> Resp3Frame {
+    Resp3Frame::BlobString {
+      data:       s.to_owned().into(),
+      attributes: None,
+    }
+  }
+
+  fn int_to_f(i: i64) -> Resp3Frame {
+    Resp3Frame::Number {
+      data:       i,
+      attributes: None,
+    }
+  }
+
+  #[test]
+  #[cfg(feature = "i-memory")]
+  fn should_parse_memory_stats() {
+    // better from()/into() interfaces for frames coming in the next redis-protocol version...
+    let input = frame_to_results(Resp3Frame::Array {
+      data:       vec![
+        str_to_f("peak.allocated"),
+        int_to_f(934192),
+        str_to_f("total.allocated"),
+        int_to_f(872040),
+        str_to_f("startup.allocated"),
+        int_to_f(809912),
+        str_to_f("replication.backlog"),
+        int_to_f(0),
+        str_to_f("clients.slaves"),
+        int_to_f(0),
+        str_to_f("clients.normal"),
+        int_to_f(20496),
+        str_to_f("aof.buffer"),
+        int_to_f(0),
+        str_to_f("lua.caches"),
+        int_to_f(0),
+        str_to_f("db.0"),
+        Resp3Frame::Array {
+          data:       vec![
+            str_to_f("overhead.hashtable.main"),
+            int_to_f(72),
+            str_to_f("overhead.hashtable.expires"),
+            int_to_f(0),
+          ],
+          attributes: None,
+        },
+        str_to_f("overhead.total"),
+        int_to_f(830480),
+        str_to_f("keys.count"),
+        int_to_f(1),
+        str_to_f("keys.bytes-per-key"),
+        int_to_f(62128),
+        str_to_f("dataset.bytes"),
+        int_to_f(41560),
+        str_to_f("dataset.percentage"),
+        str_to_f("66.894157409667969"),
+        str_to_f("peak.percentage"),
+        str_to_f("93.346977233886719"),
+        str_to_f("allocator.allocated"),
+        int_to_f(1022640),
+        str_to_f("allocator.active"),
+        int_to_f(1241088),
+        str_to_f("allocator.resident"),
+        int_to_f(5332992),
+        str_to_f("allocator-fragmentation.ratio"),
+        str_to_f("1.2136118412017822"),
+        str_to_f("allocator-fragmentation.bytes"),
+        int_to_f(218448),
+        str_to_f("allocator-rss.ratio"),
+        str_to_f("4.2970294952392578"),
+        str_to_f("allocator-rss.bytes"),
+        int_to_f(4091904),
+        str_to_f("rss-overhead.ratio"),
+        str_to_f("2.0268816947937012"),
+        str_to_f("rss-overhead.bytes"),
+        int_to_f(5476352),
+        str_to_f("fragmentation"),
+        str_to_f("13.007383346557617"),
+        str_to_f("fragmentation.bytes"),
+        int_to_f(9978328),
+      ],
+      attributes: None,
+    })
+    .unwrap();
+    let memory_stats: MemoryStats = input.convert().unwrap();
+
+    let expected_db_0 = DatabaseMemoryStats {
+      overhead_hashtable_expires:      0,
+      overhead_hashtable_main:         72,
+      overhead_hashtable_slot_to_keys: 0,
+    };
+    let mut expected_db = HashMap::new();
+    expected_db.insert(0, expected_db_0);
+    let expected = MemoryStats {
+      peak_allocated:                934192,
+      total_allocated:               872040,
+      startup_allocated:             809912,
+      replication_backlog:           0,
+      clients_slaves:                0,
+      clients_normal:                20496,
+      aof_buffer:                    0,
+      lua_caches:                    0,
+      db:                            expected_db,
+      overhead_total:                830480,
+      keys_count:                    1,
+      keys_bytes_per_key:            62128,
+      dataset_bytes:                 41560,
+      dataset_percentage:            66.894_157_409_667_97,
+      peak_percentage:               93.346_977_233_886_72,
+      allocator_allocated:           1022640,
+      allocator_active:              1241088,
+      allocator_resident:            5332992,
+      allocator_fragmentation_ratio: 1.2136118412017822,
+      allocator_fragmentation_bytes: 218448,
+      allocator_rss_ratio:           4.297_029_495_239_258,
+      allocator_rss_bytes:           4091904,
+      rss_overhead_ratio:            2.026_881_694_793_701,
+      rss_overhead_bytes:            5476352,
+      fragmentation:                 13.007383346557617,
+      fragmentation_bytes:           9978328,
+    };
+
+    assert_eq!(memory_stats, expected);
+  }
+
+  #[test]
+  #[cfg(feature = "i-slowlog")]
+  fn should_parse_slowlog_entries_redis_3() {
+    // redis 127.0.0.1:6379> slowlog get 2
+    // 1) 1) (integer) 14
+    // 2) (integer) 1309448221
+    // 3) (integer) 15
+    // 4) 1) "ping"
+    // 2) 1) (integer) 13
+    // 2) (integer) 1309448128
+    // 3) (integer) 30
+    // 4) 1) "slowlog"
+    // 2) "get"
+    // 3) "100"
+
+    let input = frame_to_results(Resp3Frame::Array {
+      data:       vec![
+        Resp3Frame::Array {
+          data:       vec![int_to_f(14), int_to_f(1309448221), int_to_f(15), Resp3Frame::Array {
+            data:       vec![str_to_bs("ping")],
+            attributes: None,
+          }],
+          attributes: None,
+        },
+        Resp3Frame::Array {
+          data:       vec![int_to_f(13), int_to_f(1309448128), int_to_f(30), Resp3Frame::Array {
+            data:       vec![str_to_bs("slowlog"), str_to_bs("get"), str_to_bs("100")],
+            attributes: None,
+          }],
+          attributes: None,
+        },
+      ],
+      attributes: None,
+    })
+    .unwrap();
+    let actual: Vec<SlowlogEntry> = input.convert().unwrap();
+
+    let expected = vec![
+      SlowlogEntry {
+        id:        14,
+        timestamp: 1309448221,
+        duration:  Duration::from_micros(15),
+        args:      vec!["ping".into()],
+        ip:        None,
+        name:      None,
+      },
+      SlowlogEntry {
+        id:        13,
+        timestamp: 1309448128,
+        duration:  Duration::from_micros(30),
+        args:      vec!["slowlog".into(), "get".into(), "100".into()],
+        ip:        None,
+        name:      None,
+      },
+    ];
+
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  #[cfg(feature = "i-slowlog")]
+  fn should_parse_slowlog_entries_redis_4() {
+    // redis 127.0.0.1:6379> slowlog get 2
+    // 1) 1) (integer) 14
+    // 2) (integer) 1309448221
+    // 3) (integer) 15
+    // 4) 1) "ping"
+    // 5) "127.0.0.1:58217"
+    // 6) "worker-123"
+    // 2) 1) (integer) 13
+    // 2) (integer) 1309448128
+    // 3) (integer) 30
+    // 4) 1) "slowlog"
+    // 2) "get"
+    // 3) "100"
+    // 5) "127.0.0.1:58217"
+    // 6) "worker-123"
+
+    let input = frame_to_results(Resp3Frame::Array {
+      data:       vec![
+        Resp3Frame::Array {
+          data:       vec![
+            int_to_f(14),
+            int_to_f(1309448221),
+            int_to_f(15),
+            Resp3Frame::Array {
+              data:       vec![str_to_bs("ping")],
+              attributes: None,
+            },
+            str_to_bs("127.0.0.1:58217"),
+            str_to_bs("worker-123"),
+          ],
+          attributes: None,
+        },
+        Resp3Frame::Array {
+          data:       vec![
+            int_to_f(13),
+            int_to_f(1309448128),
+            int_to_f(30),
+            Resp3Frame::Array {
+              data:       vec![str_to_bs("slowlog"), str_to_bs("get"), str_to_bs("100")],
+              attributes: None,
+            },
+            str_to_bs("127.0.0.1:58217"),
+            str_to_bs("worker-123"),
+          ],
+          attributes: None,
+        },
+      ],
+      attributes: None,
+    })
+    .unwrap();
+    let actual: Vec<SlowlogEntry> = input.convert().unwrap();
+
+    let expected = vec![
+      SlowlogEntry {
+        id:        14,
+        timestamp: 1309448221,
+        duration:  Duration::from_micros(15),
+        args:      vec!["ping".into()],
+        ip:        Some("127.0.0.1:58217".into()),
+        name:      Some("worker-123".into()),
+      },
+      SlowlogEntry {
+        id:        13,
+        timestamp: 1309448128,
+        duration:  Duration::from_micros(30),
+        args:      vec!["slowlog".into(), "get".into(), "100".into()],
+        ip:        Some("127.0.0.1:58217".into()),
+        name:      Some("worker-123".into()),
+      },
+    ];
+
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  #[cfg(feature = "i-cluster")]
+  fn should_parse_cluster_info() {
+    let input: RedisValue = "cluster_state:fail
+cluster_slots_assigned:16384
+cluster_slots_ok:16384
+cluster_slots_pfail:3
+cluster_slots_fail:2
+cluster_known_nodes:6
+cluster_size:3
+cluster_current_epoch:6
+cluster_my_epoch:2
+cluster_stats_messages_sent:1483972
+cluster_stats_messages_received:1483968"
+      .into();
+
+    let expected = ClusterInfo {
+      cluster_state:                   ClusterState::Fail,
+      cluster_slots_assigned:          16384,
+      cluster_slots_ok:                16384,
+      cluster_slots_fail:              2,
+      cluster_slots_pfail:             3,
+      cluster_known_nodes:             6,
+      cluster_size:                    3,
+      cluster_current_epoch:           6,
+      cluster_my_epoch:                2,
+      cluster_stats_messages_sent:     1483972,
+      cluster_stats_messages_received: 1483968,
+    };
+    let actual: ClusterInfo = input.convert().unwrap();
+
+    assert_eq!(actual, expected);
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/router/centralized.rs.html b/doc/src/fred/router/centralized.rs.html new file mode 100644 index 00000000..a0de91f8 --- /dev/null +++ b/doc/src/fred/router/centralized.rs.html @@ -0,0 +1,425 @@ +centralized.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+
use crate::{
+  error::RedisErrorKind,
+  modules::inner::RedisClientInner,
+  prelude::RedisError,
+  protocol::{
+    command::{RedisCommand, RouterResponse},
+    connection,
+    connection::{Counters, RedisWriter, SharedBuffer, SplitStreamKind},
+    responders::{self, ResponseKind},
+    types::Server,
+    utils as protocol_utils,
+  },
+  router::{responses, utils, Connections, Written},
+  runtime::{spawn, JoinHandle, RefCount},
+  types::ServerConfig,
+};
+use redis_protocol::resp3::types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame};
+use std::collections::VecDeque;
+
+pub async fn write(
+  inner: &RefCount<RedisClientInner>,
+  writer: &mut Option<RedisWriter>,
+  command: RedisCommand,
+  force_flush: bool,
+) -> Written {
+  if let Some(writer) = writer.as_mut() {
+    utils::write_command(inner, writer, command, force_flush).await
+  } else {
+    _debug!(inner, "Failed to read connection for {}", command.kind.to_str_debug());
+    Written::Disconnected((
+      None,
+      Some(command),
+      RedisError::new(RedisErrorKind::IO, "Missing connection."),
+    ))
+  }
+}
+
+/// Spawn a task to read response frames from the reader half of the socket.
+#[allow(unused_assignments)]
+pub fn spawn_reader_task(
+  inner: &RefCount<RedisClientInner>,
+  mut reader: SplitStreamKind,
+  server: &Server,
+  buffer: &SharedBuffer,
+  counters: &Counters,
+  is_replica: bool,
+) -> JoinHandle<Result<(), RedisError>> {
+  let (inner, server) = (inner.clone(), server.clone());
+  let (buffer, counters) = (buffer.clone(), counters.clone());
+
+  spawn(async move {
+    let mut last_error = None;
+
+    loop {
+      let frame = match utils::next_frame(&inner, &mut reader, &server, &buffer).await {
+        Ok(Some(frame)) => frame.into_resp3(),
+        Ok(None) => {
+          last_error = None;
+          break;
+        },
+        Err(error) => {
+          last_error = Some(error);
+          break;
+        },
+      };
+
+      if let Some(error) = responses::check_special_errors(&inner, &frame) {
+        last_error = Some(error);
+        break;
+      }
+      if let Some(frame) = responses::check_pubsub_message(&inner, &server, frame) {
+        if let Err(e) = process_response_frame(&inner, &server, &buffer, &counters, frame).await {
+          _debug!(inner, "Error processing response frame from {}: {:?}", server, e);
+          last_error = Some(e);
+          break;
+        }
+      }
+    }
+
+    // at this point the order of the shared buffer no longer matters since we can't know which commands actually made
+    // it to the server, just that the connection closed. the shared buffer will be drained when the writer notices
+    // that this task finished, but here we need to first filter out any commands that have exceeded their max write
+    // attempts.
+    utils::check_blocked_router(&inner, &buffer, &last_error);
+    utils::check_final_write_attempt(&inner, &buffer, &last_error);
+    if is_replica {
+      responses::broadcast_replica_error(&inner, &server, last_error);
+    } else {
+      responses::broadcast_reader_error(&inner, &server, last_error);
+    }
+
+    _debug!(inner, "Ending reader task from {}", server);
+    Ok(())
+  })
+}
+
+/// Process the response frame in the context of the last command.
+///
+/// Errors returned here will be logged, but will not close the socket or initiate a reconnect.
+pub async fn process_response_frame(
+  inner: &RefCount<RedisClientInner>,
+  server: &Server,
+  buffer: &SharedBuffer,
+  counters: &Counters,
+  frame: Resp3Frame,
+) -> Result<(), RedisError> {
+  _trace!(inner, "Parsing response frame from {}", server);
+  let mut command = match buffer.pop() {
+    Some(command) => command,
+    None => {
+      _debug!(
+        inner,
+        "Missing last command from {}. Dropping {:?}.",
+        server,
+        frame.kind()
+      );
+      return Ok(());
+    },
+  };
+  _trace!(
+    inner,
+    "Checking response to {} ({})",
+    command.kind.to_str_debug(),
+    command.debug_id()
+  );
+  counters.decr_in_flight();
+  if command.blocks_connection() {
+    buffer.set_unblocked();
+  }
+  responses::check_and_set_unblocked_flag(inner, &command).await;
+
+  if command.transaction_id.is_some() {
+    if let Some(error) = protocol_utils::frame_to_error(&frame) {
+      #[allow(unused_mut)]
+      if let Some(mut tx) = command.take_router_tx() {
+        let _ = tx.send(RouterResponse::TransactionError((error, command)));
+      }
+      return Ok(());
+    } else if command.kind.ends_transaction() {
+      command.respond_to_router(inner, RouterResponse::TransactionResult(frame));
+      return Ok(());
+    } else {
+      command.respond_to_router(inner, RouterResponse::Continue);
+      return Ok(());
+    }
+  }
+
+  // TODO clean this up
+  _trace!(inner, "Handling centralized response kind: {:?}", command.response);
+  match command.take_response() {
+    ResponseKind::Skip | ResponseKind::Respond(None) => {
+      command.respond_to_router(inner, RouterResponse::Continue);
+      Ok(())
+    },
+    ResponseKind::Respond(Some(tx)) => responders::respond_to_caller(inner, server, command, tx, frame),
+    ResponseKind::Buffer {
+      received,
+      expected,
+      frames,
+      tx,
+      index,
+      error_early,
+    } => responders::respond_buffer(
+      inner,
+      server,
+      command,
+      received,
+      expected,
+      error_early,
+      frames,
+      index,
+      tx,
+      frame,
+    ),
+    ResponseKind::KeyScan(scanner) => responders::respond_key_scan(inner, server, command, scanner, frame),
+    ResponseKind::ValueScan(scanner) => responders::respond_value_scan(inner, server, command, scanner, frame),
+  }
+}
+
+/// Initialize fresh connections to the server, dropping any old connections and saving in-flight commands on
+/// `buffer`.
+pub async fn initialize_connection(
+  inner: &RefCount<RedisClientInner>,
+  connections: &mut Connections,
+  buffer: &mut VecDeque<RedisCommand>,
+) -> Result<(), RedisError> {
+  _debug!(inner, "Initializing centralized connection.");
+  let commands = connections.disconnect_all(inner).await;
+  buffer.extend(commands);
+
+  match connections {
+    Connections::Centralized { writer, .. } => {
+      let server = match inner.config.server {
+        ServerConfig::Centralized { ref server } => server.clone(),
+        #[cfg(feature = "unix-sockets")]
+        ServerConfig::Unix { ref path } => path.as_path().into(),
+        _ => return Err(RedisError::new(RedisErrorKind::Config, "Expected centralized config.")),
+      };
+      let mut transport = connection::create(inner, &server, None).await?;
+      transport.setup(inner, None).await?;
+      let (server, _writer) = connection::split(inner, transport, false, spawn_reader_task)?;
+      inner.notifications.broadcast_reconnect(server);
+
+      *writer = Some(_writer);
+      Ok(())
+    },
+    _ => Err(RedisError::new(
+      RedisErrorKind::Config,
+      "Expected centralized connection.",
+    )),
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/router/clustered.rs.html b/doc/src/fred/router/clustered.rs.html new file mode 100644 index 00000000..15f6ba8f --- /dev/null +++ b/doc/src/fred/router/clustered.rs.html @@ -0,0 +1,1495 @@ +clustered.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  interfaces,
+  modules::inner::RedisClientInner,
+  protocol::{
+    command::{ClusterErrorKind, RedisCommand, RedisCommandKind, RouterCommand, RouterResponse},
+    connection::{self, Counters, RedisTransport, RedisWriter, SharedBuffer, SplitStreamKind},
+    responders,
+    responders::ResponseKind,
+    types::{ClusterRouting, Server, SlotRange},
+    utils as protocol_utils,
+  },
+  router::{responses, types::ClusterChange, utils, Connections, Written},
+  runtime::{spawn, JoinHandle, Mutex, RefCount},
+  types::{ClusterDiscoveryPolicy, ClusterStateChange},
+  utils as client_utils,
+};
+use futures::future::try_join_all;
+use redis_protocol::resp3::types::{BytesFrame as Resp3Frame, FrameKind, Resp3Frame as _Resp3Frame};
+use std::{
+  collections::{BTreeSet, HashMap, VecDeque},
+  iter::repeat,
+};
+
+/// Find the cluster node that should receive the command.
+pub fn route_command<'a>(
+  inner: &RefCount<RedisClientInner>,
+  state: &'a ClusterRouting,
+  command: &RedisCommand,
+) -> Option<&'a Server> {
+  if let Some(ref server) = command.cluster_node {
+    // this `_server` has a lifetime tied to `command`, so we switch `server` to refer to the record in `state` while
+    // we check whether that node exists in the cluster. we return None here if the command specifies a server that
+    // does not exist in the cluster.
+    _trace!(inner, "Routing with custom cluster node: {}", server);
+    state.slots().iter().find_map(|slot| {
+      if slot.primary == *server {
+        Some(&slot.primary)
+      } else {
+        None
+      }
+    })
+  } else {
+    command
+      .cluster_hash()
+      .and_then(|slot| state.get_server(slot))
+      .or_else(|| {
+        // for some commands we know they can go to any node, but for others it may depend on the arguments provided.
+        if command.args().is_empty() || command.kind.use_random_cluster_node() {
+          let node = state.random_node();
+          _trace!(
+            inner,
+            "Using random cluster node `{:?}` for {}",
+            node,
+            command.kind.to_str_debug()
+          );
+          node
+        } else {
+          None
+        }
+      })
+  }
+}
+
+/// Write a command to the cluster according to the [cluster hashing](https://redis.io/docs/reference/cluster-spec/) interface.
+pub async fn write(
+  inner: &RefCount<RedisClientInner>,
+  writers: &mut HashMap<Server, RedisWriter>,
+  state: &ClusterRouting,
+  command: RedisCommand,
+  force_flush: bool,
+) -> Written {
+  let has_custom_server = command.cluster_node.is_some();
+  let server = match route_command(inner, state, &command) {
+    Some(server) => server,
+    None => {
+      return if has_custom_server {
+        _debug!(
+          inner,
+          "Respond to caller with error from missing cluster node override ({:?})",
+          command.cluster_node
+        );
+        command.finish(
+          inner,
+          Err(RedisError::new(
+            RedisErrorKind::Cluster,
+            "Missing cluster node override.",
+          )),
+        );
+
+        Written::Ignore
+      } else {
+        // these errors usually mean the cluster is partially down or misconfigured
+        _warn!(
+          inner,
+          "Possible cluster misconfiguration. Missing hash slot owner for {:?}.",
+          command.cluster_hash()
+        );
+        Written::NotFound(command)
+      };
+    },
+  };
+
+  if let Some(writer) = writers.get_mut(server) {
+    _debug!(inner, "Writing command `{}` to {}", command.kind.to_str_debug(), server);
+    utils::write_command(inner, writer, command, force_flush).await
+  } else {
+    // a reconnect message should already be queued from the reader task
+    _debug!(
+      inner,
+      "Failed to read connection {} for {}",
+      server,
+      command.kind.to_str_debug()
+    );
+
+    Written::Disconnected((
+      Some(server.clone()),
+      Some(command),
+      RedisError::new(RedisErrorKind::IO, "Missing connection."),
+    ))
+  }
+}
+
+/// Send a command to all cluster nodes.
+///
+/// Note: if any of the commands fail to send the entire command is interrupted.
+// There's probably a much cleaner way to express this. Most of the complexity here comes from the need to
+// pre-allocate and assign response locations in the buffer ahead of time. This is done to avoid any race conditions.
+pub async fn send_all_cluster_command(
+  inner: &RefCount<RedisClientInner>,
+  writers: &mut HashMap<Server, RedisWriter>,
+  mut command: RedisCommand,
+) -> Result<(), RedisError> {
+  let num_nodes = writers.len();
+  if let ResponseKind::Buffer {
+    ref mut frames,
+    ref mut expected,
+    ..
+  } = command.response
+  {
+    *expected = num_nodes;
+
+    _trace!(
+      inner,
+      "Allocating {} null responses in buffer for {}.",
+      num_nodes,
+      command.kind.to_str_debug(),
+    );
+    let mut guard = frames.lock();
+    // pre-allocate responses
+    *guard = repeat(Resp3Frame::Null).take(num_nodes).collect();
+  }
+  let mut responder = match command.response.duplicate() {
+    Some(resp) => resp,
+    None => {
+      return Err(RedisError::new(
+        RedisErrorKind::Config,
+        "Invalid command response type.",
+      ))
+    },
+  };
+
+  for (idx, (server, writer)) in writers.iter_mut().enumerate() {
+    _debug!(
+      inner,
+      "Sending all cluster command to {} with index {}, ID: {}",
+      server,
+      idx,
+      command.debug_id()
+    );
+    let mut cmd_responder = responder.duplicate().unwrap_or(ResponseKind::Skip);
+    cmd_responder.set_expected_index(idx);
+    let mut cmd = command.duplicate(cmd_responder);
+    cmd.skip_backpressure = true;
+
+    if let Written::Disconnected((server, _, err)) = utils::write_command(inner, writer, cmd, true).await {
+      _debug!(
+        inner,
+        "Exit all nodes command early ({}/{}: {:?}) from error: {:?}",
+        idx + 1,
+        num_nodes,
+        server,
+        err
+      );
+      responder.respond_with_error(err);
+      break;
+    }
+  }
+
+  Ok(())
+}
+
+pub fn parse_cluster_changes(
+  cluster_state: &ClusterRouting,
+  writers: &HashMap<Server, RedisWriter>,
+) -> ClusterChange {
+  let mut old_servers = BTreeSet::new();
+  let mut new_servers = BTreeSet::new();
+  for server in cluster_state.unique_primary_nodes().into_iter() {
+    new_servers.insert(server);
+  }
+  for server in writers.keys() {
+    old_servers.insert(server.clone());
+  }
+  let add = new_servers.difference(&old_servers).cloned().collect();
+  let remove = old_servers.difference(&new_servers).cloned().collect();
+
+  ClusterChange { add, remove }
+}
+
+pub fn broadcast_cluster_change(inner: &RefCount<RedisClientInner>, changes: &ClusterChange) {
+  let mut added: Vec<ClusterStateChange> = changes
+    .add
+    .iter()
+    .map(|server| ClusterStateChange::Add(server.clone()))
+    .collect();
+  let removed: Vec<ClusterStateChange> = changes
+    .remove
+    .iter()
+    .map(|server| ClusterStateChange::Remove(server.clone()))
+    .collect();
+
+  let changes = if added.is_empty() && removed.is_empty() {
+    vec![ClusterStateChange::Rebalance]
+  } else {
+    added.extend(removed);
+    added
+  };
+
+  inner.notifications.broadcast_cluster_change(changes);
+}
+
+/// Spawn a task to read response frames from the reader half of the socket.
+#[allow(unused_assignments)]
+pub fn spawn_reader_task(
+  inner: &RefCount<RedisClientInner>,
+  mut reader: SplitStreamKind,
+  server: &Server,
+  buffer: &SharedBuffer,
+  counters: &Counters,
+  is_replica: bool,
+) -> JoinHandle<Result<(), RedisError>> {
+  let (inner, server) = (inner.clone(), server.clone());
+  let (buffer, counters) = (buffer.clone(), counters.clone());
+
+  // TODO support spawn_into() with glommio
+  spawn(async move {
+    let mut last_error = None;
+
+    loop {
+      let frame = match utils::next_frame(&inner, &mut reader, &server, &buffer).await {
+        Ok(Some(frame)) => frame.into_resp3(),
+        Ok(None) => {
+          last_error = None;
+          break;
+        },
+        Err(e) => {
+          last_error = Some(e);
+          break;
+        },
+      };
+
+      if let Some(error) = responses::check_special_errors(&inner, &frame) {
+        last_error = Some(error);
+        break;
+      }
+      if let Some(frame) = responses::check_pubsub_message(&inner, &server, frame) {
+        if let Err(e) = process_response_frame(&inner, &server, &buffer, &counters, frame).await {
+          _debug!(
+            inner,
+            "Error processing clustered response frame from {}: {:?}",
+            server,
+            e
+          );
+          last_error = Some(e);
+          break;
+        }
+      }
+    }
+
+    // see the centralized variant of this function for more information.
+    utils::check_blocked_router(&inner, &buffer, &last_error);
+    utils::check_final_write_attempt(&inner, &buffer, &last_error);
+    if is_replica {
+      responses::broadcast_replica_error(&inner, &server, last_error);
+    } else {
+      responses::broadcast_reader_error(&inner, &server, last_error);
+    }
+
+    _debug!(inner, "Ending reader task from {}", server);
+    Ok(())
+  })
+}
+
+/// Send a MOVED or ASK command to the router, using the router channel if possible and falling back on the
+/// command queue if appropriate.
+// Cluster errors within a transaction can only be handled via the blocking router channel.
+fn process_cluster_error(
+  inner: &RefCount<RedisClientInner>,
+  server: &Server,
+  mut command: RedisCommand,
+  frame: Resp3Frame,
+) {
+  // commands are not redirected to replica nodes
+  command.use_replica = false;
+
+  let (kind, slot, server_str) = match frame.as_str() {
+    Some(data) => match protocol_utils::parse_cluster_error(data) {
+      Ok(result) => result,
+      Err(e) => {
+        command.respond_to_router(inner, RouterResponse::Continue);
+        command.respond_to_caller(Err(e));
+        return;
+      },
+    },
+    None => {
+      command.respond_to_router(inner, RouterResponse::Continue);
+      command.respond_to_caller(Err(RedisError::new(RedisErrorKind::Protocol, "Invalid cluster error.")));
+      return;
+    },
+  };
+  let server = match Server::from_parts(&server_str, &server.host) {
+    Some(server) => server,
+    None => {
+      _warn!(inner, "Invalid server field in cluster error: {}", server_str);
+      command.respond_to_router(inner, RouterResponse::Continue);
+      command.respond_to_caller(Err(RedisError::new(
+        RedisErrorKind::Cluster,
+        "Invalid cluster redirection error.",
+      )));
+      return;
+    },
+  };
+
+  #[allow(unused_mut)]
+  if let Some(mut tx) = command.take_router_tx() {
+    let response = match kind {
+      ClusterErrorKind::Ask => RouterResponse::Ask((slot, server, command)),
+      ClusterErrorKind::Moved => RouterResponse::Moved((slot, server, command)),
+    };
+
+    _debug!(inner, "Sending cluster error to router channel.");
+    if let Err(response) = tx.send(response) {
+      #[cfg(feature = "glommio")]
+      let response = response.into_inner();
+
+      // if it could not be sent on the router tx then send it on the command channel
+      let command = match response {
+        RouterResponse::Ask((slot, server, command)) => {
+          if command.transaction_id.is_some() {
+            _debug!(
+              inner,
+              "Failed sending ASK cluster error to router in transaction: {}",
+              command.kind.to_str_debug()
+            );
+            // do not send the command to the command queue
+            return;
+          } else {
+            RouterCommand::Ask { slot, server, command }
+          }
+        },
+        RouterResponse::Moved((slot, server, command)) => {
+          if command.transaction_id.is_some() {
+            _debug!(
+              inner,
+              "Failed sending MOVED cluster error to router in transaction: {}",
+              command.kind.to_str_debug()
+            );
+            // do not send the command to the command queue
+            return;
+          } else {
+            RouterCommand::Moved { slot, server, command }
+          }
+        },
+        _ => {
+          _error!(inner, "Invalid cluster error router response type.");
+          return;
+        },
+      };
+
+      _debug!(inner, "Sending cluster error to command queue.");
+      if let Err(e) = interfaces::send_to_router(inner, command) {
+        _warn!(inner, "Cannot send MOVED to router channel: {:?}", e);
+      }
+    }
+  } else {
+    let command = match kind {
+      ClusterErrorKind::Ask => RouterCommand::Ask { slot, server, command },
+      ClusterErrorKind::Moved => RouterCommand::Moved { slot, server, command },
+    };
+
+    _debug!(inner, "Sending cluster error to command queue.");
+    if let Err(e) = interfaces::send_to_router(inner, command) {
+      _warn!(inner, "Cannot send ASKED to router channel: {:?}", e);
+    }
+  }
+}
+
+/// Process the response frame in the context of the last command.
+///
+/// Errors returned here will be logged, but will not close the socket or initiate a reconnect.
+pub async fn process_response_frame(
+  inner: &RefCount<RedisClientInner>,
+  server: &Server,
+  buffer: &SharedBuffer,
+  counters: &Counters,
+  frame: Resp3Frame,
+) -> Result<(), RedisError> {
+  _trace!(inner, "Parsing response frame from {}", server);
+  let mut command = match buffer.pop() {
+    Some(command) => command,
+    None => {
+      _debug!(
+        inner,
+        "Missing last command from {}. Dropping {:?}.",
+        server,
+        frame.kind()
+      );
+      return Ok(());
+    },
+  };
+  _trace!(
+    inner,
+    "Checking response to {} ({})",
+    command.kind.to_str_debug(),
+    command.debug_id()
+  );
+  counters.decr_in_flight();
+  if command.blocks_connection() {
+    buffer.set_unblocked();
+  }
+  responses::check_and_set_unblocked_flag(inner, &command).await;
+
+  if frame.is_redirection() {
+    _debug!(
+      inner,
+      "Recv MOVED or ASK error for `{}` from {}: {:?}",
+      command.kind.to_str_debug(),
+      server,
+      frame.as_str()
+    );
+    process_cluster_error(inner, server, command, frame);
+    return Ok(());
+  }
+
+  if command.transaction_id.is_some() {
+    if let Some(error) = protocol_utils::frame_to_error(&frame) {
+      #[allow(unused_mut)]
+      if let Some(mut tx) = command.take_router_tx() {
+        let _ = tx.send(RouterResponse::TransactionError((error, command)));
+      }
+      return Ok(());
+    } else if command.kind.ends_transaction() {
+      command.respond_to_router(inner, RouterResponse::TransactionResult(frame));
+      return Ok(());
+    } else {
+      command.respond_to_router(inner, RouterResponse::Continue);
+      return Ok(());
+    }
+  }
+
+  _trace!(inner, "Handling clustered response kind: {:?}", command.response);
+  match command.take_response() {
+    ResponseKind::Skip | ResponseKind::Respond(None) => {
+      command.respond_to_router(inner, RouterResponse::Continue);
+      Ok(())
+    },
+    ResponseKind::Respond(Some(tx)) => responders::respond_to_caller(inner, server, command, tx, frame),
+    ResponseKind::Buffer {
+      received,
+      expected,
+      frames,
+      tx,
+      index,
+      error_early,
+    } => responders::respond_buffer(
+      inner,
+      server,
+      command,
+      received,
+      expected,
+      error_early,
+      frames,
+      index,
+      tx,
+      frame,
+    ),
+    ResponseKind::KeyScan(scanner) => responders::respond_key_scan(inner, server, command, scanner, frame),
+    ResponseKind::ValueScan(scanner) => responders::respond_value_scan(inner, server, command, scanner, frame),
+  }
+}
+
+/// Try connecting to any node in the provided `RedisConfig` or `old_servers`.
+pub async fn connect_any(
+  inner: &RefCount<RedisClientInner>,
+  old_cache: Option<&[SlotRange]>,
+) -> Result<RedisTransport, RedisError> {
+  let mut all_servers: BTreeSet<Server> = if let Some(old_cache) = old_cache {
+    old_cache.iter().map(|slot_range| slot_range.primary.clone()).collect()
+  } else {
+    BTreeSet::new()
+  };
+  all_servers.extend(inner.config.server.hosts());
+  _debug!(inner, "Attempting clustered connections to any of {:?}", all_servers);
+
+  let num_servers = all_servers.len();
+  let mut last_error = None;
+  for (idx, server) in all_servers.into_iter().enumerate() {
+    let mut connection = match connection::create(inner, &server, None).await {
+      Ok(connection) => connection,
+      Err(e) => {
+        last_error = Some(e);
+        continue;
+      },
+    };
+
+    if let Err(e) = connection.setup(inner, None).await {
+      last_error = Some(e);
+      continue;
+    }
+    _debug!(
+      inner,
+      "Connected to {} ({}/{})",
+      connection.server,
+      idx + 1,
+      num_servers
+    );
+    return Ok(connection);
+  }
+
+  Err(last_error.unwrap_or(RedisError::new(
+    RedisErrorKind::Cluster,
+    "Failed connecting to any cluster node.",
+  )))
+}
+
+/// Run the `CLUSTER SLOTS` command on the backchannel, creating a new connection if needed.
+///
+/// This function will attempt to use the existing backchannel connection, if found. Failing that it will
+/// try to connect to any of the cluster nodes as identified in the `RedisConfig` or previous cached state.
+///
+/// If this returns an error then all known cluster nodes are unreachable.
+pub async fn cluster_slots_backchannel(
+  inner: &RefCount<RedisClientInner>,
+  cache: Option<&ClusterRouting>,
+  force_disconnect: bool,
+) -> Result<ClusterRouting, RedisError> {
+  if force_disconnect {
+    inner.backchannel.write().await.check_and_disconnect(inner, None).await;
+  }
+
+  let (response, host) = {
+    let command: RedisCommand = RedisCommandKind::ClusterSlots.into();
+
+    let backchannel_result = {
+      // try to use the existing backchannel connection first
+      let mut backchannel = inner.backchannel.write().await;
+      if let Some(ref mut transport) = backchannel.transport {
+        let default_host = transport.default_host.clone();
+
+        _trace!(inner, "Sending backchannel CLUSTER SLOTS to {}", transport.server);
+        client_utils::timeout(
+          transport.request_response(command, inner.is_resp3()),
+          inner.internal_command_timeout(),
+        )
+        .await
+        .ok()
+        .map(|frame| (frame, default_host))
+      } else {
+        None
+      }
+    };
+    if backchannel_result.is_none() {
+      inner.backchannel.write().await.check_and_disconnect(inner, None).await;
+    }
+
+    // failing the backchannel, try to connect to any of the user-provided hosts or the last known cluster nodes
+    let old_cache = if let Some(policy) = inner.cluster_discovery_policy() {
+      match policy {
+        ClusterDiscoveryPolicy::ConfigEndpoint => None,
+        ClusterDiscoveryPolicy::UseCache => cache.map(|cache| cache.slots()),
+      }
+    } else {
+      cache.map(|cache| cache.slots())
+    };
+
+    let command: RedisCommand = RedisCommandKind::ClusterSlots.into();
+    let (frame, host) = if let Some((frame, host)) = backchannel_result {
+      let kind = frame.kind();
+
+      if matches!(kind, FrameKind::SimpleError | FrameKind::BlobError) {
+        // try connecting to any of the nodes, then try again
+        let mut transport = connect_any(inner, old_cache).await?;
+        let frame = client_utils::timeout(
+          transport.request_response(command, inner.is_resp3()),
+          inner.internal_command_timeout(),
+        )
+        .await?;
+        let host = transport.default_host.clone();
+        inner.update_backchannel(transport).await;
+
+        (frame, host)
+      } else {
+        // use the response from the backchannel command
+        (frame, host)
+      }
+    } else {
+      // try connecting to any of the nodes, then try again
+      let mut transport = connect_any(inner, old_cache).await?;
+      let frame = client_utils::timeout(
+        transport.request_response(command, inner.is_resp3()),
+        inner.internal_command_timeout(),
+      )
+      .await?;
+      let host = transport.default_host.clone();
+      inner.update_backchannel(transport).await;
+
+      (frame, host)
+    };
+
+    (protocol_utils::frame_to_results(frame)?, host)
+  };
+  _trace!(inner, "Recv CLUSTER SLOTS response: {:?}", response);
+  if response.is_null() {
+    inner.backchannel.write().await.check_and_disconnect(inner, None).await;
+    return Err(RedisError::new(
+      RedisErrorKind::Protocol,
+      "Invalid or missing CLUSTER SLOTS response.",
+    ));
+  }
+
+  let mut new_cache = ClusterRouting::new();
+  _debug!(inner, "Rebuilding cluster state from host: {}", host);
+  new_cache.rebuild(inner, response, &host)?;
+  Ok(new_cache)
+}
+
+/// Check each connection and remove it from the writer map if it's not [working](RedisWriter::is_working).
+pub async fn drop_broken_connections(writers: &mut HashMap<Server, RedisWriter>) -> VecDeque<RedisCommand> {
+  let mut new_writers = HashMap::with_capacity(writers.len());
+  let mut buffer = VecDeque::new();
+
+  for (server, writer) in writers.drain() {
+    if writer.is_working() {
+      new_writers.insert(server, writer);
+    } else {
+      buffer.extend(writer.graceful_close().await);
+    }
+  }
+
+  *writers = new_writers;
+  buffer
+}
+
+/// Run `CLUSTER SLOTS`, update the cached routing table, and modify the connection map.
+pub async fn sync(
+  inner: &RefCount<RedisClientInner>,
+  connections: &mut Connections,
+  buffer: &mut VecDeque<RedisCommand>,
+) -> Result<(), RedisError> {
+  _debug!(inner, "Synchronizing cluster state.");
+
+  if let Connections::Clustered { cache, writers } = connections {
+    // force disconnect after a connection unexpectedly closes or goes unresponsive
+    let force_disconnect = writers.is_empty()
+      || writers
+        .values()
+        .find_map(|t| if t.is_working() { None } else { Some(true) })
+        .unwrap_or(false);
+
+    let state = cluster_slots_backchannel(inner, Some(&*cache), force_disconnect).await?;
+    _debug!(inner, "Cluster routing state: {:?}", state.pretty());
+    // update the cached routing table
+    inner
+      .server_state
+      .write()
+      .kind
+      .update_cluster_state(Some(state.clone()));
+    *cache = state.clone();
+
+    buffer.extend(drop_broken_connections(writers).await);
+    // detect changes to the cluster topology
+    let changes = parse_cluster_changes(&state, writers);
+    _debug!(inner, "Changing cluster connections: {:?}", changes);
+    broadcast_cluster_change(inner, &changes);
+
+    // drop connections that are no longer used
+    for removed_server in changes.remove.into_iter() {
+      _debug!(inner, "Disconnecting from cluster node {}", removed_server);
+      let writer = match writers.remove(&removed_server) {
+        Some(writer) => writer,
+        None => continue,
+      };
+
+      let commands = writer.graceful_close().await;
+      buffer.extend(commands);
+    }
+
+    let mut connections_ft = Vec::with_capacity(changes.add.len());
+    let new_writers = RefCount::new(Mutex::new(HashMap::with_capacity(changes.add.len())));
+    // connect to each of the new nodes
+    for server in changes.add.into_iter() {
+      let _inner = inner.clone();
+      let _new_writers = new_writers.clone();
+      connections_ft.push(async move {
+        _debug!(inner, "Connecting to cluster node {}", server);
+        let mut transport = connection::create(&_inner, &server, None).await?;
+        transport.setup(&_inner, None).await?;
+
+        let (server, writer) = connection::split(&_inner, transport, false, spawn_reader_task)?;
+        inner.notifications.broadcast_reconnect(server.clone());
+        _new_writers.lock().insert(server, writer);
+        Ok::<_, RedisError>(())
+      });
+    }
+
+    let _ = try_join_all(connections_ft).await?;
+    for (server, writer) in new_writers.lock().drain() {
+      writers.insert(server, writer);
+    }
+
+    _debug!(inner, "Finish synchronizing cluster connections.");
+  } else {
+    return Err(RedisError::new(
+      RedisErrorKind::Config,
+      "Expected clustered connections.",
+    ));
+  }
+
+  if let Some(version) = connections.server_version() {
+    inner.server_state.write().kind.set_server_version(version);
+  }
+  Ok(())
+}
+
+/// Initialize fresh connections to the server, dropping any old connections and saving in-flight commands on
+/// `buffer`.
+pub async fn initialize_connections(
+  inner: &RefCount<RedisClientInner>,
+  connections: &mut Connections,
+  buffer: &mut VecDeque<RedisCommand>,
+) -> Result<(), RedisError> {
+  let commands = connections.disconnect_all(inner).await;
+  _trace!(inner, "Adding {} commands to retry buffer.", commands.len());
+  buffer.extend(commands);
+  sync(inner, connections, buffer).await
+}
+
\ No newline at end of file diff --git a/doc/src/fred/router/commands.rs.html b/doc/src/fred/router/commands.rs.html new file mode 100644 index 00000000..74ba4375 --- /dev/null +++ b/doc/src/fred/router/commands.rs.html @@ -0,0 +1,1455 @@ +commands.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  modules::inner::{CommandReceiver, RedisClientInner},
+  protocol::command::{
+    RedisCommand,
+    RedisCommandKind,
+    ResponseSender,
+    RouterCommand,
+    RouterReceiver,
+    RouterResponse,
+  },
+  router::{utils, Backpressure, Router, Written},
+  runtime::{OneshotSender, RefCount},
+  types::{Blocking, ClientState, ClientUnblockFlag, ClusterHash, Server},
+  utils as client_utils,
+};
+use redis_protocol::resp3::types::BytesFrame as Resp3Frame;
+
+#[cfg(feature = "transactions")]
+use crate::router::transactions;
+#[cfg(feature = "full-tracing")]
+use tracing_futures::Instrument;
+
+/// Wait for the response from the reader task, handling cluster redirections if needed.
+///
+/// The command is returned if it failed to write but could be immediately retried.
+///
+/// Errors from this function should end the connection task.
+async fn handle_router_response(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  rx: Option<RouterReceiver>,
+) -> Result<Option<RedisCommand>, RedisError> {
+  if let Some(rx) = rx {
+    _debug!(inner, "Waiting on router channel.");
+    let response = match rx.await {
+      Ok(response) => response,
+      Err(e) => {
+        _warn!(inner, "Dropped router response channel with error: {:?}", e);
+        return Ok(None);
+      },
+    };
+
+    _debug!(inner, "Recv router response.");
+    match response {
+      RouterResponse::Continue => Ok(None),
+      RouterResponse::Ask((slot, server, mut command)) => {
+        if let Err(e) = command.decr_check_redirections() {
+          command.respond_to_caller(Err(e));
+          Ok(None)
+        } else {
+          utils::send_asking_with_policy(inner, router, &server, slot).await?;
+          command.hasher = ClusterHash::Custom(slot);
+          command.use_replica = false;
+          command.attempts_remaining += 1;
+          Ok(Some(command))
+        }
+      },
+      RouterResponse::Moved((slot, server, mut command)) => {
+        // check if slot belongs to server, if not then run sync cluster
+        if !router.cluster_node_owns_slot(slot, &server) {
+          utils::sync_cluster_with_policy(inner, router).await?;
+        }
+
+        if let Err(e) = command.decr_check_redirections() {
+          command.finish(inner, Err(e));
+          Ok(None)
+        } else {
+          command.hasher = ClusterHash::Custom(slot);
+          command.use_replica = false;
+          command.attempts_remaining += 1;
+          Ok(Some(command))
+        }
+      },
+      RouterResponse::ConnectionClosed((error, command)) => {
+        let command = if command.should_finish_with_error(inner) {
+          command.finish(inner, Err(error.clone()));
+          None
+        } else {
+          Some(command)
+        };
+
+        utils::reconnect_with_policy(inner, router).await?;
+        Ok(command)
+      },
+      RouterResponse::TransactionError(_) | RouterResponse::TransactionResult(_) => {
+        _error!(inner, "Unexpected transaction response. This is a bug.");
+        Err(RedisError::new(
+          RedisErrorKind::Unknown,
+          "Invalid transaction response.",
+        ))
+      },
+    }
+  } else {
+    Ok(None)
+  }
+}
+
+/// Continuously write the command until it is sent, queued to try later, or fails with a fatal error.
+async fn write_with_backpressure(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  command: RedisCommand,
+  force_pipeline: bool,
+) -> Result<(), RedisError> {
+  Box::pin(async {
+    _trace!(inner, "Writing command: {:?}", command);
+
+    let mut _command: Option<RedisCommand> = Some(command);
+    let mut _backpressure: Option<Backpressure> = None;
+    loop {
+      let mut command = match _command.take() {
+        Some(command) => command,
+        None => return Err(RedisError::new(RedisErrorKind::Unknown, "Missing command.")),
+      };
+      if let Err(e) = command.decr_check_attempted() {
+        command.finish(inner, Err(e));
+        break;
+      }
+
+      // apply backpressure first if needed. as a part of that check we may decide to block on the next command.
+      let router_rx = match _backpressure {
+        Some(backpressure) => match backpressure.wait(inner, &mut command).await {
+          Ok(Some(rx)) => Some(rx),
+          Ok(None) => {
+            if command.should_auto_pipeline(inner, force_pipeline) {
+              None
+            } else {
+              Some(command.create_router_channel())
+            }
+          },
+          Err(e) => {
+            command.respond_to_caller(Err(e));
+            return Ok(());
+          },
+        },
+        None => {
+          if command.should_auto_pipeline(inner, force_pipeline) {
+            None
+          } else {
+            Some(command.create_router_channel())
+          }
+        },
+      };
+      let closes_connection = command.kind.closes_connection();
+      let is_blocking = command.blocks_connection();
+      let use_replica = command.use_replica;
+
+      let result = if use_replica {
+        router.write_replica(command, false).await
+      } else {
+        router.write(command, false).await
+      };
+
+      match result {
+        Written::Backpressure((mut command, backpressure)) => {
+          _debug!(inner, "Recv backpressure again for {}.", command.kind.to_str_debug());
+          // backpressure doesn't count as a write attempt
+          command.attempts_remaining += 1;
+          _command = Some(command);
+          _backpressure = Some(backpressure);
+
+          continue;
+        },
+        Written::Disconnected((server, command, error)) => {
+          _debug!(inner, "Handle disconnect for {:?} due to {:?}", server, error);
+          let commands = router.connections.disconnect(inner, server.as_ref()).await;
+          router.buffer_commands(commands);
+          if let Some(command) = command {
+            if command.should_finish_with_error(inner) {
+              command.finish(inner, Err(error));
+            } else {
+              router.buffer_command(command);
+            }
+          }
+
+          utils::defer_reconnect(inner);
+          break;
+        },
+        Written::NotFound(mut command) => {
+          if let Err(e) = command.decr_check_redirections() {
+            command.finish(inner, Err(e));
+            utils::defer_reconnect(inner);
+            break;
+          }
+
+          _debug!(inner, "Perform cluster sync after missing hash slot lookup.");
+          if let Err(error) = router.sync_cluster().await {
+            // try to sync the cluster once, and failing that buffer the command.
+            _warn!(inner, "Failed to sync cluster after NotFound: {:?}", error);
+            utils::defer_reconnect(inner);
+            router.buffer_command(command);
+            utils::delay_cluster_sync(inner).await?;
+            break;
+          } else {
+            _command = Some(command);
+            _backpressure = None;
+            continue;
+          }
+        },
+        Written::Ignore => {
+          _trace!(inner, "Ignore `Written` response.");
+          break;
+        },
+        Written::SentAll => {
+          _trace!(inner, "Sent command to all servers.");
+          let _ = router.check_and_flush().await;
+          if let Some(command) = handle_router_response(inner, router, router_rx).await? {
+            // commands that are sent to all nodes are not retried after a connection closing
+            _warn!(inner, "Responding with canceled error after all nodes command failure.");
+            command.finish(inner, Err(RedisError::new_canceled()));
+            break;
+          } else {
+            if closes_connection {
+              _trace!(inner, "Ending command loop after QUIT or SHUTDOWN.");
+              return Err(RedisError::new_canceled());
+            }
+
+            break;
+          }
+        },
+        Written::Sent((server, flushed)) => {
+          _trace!(inner, "Sent command to {}. Flushed: {}", server, flushed);
+          if is_blocking {
+            inner.backchannel.write().await.set_blocked(&server);
+          }
+          if !flushed {
+            let _ = router.check_and_flush().await;
+          }
+
+          let should_interrupt =
+            is_blocking && inner.counters.read_cmd_buffer_len() > 0 && inner.config.blocking == Blocking::Interrupt;
+          if should_interrupt {
+            // if there's other commands in the queue then interrupt the command that was just sent
+            _debug!(inner, "Interrupt after write.");
+            if let Err(e) = client_utils::interrupt_blocked_connection(inner, ClientUnblockFlag::Error).await {
+              _warn!(inner, "Failed to unblock connection: {:?}", e);
+            }
+          }
+
+          if let Some(command) = handle_router_response(inner, router, router_rx).await? {
+            _command = Some(command);
+            _backpressure = None;
+            continue;
+          } else {
+            if closes_connection {
+              _trace!(inner, "Ending command loop after QUIT or SHUTDOWN.");
+              return Err(RedisError::new_canceled());
+            }
+
+            break;
+          }
+        },
+        Written::Error((error, command)) => {
+          _debug!(inner, "Fatal error writing command: {:?}", error);
+          if let Some(command) = command {
+            command.finish(inner, Err(error.clone()));
+          }
+          inner.notifications.broadcast_error(error.clone());
+
+          utils::defer_reconnect(inner);
+          return Err(error);
+        },
+        #[cfg(feature = "replicas")]
+        Written::Fallback(command) => {
+          _error!(
+            inner,
+            "Unexpected replica response to {} ({})",
+            command.kind.to_str_debug(),
+            command.debug_id()
+          );
+          command.finish(
+            inner,
+            Err(RedisError::new(RedisErrorKind::Replica, "Unexpected replica response.")),
+          );
+          break;
+        },
+      }
+    }
+
+    Ok(())
+  })
+  .await
+}
+
+#[cfg(feature = "full-tracing")]
+async fn write_with_backpressure_t(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  mut command: RedisCommand,
+  force_pipeline: bool,
+) -> Result<(), RedisError> {
+  if inner.should_trace() {
+    command.take_queued_span();
+    let span = fspan!(command, inner.full_tracing_span_level(), "fred.write");
+    write_with_backpressure(inner, router, command, force_pipeline)
+      .instrument(span)
+      .await
+  } else {
+    write_with_backpressure(inner, router, command, force_pipeline).await
+  }
+}
+
+#[cfg(not(feature = "full-tracing"))]
+async fn write_with_backpressure_t(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  command: RedisCommand,
+  force_pipeline: bool,
+) -> Result<(), RedisError> {
+  write_with_backpressure(inner, router, command, force_pipeline).await
+}
+
+/// Run a pipelined series of commands, queueing commands to run later if needed.
+async fn process_pipeline(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  commands: Vec<RedisCommand>,
+) -> Result<(), RedisError> {
+  _debug!(inner, "Writing pipeline with {} commands", commands.len());
+
+  for mut command in commands.into_iter() {
+    // trying to pipeline `SSUBSCRIBE` is problematic since successful responses arrive out-of-order via pubsub push
+    // frames, but error redirections are returned in-order and the client is expected to follow them. this makes it
+    // very difficult to accurately associate redirections with `ssubscribe` calls within a pipeline. to avoid this we
+    // never pipeline `ssubscribe`, even if the caller asks.
+    let force_pipeline = if command.kind == RedisCommandKind::Ssubscribe {
+      command.can_pipeline = false;
+      false
+    } else {
+      command.can_pipeline = true;
+      !command.is_all_cluster_nodes()
+    };
+    command.skip_backpressure = true;
+
+    if let Err(e) = write_with_backpressure_t(inner, router, command, force_pipeline).await {
+      // if the command cannot be written it will be queued to run later.
+      // if a connection is dropped due to an error the reader will send a command to reconnect and retry later.
+      _debug!(inner, "Error writing command in pipeline: {:?}", e);
+    }
+  }
+
+  Ok(())
+}
+
+/// Send ASKING to the provided server, then retry the provided command.
+async fn process_ask(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  server: Server,
+  slot: u16,
+  mut command: RedisCommand,
+) -> Result<(), RedisError> {
+  command.use_replica = false;
+  command.hasher = ClusterHash::Custom(slot);
+
+  if let Err(e) = command.decr_check_redirections() {
+    command.respond_to_caller(Err(e));
+    return Ok(());
+  }
+  if let Err(e) = utils::send_asking_with_policy(inner, router, &server, slot).await {
+    command.respond_to_caller(Err(e.clone()));
+    return Err(e);
+  }
+  if let Err(error) = write_with_backpressure_t(inner, router, command, false).await {
+    _debug!(inner, "Error sending command after ASKING: {:?}", error);
+    Err(error)
+  } else {
+    Ok(())
+  }
+}
+
+/// Sync the cluster state then retry the command.
+async fn process_moved(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  server: Server,
+  slot: u16,
+  mut command: RedisCommand,
+) -> Result<(), RedisError> {
+  command.use_replica = false;
+  command.hasher = ClusterHash::Custom(slot);
+
+  utils::delay_cluster_sync(inner).await?;
+  _debug!(inner, "Syncing cluster after MOVED {} {}", slot, server);
+  if let Err(e) = utils::sync_cluster_with_policy(inner, router).await {
+    command.respond_to_caller(Err(e.clone()));
+    return Err(e);
+  }
+  if let Err(e) = command.decr_check_redirections() {
+    command.respond_to_caller(Err(e));
+    return Ok(());
+  }
+  if let Err(error) = write_with_backpressure_t(inner, router, command, false).await {
+    _debug!(inner, "Error sending command after MOVED: {:?}", error);
+    Err(error)
+  } else {
+    Ok(())
+  }
+}
+
+#[cfg(feature = "replicas")]
+async fn process_replica_reconnect(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  server: Option<Server>,
+  force: bool,
+  tx: Option<ResponseSender>,
+  replica: bool,
+) -> Result<(), RedisError> {
+  #[allow(unused_mut)]
+  if replica {
+    let result = utils::sync_replicas_with_policy(inner, router, false).await;
+    if let Some(mut tx) = tx {
+      let _ = tx.send(result.map(|_| Resp3Frame::Null));
+    }
+
+    Ok(())
+  } else {
+    process_reconnect(inner, router, server, force, tx).await
+  }
+}
+
+/// Reconnect to the server(s).
+#[allow(unused_mut)]
+async fn process_reconnect(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  server: Option<Server>,
+  force: bool,
+  tx: Option<ResponseSender>,
+) -> Result<(), RedisError> {
+  _debug!(inner, "Maybe reconnecting to {:?} (force: {})", server, force);
+
+  if let Some(server) = server {
+    let has_connection = router.connections.has_server_connection(&server);
+    _debug!(inner, "Has working connection: {}", has_connection);
+
+    if has_connection && !force {
+      _debug!(inner, "Skip reconnecting to {}", server);
+      if let Some(mut tx) = tx {
+        let _ = tx.send(Ok(Resp3Frame::Null));
+      }
+
+      return Ok(());
+    }
+  }
+
+  if !force && router.has_healthy_centralized_connection() {
+    _debug!(inner, "Skip reconnecting to centralized host");
+    if let Some(mut tx) = tx {
+      let _ = tx.send(Ok(Resp3Frame::Null));
+    }
+    return Ok(());
+  }
+
+  _debug!(inner, "Starting reconnection loop...");
+  if let Err(e) = utils::reconnect_with_policy(inner, router).await {
+    if let Some(mut tx) = tx {
+      let _ = tx.send(Err(e.clone()));
+    }
+
+    Err(e)
+  } else {
+    if let Some(mut tx) = tx {
+      let _ = tx.send(Ok(Resp3Frame::Null));
+    }
+
+    Ok(())
+  }
+}
+
+#[cfg(feature = "replicas")]
+#[allow(unused_mut)]
+async fn process_sync_replicas(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  mut tx: OneshotSender<Result<(), RedisError>>,
+  reset: bool,
+) -> Result<(), RedisError> {
+  let result = utils::sync_replicas_with_policy(inner, router, reset).await;
+  let _ = tx.send(result);
+  Ok(())
+}
+
+/// Sync and update the cached cluster state.
+#[allow(unused_mut)]
+async fn process_sync_cluster(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  mut tx: OneshotSender<Result<(), RedisError>>,
+) -> Result<(), RedisError> {
+  let result = utils::sync_cluster_with_policy(inner, router).await;
+  let _ = tx.send(result.clone());
+  result
+}
+
+/// Send a single command to the server(s).
+async fn process_normal_command(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  command: RedisCommand,
+) -> Result<(), RedisError> {
+  write_with_backpressure_t(inner, router, command, false).await
+}
+
+/// Read the set of active connections managed by the client.
+#[allow(unused_mut)]
+fn process_connections(
+  inner: &RefCount<RedisClientInner>,
+  router: &Router,
+  mut tx: OneshotSender<Vec<Server>>,
+) -> Result<(), RedisError> {
+  #[allow(unused_mut)]
+  let mut connections = router.connections.active_connections();
+  #[cfg(feature = "replicas")]
+  connections.extend(router.replicas.writers.keys().cloned());
+
+  _debug!(inner, "Active connections: {:?}", connections);
+  let _ = tx.send(connections);
+  Ok(())
+}
+
+/// Process any kind of router command.
+async fn process_command(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  command: RouterCommand,
+) -> Result<(), RedisError> {
+  match command {
+    RouterCommand::Ask { server, slot, command } => process_ask(inner, router, server, slot, command).await,
+    RouterCommand::Moved { server, slot, command } => process_moved(inner, router, server, slot, command).await,
+    RouterCommand::SyncCluster { tx } => process_sync_cluster(inner, router, tx).await,
+    #[cfg(feature = "transactions")]
+    RouterCommand::Transaction {
+      commands,
+      watched,
+      id,
+      tx,
+      abort_on_error,
+    } => transactions::run(inner, router, commands, watched, id, abort_on_error, tx).await,
+    RouterCommand::Pipeline { commands } => process_pipeline(inner, router, commands).await,
+    RouterCommand::Command(command) => process_normal_command(inner, router, command).await,
+    RouterCommand::Connections { tx } => process_connections(inner, router, tx),
+    #[cfg(feature = "replicas")]
+    RouterCommand::SyncReplicas { tx, reset } => process_sync_replicas(inner, router, tx, reset).await,
+    #[cfg(not(feature = "replicas"))]
+    RouterCommand::Reconnect { server, force, tx } => process_reconnect(inner, router, server, force, tx).await,
+    #[cfg(feature = "replicas")]
+    RouterCommand::Reconnect {
+      server,
+      force,
+      tx,
+      replica,
+    } => process_replica_reconnect(inner, router, server, force, tx, replica).await,
+  }
+}
+
+/// Start processing commands from the client front end.
+async fn process_commands(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  rx: &mut CommandReceiver,
+) -> Result<(), RedisError> {
+  _debug!(inner, "Starting command processing stream...");
+  while let Some(command) = rx.recv().await {
+    inner.counters.decr_cmd_buffer_len();
+
+    _trace!(inner, "Recv command: {:?}", command);
+    if let Err(e) = process_command(inner, router, command).await {
+      // errors on this interface end the client connection task
+      if e.is_canceled() {
+        break;
+      } else {
+        _error!(inner, "Disconnecting after error processing command: {:?}", e);
+        let _ = router.disconnect_all().await;
+        router.clear_retry_buffer();
+        return Err(e);
+      }
+    }
+  }
+
+  _debug!(inner, "Disconnecting after command stream closes.");
+  let _ = router.disconnect_all().await;
+  router.clear_retry_buffer();
+  Ok(())
+}
+
+/// Start the command processing stream, initiating new connections in the process.
+pub async fn start(inner: &RefCount<RedisClientInner>) -> Result<(), RedisError> {
+  #[cfg(feature = "mocks")]
+  if let Some(ref mocks) = inner.config.mocks {
+    return mocking::start(inner, mocks).await;
+  }
+
+  let mut rx = match inner.take_command_rx() {
+    Some(rx) => rx,
+    None => {
+      // the `_lock` field on inner synchronizes the getters/setters on the command channel halves, so if this field
+      // is None then another task must have set and removed the receiver concurrently.
+      return Err(RedisError::new(
+        RedisErrorKind::Config,
+        "Another connection task is already running.",
+      ));
+    },
+  };
+
+  inner.reset_reconnection_attempts();
+  let mut router = Router::new(inner);
+  _debug!(inner, "Initializing router with policy: {:?}", inner.reconnect_policy());
+  let result = if inner.config.fail_fast {
+    if let Err(e) = Box::pin(router.connect()).await {
+      inner.notifications.broadcast_connect(Err(e.clone()));
+      inner.notifications.broadcast_error(e.clone());
+      Err(e)
+    } else {
+      client_utils::set_client_state(&inner.state, ClientState::Connected);
+      inner.notifications.broadcast_connect(Ok(()));
+      Ok(())
+    }
+  } else {
+    utils::reconnect_with_policy(inner, &mut router).await
+  };
+
+  if let Err(error) = result {
+    inner.store_command_rx(rx, false);
+    Err(error)
+  } else {
+    let result = Box::pin(process_commands(inner, &mut router, &mut rx)).await;
+    inner.store_command_rx(rx, false);
+    result
+  }
+}
+
+#[cfg(feature = "mocks")]
+#[allow(unused_mut)]
+mod mocking {
+  use super::*;
+  use crate::{modules::mocks::Mocks, protocol::utils as protocol_utils};
+
+  /// Process any kind of router command.
+  pub fn process_command(mocks: &RefCount<dyn Mocks>, command: RouterCommand) -> Result<(), RedisError> {
+    match command {
+      #[cfg(feature = "transactions")]
+      RouterCommand::Transaction { commands, mut tx, .. } => {
+        let mocked = commands.into_iter().skip(1).map(|c| c.to_mocked()).collect();
+
+        match mocks.process_transaction(mocked) {
+          Ok(result) => {
+            let _ = tx.send(Ok(protocol_utils::mocked_value_to_frame(result)));
+            Ok(())
+          },
+          Err(err) => {
+            let _ = tx.send(Err(err));
+            Ok(())
+          },
+        }
+      },
+      RouterCommand::Pipeline { commands } => {
+        for mut command in commands.into_iter() {
+          let mocked = command.to_mocked();
+          let result = mocks.process_command(mocked).map(protocol_utils::mocked_value_to_frame);
+
+          command.respond_to_caller(result);
+        }
+
+        Ok(())
+      },
+      RouterCommand::Command(mut command) => {
+        let result = mocks
+          .process_command(command.to_mocked())
+          .map(protocol_utils::mocked_value_to_frame);
+        command.respond_to_caller(result);
+
+        Ok(())
+      },
+      _ => Err(RedisError::new(RedisErrorKind::Unknown, "Unimplemented.")),
+    }
+  }
+
+  pub async fn process_commands(
+    inner: &RefCount<RedisClientInner>,
+    mocks: &RefCount<dyn Mocks>,
+    rx: &mut CommandReceiver,
+  ) -> Result<(), RedisError> {
+    while let Some(command) = rx.recv().await {
+      inner.counters.decr_cmd_buffer_len();
+
+      _trace!(inner, "Recv mock command: {:?}", command);
+      if let Err(e) = process_command(mocks, command) {
+        // errors on this interface end the client connection task
+        _error!(inner, "Ending early after error processing mock command: {:?}", e);
+        if e.is_canceled() {
+          break;
+        } else {
+          return Err(e);
+        }
+      }
+    }
+
+    Ok(())
+  }
+
+  pub async fn start(inner: &RefCount<RedisClientInner>, mocks: &RefCount<dyn Mocks>) -> Result<(), RedisError> {
+    _debug!(inner, "Starting mocking layer");
+
+    #[cfg(feature = "glommio")]
+    glommio::yield_if_needed().await;
+    #[cfg(not(feature = "glommio"))]
+    tokio::task::yield_now().await;
+
+    let mut rx = match inner.take_command_rx() {
+      Some(rx) => rx,
+      None => {
+        return Err(RedisError::new(
+          RedisErrorKind::Config,
+          "Redis client is already initialized.",
+        ))
+      },
+    };
+
+    inner.notifications.broadcast_connect(Ok(()));
+    let result = process_commands(inner, mocks, &mut rx).await;
+    inner.store_command_rx(rx, false);
+    result
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/router/mod.rs.html b/doc/src/fred/router/mod.rs.html new file mode 100644 index 00000000..ee47b54f --- /dev/null +++ b/doc/src/fred/router/mod.rs.html @@ -0,0 +1,1941 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  modules::inner::RedisClientInner,
+  protocol::{
+    command::{RedisCommand, RouterReceiver},
+    connection::{self, CommandBuffer, Counters, RedisWriter},
+    types::{ClusterRouting, Server},
+  },
+  runtime::RefCount,
+  trace,
+  utils as client_utils,
+};
+use futures::future::try_join_all;
+use semver::Version;
+use std::{
+  collections::{HashMap, VecDeque},
+  fmt,
+  fmt::Formatter,
+  time::Duration,
+};
+
+#[cfg(feature = "transactions")]
+use crate::runtime::oneshot_channel;
+#[cfg(feature = "transactions")]
+use crate::{protocol::command::ClusterErrorKind, protocol::responders::ResponseKind};
+#[cfg(feature = "replicas")]
+use std::collections::HashSet;
+
+pub mod centralized;
+pub mod clustered;
+pub mod commands;
+pub mod reader;
+pub mod replicas;
+pub mod responses;
+pub mod sentinel;
+pub mod types;
+pub mod utils;
+
+#[cfg(feature = "transactions")]
+pub mod transactions;
+
+#[cfg(feature = "replicas")]
+use crate::router::replicas::Replicas;
+
+/// The result of an attempt to send a command to the server.
+// This is not an ideal pattern, but it mostly comes from the requirement that the shared buffer interface take
+// ownership over the command.
+pub enum Written {
+  /// Apply backpressure to the command before retrying.
+  Backpressure((RedisCommand, Backpressure)),
+  /// Indicates that the command was sent to the associated server and whether the socket was flushed.
+  Sent((Server, bool)),
+  /// Indicates that the command was sent to all servers.
+  SentAll,
+  /// The command could not be written since the connection is down.
+  Disconnected((Option<Server>, Option<RedisCommand>, RedisError)),
+  /// Ignore the result and move on to the next command.
+  Ignore,
+  /// The command could not be routed to any server.
+  NotFound(RedisCommand),
+  /// A fatal error that should interrupt the router.
+  Error((RedisError, Option<RedisCommand>)),
+  /// Restart the write process on a primary node connection.
+  #[cfg(feature = "replicas")]
+  Fallback(RedisCommand),
+}
+
+impl fmt::Display for Written {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    write!(f, "{}", match self {
+      Written::Backpressure(_) => "Backpressure",
+      Written::Sent(_) => "Sent",
+      Written::SentAll => "SentAll",
+      Written::Disconnected(_) => "Disconnected",
+      Written::Ignore => "Ignore",
+      Written::NotFound(_) => "NotFound",
+      Written::Error(_) => "Error",
+      #[cfg(feature = "replicas")]
+      Written::Fallback(_) => "Fallback",
+    })
+  }
+}
+
+pub enum Backpressure {
+  /// The amount of time to wait.
+  Wait(Duration),
+  /// Block the client until the command receives a response.
+  Block,
+  /// Return a backpressure error to the caller of the command.
+  Error(RedisError),
+}
+
+impl Backpressure {
+  /// Apply the backpressure policy.
+  pub async fn wait(
+    self,
+    inner: &RefCount<RedisClientInner>,
+    command: &mut RedisCommand,
+  ) -> Result<Option<RouterReceiver>, RedisError> {
+    match self {
+      Backpressure::Error(e) => Err(e),
+      Backpressure::Wait(duration) => {
+        _debug!(inner, "Backpressure policy (wait): {:?}", duration);
+        trace::backpressure_event(command, Some(duration.as_millis()));
+        inner.wait_with_interrupt(duration).await?;
+        Ok(None)
+      },
+      Backpressure::Block => {
+        _debug!(inner, "Backpressure (block)");
+        trace::backpressure_event(command, None);
+        if !command.has_router_channel() {
+          _trace!(
+            inner,
+            "Blocking router for backpressure for {}",
+            command.kind.to_str_debug()
+          );
+          command.skip_backpressure = true;
+          Ok(Some(command.create_router_channel()))
+        } else {
+          Ok(None)
+        }
+      },
+    }
+  }
+}
+
+/// Connection maps for the supported deployment types.
+pub enum Connections {
+  Centralized {
+    /// The connection to the primary server.
+    writer: Option<RedisWriter>,
+  },
+  Clustered {
+    /// The cached cluster routing table used for mapping keys to server IDs.
+    cache:   ClusterRouting,
+    /// A map of server IDs and connections.
+    writers: HashMap<Server, RedisWriter>,
+  },
+  Sentinel {
+    /// The connection to the primary server.
+    writer: Option<RedisWriter>,
+  },
+}
+
+impl Connections {
+  pub fn new_centralized() -> Self {
+    Connections::Centralized { writer: None }
+  }
+
+  pub fn new_sentinel() -> Self {
+    Connections::Sentinel { writer: None }
+  }
+
+  pub fn new_clustered() -> Self {
+    Connections::Clustered {
+      cache:   ClusterRouting::new(),
+      writers: HashMap::new(),
+    }
+  }
+
+  /// Discover and return a mapping of replica nodes to their associated primary node.
+  #[cfg(feature = "replicas")]
+  pub async fn replica_map(
+    &mut self,
+    inner: &RefCount<RedisClientInner>,
+  ) -> Result<HashMap<Server, Server>, RedisError> {
+    Ok(match self {
+      Connections::Centralized { ref mut writer } | Connections::Sentinel { ref mut writer } => {
+        if let Some(writer) = writer {
+          writer
+            .discover_replicas(inner)
+            .await?
+            .into_iter()
+            .map(|replica| (replica, writer.server.clone()))
+            .collect()
+        } else {
+          HashMap::new()
+        }
+      },
+      Connections::Clustered { ref writers, .. } => {
+        let mut out = HashMap::with_capacity(writers.len());
+
+        for primary in writers.keys() {
+          let replicas = inner
+            .with_cluster_state(|state| Ok(state.replicas(primary)))
+            .ok()
+            .unwrap_or_default();
+
+          for replica in replicas.into_iter() {
+            out.insert(replica, primary.clone());
+          }
+        }
+        out
+      },
+    })
+  }
+
+  /// Whether the connection map has a connection to the provided server`.
+  pub fn has_server_connection(&mut self, server: &Server) -> bool {
+    match self {
+      Connections::Centralized { ref mut writer } | Connections::Sentinel { ref mut writer } => {
+        if let Some(writer) = writer.as_mut() {
+          if writer.server == *server {
+            writer.is_working()
+          } else {
+            false
+          }
+        } else {
+          false
+        }
+      },
+      Connections::Clustered { ref mut writers, .. } => {
+        for (_, writer) in writers.iter_mut() {
+          if writer.server == *server {
+            return writer.is_working();
+          }
+        }
+
+        false
+      },
+    }
+  }
+
+  /// Get the connection writer half for the provided server.
+  pub fn get_connection_mut(&mut self, server: &Server) -> Option<&mut RedisWriter> {
+    match self {
+      Connections::Centralized { ref mut writer } => {
+        writer
+          .as_mut()
+          .and_then(|writer| if writer.server == *server { Some(writer) } else { None })
+      },
+      Connections::Sentinel { ref mut writer } => {
+        writer
+          .as_mut()
+          .and_then(|writer| if writer.server == *server { Some(writer) } else { None })
+      },
+      Connections::Clustered { ref mut writers, .. } => writers.get_mut(server),
+    }
+  }
+
+  /// Initialize the underlying connection(s) and update the cached backchannel information.
+  pub async fn initialize(
+    &mut self,
+    inner: &RefCount<RedisClientInner>,
+    buffer: &mut VecDeque<RedisCommand>,
+  ) -> Result<(), RedisError> {
+    let result = if inner.config.server.is_clustered() {
+      Box::pin(clustered::initialize_connections(inner, self, buffer)).await
+    } else if inner.config.server.is_centralized() || inner.config.server.is_unix_socket() {
+      Box::pin(centralized::initialize_connection(inner, self, buffer)).await
+    } else if inner.config.server.is_sentinel() {
+      Box::pin(sentinel::initialize_connection(inner, self, buffer)).await
+    } else {
+      return Err(RedisError::new(RedisErrorKind::Config, "Invalid client configuration."));
+    };
+
+    // TODO clean this up
+    if result.is_ok() {
+      if let Some(version) = self.server_version() {
+        inner.server_state.write().kind.set_server_version(version);
+      }
+
+      let mut backchannel = inner.backchannel.write().await;
+      backchannel.connection_ids = self.connection_ids();
+    }
+    result
+  }
+
+  /// Read the counters associated with a connection to a server.
+  pub fn counters(&self, server: Option<&Server>) -> Option<&Counters> {
+    match self {
+      Connections::Centralized { ref writer } => writer.as_ref().map(|w| &w.counters),
+      Connections::Sentinel { ref writer, .. } => writer.as_ref().map(|w| &w.counters),
+      Connections::Clustered { ref writers, .. } => {
+        server.and_then(|server| writers.get(server).map(|w| &w.counters))
+      },
+    }
+  }
+
+  /// Read the server version, if known.
+  pub fn server_version(&self) -> Option<Version> {
+    match self {
+      Connections::Centralized { ref writer } => writer.as_ref().and_then(|w| w.version.clone()),
+      Connections::Clustered { ref writers, .. } => writers.iter().find_map(|(_, w)| w.version.clone()),
+      Connections::Sentinel { ref writer, .. } => writer.as_ref().and_then(|w| w.version.clone()),
+    }
+  }
+
+  /// Disconnect from the provided server, using the default centralized connection if `None` is provided.
+  pub async fn disconnect(&mut self, inner: &RefCount<RedisClientInner>, server: Option<&Server>) -> CommandBuffer {
+    match self {
+      Connections::Centralized { ref mut writer } => {
+        if let Some(writer) = writer.take() {
+          _debug!(inner, "Disconnecting from {}", writer.server);
+          writer.graceful_close().await
+        } else {
+          Vec::new()
+        }
+      },
+      Connections::Clustered { ref mut writers, .. } => {
+        let mut out = VecDeque::new();
+
+        if let Some(server) = server {
+          if let Some(writer) = writers.remove(server) {
+            _debug!(inner, "Disconnecting from {}", writer.server);
+            let commands = writer.graceful_close().await;
+            out.extend(commands);
+          }
+        }
+        out.into_iter().collect()
+      },
+      Connections::Sentinel { ref mut writer } => {
+        if let Some(writer) = writer.take() {
+          _debug!(inner, "Disconnecting from {}", writer.server);
+          writer.graceful_close().await
+        } else {
+          Vec::new()
+        }
+      },
+    }
+  }
+
+  /// Disconnect and clear local state for all connections, returning all in-flight commands.
+  pub async fn disconnect_all(&mut self, inner: &RefCount<RedisClientInner>) -> CommandBuffer {
+    match self {
+      Connections::Centralized { ref mut writer } => {
+        if let Some(writer) = writer.take() {
+          _debug!(inner, "Disconnecting from {}", writer.server);
+          writer.graceful_close().await
+        } else {
+          Vec::new()
+        }
+      },
+      Connections::Clustered { ref mut writers, .. } => {
+        let mut out = VecDeque::new();
+        for (_, writer) in writers.drain() {
+          _debug!(inner, "Disconnecting from {}", writer.server);
+          let commands = writer.graceful_close().await;
+          out.extend(commands.into_iter());
+        }
+        out.into_iter().collect()
+      },
+      Connections::Sentinel { ref mut writer } => {
+        if let Some(writer) = writer.take() {
+          _debug!(inner, "Disconnecting from {}", writer.server);
+          writer.graceful_close().await
+        } else {
+          Vec::new()
+        }
+      },
+    }
+  }
+
+  /// Read a map of connection IDs (via `CLIENT ID`) for each inner connections.
+  pub fn connection_ids(&self) -> HashMap<Server, i64> {
+    let mut out = HashMap::new();
+
+    match self {
+      Connections::Centralized { writer } => {
+        if let Some(writer) = writer {
+          if let Some(id) = writer.id {
+            out.insert(writer.server.clone(), id);
+          }
+        }
+      },
+      Connections::Sentinel { writer, .. } => {
+        if let Some(writer) = writer {
+          if let Some(id) = writer.id {
+            out.insert(writer.server.clone(), id);
+          }
+        }
+      },
+      Connections::Clustered { writers, .. } => {
+        for (server, writer) in writers.iter() {
+          if let Some(id) = writer.id {
+            out.insert(server.clone(), id);
+          }
+        }
+      },
+    }
+
+    out
+  }
+
+  /// Flush the socket(s) associated with each server if they have pending frames.
+  pub async fn check_and_flush(&mut self, inner: &RefCount<RedisClientInner>) -> Result<(), RedisError> {
+    _trace!(inner, "Checking and flushing sockets...");
+
+    match self {
+      Connections::Centralized { ref mut writer } => {
+        if let Some(writer) = writer {
+          writer.flush().await
+        } else {
+          Ok(())
+        }
+      },
+      Connections::Sentinel { ref mut writer, .. } => {
+        if let Some(writer) = writer {
+          writer.flush().await
+        } else {
+          Ok(())
+        }
+      },
+      Connections::Clustered { ref mut writers, .. } => {
+        try_join_all(writers.values_mut().map(|writer| writer.flush()))
+          .await
+          .map(|_| ())
+      },
+    }
+  }
+
+  /// Send a command to the server(s).
+  pub async fn write(
+    &mut self,
+    inner: &RefCount<RedisClientInner>,
+    command: RedisCommand,
+    force_flush: bool,
+  ) -> Written {
+    match self {
+      Connections::Clustered {
+        ref mut writers,
+        ref mut cache,
+      } => clustered::write(inner, writers, cache, command, force_flush).await,
+      Connections::Centralized { ref mut writer } => centralized::write(inner, writer, command, force_flush).await,
+      Connections::Sentinel { ref mut writer, .. } => centralized::write(inner, writer, command, force_flush).await,
+    }
+  }
+
+  /// Send a command to all servers in a cluster.
+  pub async fn write_all_cluster(&mut self, inner: &RefCount<RedisClientInner>, command: RedisCommand) -> Written {
+    if let Connections::Clustered { ref mut writers, .. } = self {
+      if let Err(error) = clustered::send_all_cluster_command(inner, writers, command).await {
+        Written::Disconnected((None, None, error))
+      } else {
+        Written::SentAll
+      }
+    } else {
+      Written::Error((
+        RedisError::new(RedisErrorKind::Config, "Expected clustered configuration."),
+        None,
+      ))
+    }
+  }
+
+  /// Check if the provided `server` node owns the provided `slot`.
+  pub fn check_cluster_owner(&self, slot: u16, server: &Server) -> bool {
+    match self {
+      Connections::Clustered { ref cache, .. } => cache
+        .get_server(slot)
+        .map(|owner| {
+          trace!("Comparing cached cluster owner for {}: {} == {}", slot, owner, server);
+          owner == server
+        })
+        .unwrap_or(false),
+      _ => false,
+    }
+  }
+
+  /// Connect or reconnect to the provided `host:port`.
+  pub async fn add_connection(
+    &mut self,
+    inner: &RefCount<RedisClientInner>,
+    server: &Server,
+  ) -> Result<(), RedisError> {
+    if let Connections::Clustered { ref mut writers, .. } = self {
+      let mut transport = connection::create(inner, server, None).await?;
+      transport.setup(inner, None).await?;
+
+      let (server, writer) = connection::split(inner, transport, false, clustered::spawn_reader_task)?;
+      writers.insert(server, writer);
+      Ok(())
+    } else {
+      Err(RedisError::new(
+        RedisErrorKind::Config,
+        "Expected clustered configuration.",
+      ))
+    }
+  }
+
+  /// Read the list of active/working connections.
+  pub fn active_connections(&self) -> Vec<Server> {
+    match self {
+      Connections::Clustered { ref writers, .. } => writers
+        .iter()
+        .filter_map(|(server, writer)| {
+          if writer.is_working() {
+            Some(server.clone())
+          } else {
+            None
+          }
+        })
+        .collect(),
+      Connections::Centralized { ref writer } | Connections::Sentinel { ref writer, .. } => writer
+        .as_ref()
+        .and_then(|writer| {
+          if writer.is_working() {
+            Some(vec![writer.server.clone()])
+          } else {
+            None
+          }
+        })
+        .unwrap_or(Vec::new()),
+    }
+  }
+}
+
+/// A struct for routing commands to the server(s).
+pub struct Router {
+  /// The connection map for each deployment type.
+  pub connections: Connections,
+  /// The inner client state associated with the router.
+  pub inner:       RefCount<RedisClientInner>,
+  /// Storage for commands that should be deferred or retried later.
+  pub buffer:      VecDeque<RedisCommand>,
+  /// The replica routing interface.
+  #[cfg(feature = "replicas")]
+  pub replicas:    Replicas,
+}
+
+impl Router {
+  /// Create a new `Router` without connecting to the server(s).
+  pub fn new(inner: &RefCount<RedisClientInner>) -> Self {
+    let connections = if inner.config.server.is_clustered() {
+      Connections::new_clustered()
+    } else if inner.config.server.is_sentinel() {
+      Connections::new_sentinel()
+    } else {
+      Connections::new_centralized()
+    };
+
+    Router {
+      buffer: VecDeque::new(),
+      inner: inner.clone(),
+      connections,
+      #[cfg(feature = "replicas")]
+      replicas: Replicas::new(),
+    }
+  }
+
+  /// Read the server that should receive the provided command.
+  #[cfg(any(feature = "transactions", feature = "replicas"))]
+  pub fn find_connection(&self, command: &RedisCommand) -> Option<&Server> {
+    match self.connections {
+      Connections::Centralized { ref writer } => writer.as_ref().map(|w| &w.server),
+      Connections::Sentinel { ref writer } => writer.as_ref().map(|w| &w.server),
+      Connections::Clustered { ref cache, .. } => command.cluster_hash().and_then(|slot| cache.get_server(slot)),
+    }
+  }
+
+  pub fn has_healthy_centralized_connection(&self) -> bool {
+    match self.connections {
+      Connections::Centralized { ref writer } | Connections::Sentinel { ref writer } => {
+        writer.as_ref().map(|w| w.is_working()).unwrap_or(false)
+      },
+      Connections::Clustered { .. } => false,
+    }
+  }
+
+  /// Attempt to send the command to the server.
+  pub async fn write(&mut self, command: RedisCommand, force_flush: bool) -> Written {
+    let send_all_cluster_nodes =
+      self.inner.config.server.is_clustered() && (command.is_all_cluster_nodes() || command.kind.closes_connection());
+
+    if command.write_attempts >= 1 {
+      self.inner.counters.incr_redelivery_count();
+    }
+    if send_all_cluster_nodes {
+      self.connections.write_all_cluster(&self.inner, command).await
+    } else {
+      self.connections.write(&self.inner, command, force_flush).await
+    }
+  }
+
+  /// Write a command to a replica node if possible, falling back to a primary node if configured.
+  #[cfg(feature = "replicas")]
+  pub async fn write_replica(&mut self, mut command: RedisCommand, force_flush: bool) -> Written {
+    if !command.use_replica {
+      return self.write(command, force_flush).await;
+    }
+
+    let primary = match self.find_connection(&command) {
+      Some(server) => server.clone(),
+      None => {
+        return if self.inner.connection.replica.primary_fallback {
+          debug!(
+            "{}: Fallback to primary node connection for {} ({})",
+            self.inner.id,
+            command.kind.to_str_debug(),
+            command.debug_id()
+          );
+
+          command.use_replica = false;
+          self.write(command, force_flush).await
+        } else {
+          command.finish(
+            &self.inner,
+            Err(RedisError::new(
+              RedisErrorKind::Replica,
+              "Missing primary node connection.",
+            )),
+          );
+
+          Written::Ignore
+        }
+      },
+    };
+
+    let result = self.replicas.write(&self.inner, &primary, command, force_flush).await;
+    match result {
+      Written::Fallback(mut command) => {
+        debug!(
+          "{}: Fall back to primary node for {} ({}) after replica error",
+          self.inner.id,
+          command.kind.to_str_debug(),
+          command.debug_id(),
+        );
+
+        command.use_replica = false;
+        self.write(command, force_flush).await
+      },
+      _ => result,
+    }
+  }
+
+  /// Write a command to a replica node if possible, falling back to a primary node if configured.
+  #[cfg(not(feature = "replicas"))]
+  pub async fn write_replica(&mut self, command: RedisCommand, force_flush: bool) -> Written {
+    self.write(command, force_flush).await
+  }
+
+  /// Attempt to write the command to a specific server without backpressure.
+  pub async fn write_direct(&mut self, mut command: RedisCommand, server: &Server) -> Written {
+    debug!(
+      "{}: Direct write `{}` command to {}, ID: {}",
+      self.inner.id,
+      command.kind.to_str_debug(),
+      server,
+      command.debug_id()
+    );
+
+    let writer = match self.connections.get_connection_mut(server) {
+      Some(writer) => writer,
+      None => {
+        trace!("{}: Missing connection to {}", self.inner.id, server);
+        return Written::NotFound(command);
+      },
+    };
+    let frame = match utils::prepare_command(&self.inner, &writer.counters, &mut command) {
+      Ok((frame, _)) => frame,
+      Err(e) => {
+        warn!(
+          "{}: Frame encoding error for {}",
+          self.inner.id,
+          command.kind.to_str_debug()
+        );
+        // do not retry commands that trigger frame encoding errors
+        command.finish(&self.inner, Err(e));
+        return Written::Ignore;
+      },
+    };
+    let blocks_connection = command.blocks_connection();
+    command.write_attempts += 1;
+
+    if !writer.is_working() {
+      let error = RedisError::new(RedisErrorKind::IO, "Connection closed.");
+      debug!("{}: Error sending command: {:?}", self.inner.id, error);
+      return Written::Disconnected((Some(writer.server.clone()), Some(command), error));
+    }
+
+    let no_incr = command.has_no_responses();
+    writer.push_command(&self.inner, command);
+    if let Err(err) = writer.write_frame(frame, true, no_incr).await {
+      Written::Disconnected((Some(writer.server.clone()), None, err))
+    } else {
+      if blocks_connection {
+        self.inner.backchannel.write().await.set_blocked(&writer.server);
+      }
+      Written::Sent((writer.server.clone(), true))
+    }
+  }
+
+  /// Disconnect from all the servers, moving the in-flight messages to the internal command buffer and triggering a
+  /// reconnection, if necessary.
+  pub async fn disconnect_all(&mut self) {
+    let commands = self.connections.disconnect_all(&self.inner).await;
+    self.buffer_commands(commands);
+    self.disconnect_replicas().await;
+  }
+
+  /// Disconnect from all the servers, moving the in-flight messages to the internal command buffer and triggering a
+  /// reconnection, if necessary.
+  #[cfg(feature = "replicas")]
+  pub async fn disconnect_replicas(&mut self) {
+    if let Err(e) = self.replicas.clear_connections(&self.inner).await {
+      warn!("{}: Error disconnecting replicas: {:?}", self.inner.id, e);
+    }
+  }
+
+  #[cfg(not(feature = "replicas"))]
+  pub async fn disconnect_replicas(&mut self) {}
+
+  /// Add the provided commands to the retry buffer.
+  pub fn buffer_commands(&mut self, commands: impl IntoIterator<Item = RedisCommand>) {
+    for command in commands.into_iter() {
+      self.buffer_command(command);
+    }
+  }
+
+  /// Add the provided command to the retry buffer.
+  pub fn buffer_command(&mut self, command: RedisCommand) {
+    trace!(
+      "{}: Adding {} ({}) command to retry buffer.",
+      self.inner.id,
+      command.kind.to_str_debug(),
+      command.debug_id()
+    );
+    self.buffer.push_back(command);
+  }
+
+  /// Clear all the commands in the retry buffer.
+  pub fn clear_retry_buffer(&mut self) {
+    trace!(
+      "{}: Clearing retry buffer with {} commands.",
+      self.inner.id,
+      self.buffer.len()
+    );
+    self.buffer.clear();
+  }
+
+  /// Connect to the server(s), discarding any previous connection state.
+  pub async fn connect(&mut self) -> Result<(), RedisError> {
+    self.disconnect_all().await;
+    let result = self.connections.initialize(&self.inner, &mut self.buffer).await;
+
+    if result.is_ok() {
+      #[cfg(feature = "replicas")]
+      self.refresh_replica_routing().await?;
+
+      Ok(())
+    } else {
+      result
+    }
+  }
+
+  /// Gracefully reset the replica routing table.
+  #[cfg(feature = "replicas")]
+  pub async fn refresh_replica_routing(&mut self) -> Result<(), RedisError> {
+    self.replicas.clear_routing();
+    if let Err(e) = self.sync_replicas().await {
+      if !self.inner.ignore_replica_reconnect_errors() {
+        return Err(e);
+      }
+    }
+
+    Ok(())
+  }
+
+  /// Sync the cached cluster state with the server via `CLUSTER SLOTS`.
+  ///
+  /// This will also create new connections or drop old connections as needed.
+  pub async fn sync_cluster(&mut self) -> Result<(), RedisError> {
+    let result = clustered::sync(&self.inner, &mut self.connections, &mut self.buffer).await;
+
+    if result.is_ok() {
+      #[cfg(feature = "replicas")]
+      self.refresh_replica_routing().await?;
+      self.retry_buffer().await;
+    }
+
+    result
+  }
+
+  /// Rebuild the cached replica routing table based on the primary node connections.
+  #[cfg(feature = "replicas")]
+  pub async fn sync_replicas(&mut self) -> Result<(), RedisError> {
+    debug!("{}: Syncing replicas...", self.inner.id);
+    self.replicas.drop_broken_connections().await;
+    let old_connections = self.replicas.active_connections();
+    let new_replica_map = self.connections.replica_map(&self.inner).await?;
+
+    let old_connections_idx: HashSet<_> = old_connections.iter().collect();
+    let new_connections_idx: HashSet<_> = new_replica_map.keys().collect();
+    let remove: Vec<_> = old_connections_idx.difference(&new_connections_idx).collect();
+
+    for server in remove.into_iter() {
+      debug!("{}: Dropping replica connection to {}", self.inner.id, server);
+      self.replicas.drop_writer(server).await;
+      self.replicas.remove_replica(server);
+    }
+
+    for (mut replica, primary) in new_replica_map.into_iter() {
+      let should_use = if let Some(filter) = self.inner.connection.replica.filter.as_ref() {
+        filter.filter(&primary, &replica).await
+      } else {
+        true
+      };
+
+      if should_use {
+        replicas::map_replica_tls_names(&self.inner, &primary, &mut replica);
+
+        self
+          .replicas
+          .add_connection(&self.inner, primary, replica, false)
+          .await?;
+      }
+    }
+
+    self
+      .inner
+      .server_state
+      .write()
+      .update_replicas(self.replicas.routing_table());
+    Ok(())
+  }
+
+  /// Attempt to replay all queued commands on the internal buffer without backpressure.
+  pub async fn retry_buffer(&mut self) {
+    let mut failed_commands: VecDeque<_> = VecDeque::new();
+    let mut commands: VecDeque<_> = self.buffer.drain(..).collect();
+    #[cfg(feature = "replicas")]
+    commands.extend(self.replicas.take_retry_buffer());
+
+    while let Some(mut command) = commands.pop_front() {
+      if client_utils::read_bool_atomic(&command.timed_out) {
+        debug!(
+          "{}: Ignore retrying timed out command: {}",
+          self.inner.id,
+          command.kind.to_str_debug()
+        );
+        continue;
+      }
+
+      if let Err(e) = command.decr_check_attempted() {
+        command.finish(&self.inner, Err(e));
+        continue;
+      }
+      command.skip_backpressure = true;
+      trace!(
+        "{}: Retry `{}` ({}) command, attempts left: {}",
+        self.inner.id,
+        command.kind.to_str_debug(),
+        command.debug_id(),
+        command.attempts_remaining,
+      );
+
+      let result = if command.use_replica {
+        self.write_replica(command, true).await
+      } else {
+        self.write(command, true).await
+      };
+
+      match result {
+        Written::Disconnected((server, command, error)) => {
+          if let Some(command) = command {
+            failed_commands.push_back(command);
+          }
+
+          debug!(
+            "{}: Disconnect while retrying after write error: {:?}",
+            &self.inner.id, error
+          );
+          self.connections.disconnect(&self.inner, server.as_ref()).await;
+          utils::defer_reconnect(&self.inner);
+          break;
+        },
+        Written::NotFound(command) => {
+          failed_commands.push_back(command);
+
+          warn!(
+            "{}: Disconnect and re-sync cluster state after routing error while retrying commands.",
+            self.inner.id
+          );
+          self.disconnect_all().await;
+          utils::defer_reconnect(&self.inner);
+          break;
+        },
+        Written::Error((error, command)) => {
+          warn!("{}: Error replaying command: {:?}", self.inner.id, error);
+          if let Some(command) = command {
+            command.finish(&self.inner, Err(error));
+          }
+          self.disconnect_all().await;
+          utils::defer_reconnect(&self.inner);
+          break;
+        },
+        _ => {},
+      }
+    }
+
+    failed_commands.extend(commands);
+    self.buffer_commands(failed_commands);
+  }
+
+  /// Check each connection for pending frames that have not been flushed, and flush the connection if needed.
+  #[cfg(feature = "replicas")]
+  pub async fn check_and_flush(&mut self) -> Result<(), RedisError> {
+    if let Err(e) = self.replicas.check_and_flush().await {
+      warn!("{}: Error flushing replica connections: {:?}", self.inner.id, e);
+    }
+    self.connections.check_and_flush(&self.inner).await
+  }
+
+  #[cfg(not(feature = "replicas"))]
+  pub async fn check_and_flush(&mut self) -> Result<(), RedisError> {
+    self.connections.check_and_flush(&self.inner).await
+  }
+
+  /// Returns whether the provided `server` owns the provided `slot`.
+  pub fn cluster_node_owns_slot(&self, slot: u16, server: &Server) -> bool {
+    match self.connections {
+      Connections::Clustered { ref cache, .. } => cache.get_server(slot).map(|node| node == server).unwrap_or(false),
+      _ => false,
+    }
+  }
+
+  /// Modify connection state according to the cluster redirection error.
+  ///
+  /// * Synchronizes the cached cluster state in response to MOVED
+  /// * Connects and sends `ASKING` to the provided server in response to ASKED
+  #[cfg(feature = "transactions")]
+  pub async fn cluster_redirection(
+    &mut self,
+    kind: &ClusterErrorKind,
+    slot: u16,
+    server: &Server,
+  ) -> Result<(), RedisError> {
+    debug!(
+      "{}: Handling cluster redirect {:?} {} {}",
+      &self.inner.id, kind, slot, server
+    );
+
+    if *kind == ClusterErrorKind::Moved {
+      let should_sync = self
+        .inner
+        .with_cluster_state(|state| Ok(state.get_server(slot).map(|owner| server != owner).unwrap_or(true)))
+        .unwrap_or(true);
+
+      if should_sync {
+        self.sync_cluster().await?;
+      }
+    } else if *kind == ClusterErrorKind::Ask {
+      if !self.connections.has_server_connection(server) {
+        self.connections.add_connection(&self.inner, server).await?;
+        self
+          .inner
+          .backchannel
+          .write()
+          .await
+          .update_connection_ids(&self.connections);
+      }
+
+      // can't use request_response since there may be pipelined commands ahead of this
+      let (tx, rx) = oneshot_channel();
+      let mut command = RedisCommand::new_asking(slot);
+      command.response = ResponseKind::Respond(Some(tx));
+      command.skip_backpressure = true;
+
+      match self.write_direct(command, server).await {
+        Written::Error((error, _)) => return Err(error),
+        Written::Disconnected((_, _, error)) => return Err(error),
+        Written::NotFound(_) => return Err(RedisError::new(RedisErrorKind::Cluster, "Connection not found.")),
+        _ => {},
+      };
+
+      let _ = client_utils::timeout(rx, self.inner.internal_command_timeout()).await??;
+    }
+
+    Ok(())
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/router/reader.rs.html b/doc/src/fred/router/reader.rs.html new file mode 100644 index 00000000..a830446e --- /dev/null +++ b/doc/src/fred/router/reader.rs.html @@ -0,0 +1,3 @@ +reader.rs - source
1
+

+
\ No newline at end of file diff --git a/doc/src/fred/router/replicas.rs.html b/doc/src/fred/router/replicas.rs.html new file mode 100644 index 00000000..396389da --- /dev/null +++ b/doc/src/fred/router/replicas.rs.html @@ -0,0 +1,1111 @@ +replicas.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+
#[cfg(all(feature = "replicas", any(feature = "enable-native-tls", feature = "enable-rustls")))]
+use crate::types::TlsHostMapping;
+#[cfg(feature = "replicas")]
+use crate::{
+  error::{RedisError, RedisErrorKind},
+  modules::inner::RedisClientInner,
+  protocol::{
+    command::RedisCommand,
+    connection,
+    connection::{CommandBuffer, RedisWriter},
+  },
+  router::{centralized, clustered, utils, Written},
+  runtime::RefCount,
+  types::Server,
+};
+#[cfg(feature = "replicas")]
+use std::{
+  collections::{HashMap, VecDeque},
+  fmt,
+  fmt::Formatter,
+};
+
+/// An interface used to filter the list of available replica nodes.
+#[cfg(feature = "replicas")]
+#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+#[async_trait]
+pub trait ReplicaFilter: Send + Sync + 'static {
+  /// Returns whether the replica node mapping can be used when routing commands to replicas.
+  #[allow(unused_variables)]
+  async fn filter(&self, primary: &Server, replica: &Server) -> bool {
+    true
+  }
+}
+
+/// Configuration options for replica node connections.
+#[cfg(feature = "replicas")]
+#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+#[derive(Clone)]
+pub struct ReplicaConfig {
+  /// Whether the client should lazily connect to replica nodes.
+  ///
+  /// Default: `true`
+  pub lazy_connections:           bool,
+  /// An optional interface for filtering available replica nodes.
+  ///
+  /// Default: `None`
+  pub filter:                     Option<RefCount<dyn ReplicaFilter>>,
+  /// Whether the client should ignore errors from replicas that occur when the max reconnection count is reached.
+  ///
+  /// Default: `true`
+  pub ignore_reconnection_errors: bool,
+  /// The number of times a command can fail with a replica connection error before being sent to a primary node.
+  ///
+  /// Default: `0` (unlimited)
+  pub connection_error_count:     u32,
+  /// Whether the client should use the associated primary node if no replica exists that can serve a command.
+  ///
+  /// Default: `true`
+  pub primary_fallback:           bool,
+}
+
+#[cfg(feature = "replicas")]
+impl fmt::Debug for ReplicaConfig {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    f.debug_struct("ReplicaConfig")
+      .field("lazy_connections", &self.lazy_connections)
+      .field("ignore_reconnection_errors", &self.ignore_reconnection_errors)
+      .field("connection_error_count", &self.connection_error_count)
+      .field("primary_fallback", &self.primary_fallback)
+      .finish()
+  }
+}
+
+#[cfg(feature = "replicas")]
+impl PartialEq for ReplicaConfig {
+  fn eq(&self, other: &Self) -> bool {
+    self.lazy_connections == other.lazy_connections
+      && self.ignore_reconnection_errors == other.ignore_reconnection_errors
+      && self.connection_error_count == other.connection_error_count
+      && self.primary_fallback == other.primary_fallback
+  }
+}
+
+#[cfg(feature = "replicas")]
+impl Eq for ReplicaConfig {}
+
+#[cfg(feature = "replicas")]
+impl Default for ReplicaConfig {
+  fn default() -> Self {
+    ReplicaConfig {
+      lazy_connections:           true,
+      filter:                     None,
+      ignore_reconnection_errors: true,
+      connection_error_count:     0,
+      primary_fallback:           true,
+    }
+  }
+}
+
+/// A container for round-robin routing among replica nodes.
+// This implementation optimizes for next() at the cost of add() and remove()
+#[derive(Clone, Debug, PartialEq, Eq, Default)]
+#[cfg(feature = "replicas")]
+#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+pub struct ReplicaRouter {
+  counter: usize,
+  servers: Vec<Server>,
+}
+
+#[cfg(feature = "replicas")]
+impl ReplicaRouter {
+  /// Read the server that should receive the next command.
+  pub fn next(&mut self) -> Option<&Server> {
+    self.counter = (self.counter + 1) % self.servers.len();
+    self.servers.get(self.counter)
+  }
+
+  /// Conditionally add the server to the replica set.
+  pub fn add(&mut self, server: Server) {
+    if !self.servers.contains(&server) {
+      self.servers.push(server);
+    }
+  }
+
+  /// Remove the server from the replica set.
+  pub fn remove(&mut self, server: &Server) {
+    self.servers = self.servers.drain(..).filter(|_server| server != _server).collect();
+  }
+
+  /// The size of the replica set.
+  pub fn len(&self) -> usize {
+    self.servers.len()
+  }
+
+  /// Iterate over the replica set.
+  pub fn iter(&self) -> impl Iterator<Item = &Server> {
+    self.servers.iter()
+  }
+}
+
+/// A container for round-robin routing to replica servers.
+#[cfg(feature = "replicas")]
+#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+#[derive(Clone, Debug, Eq, PartialEq, Default)]
+pub struct ReplicaSet {
+  /// A map of primary server IDs to a counter and set of replica server IDs.
+  servers: HashMap<Server, ReplicaRouter>,
+}
+
+#[cfg(feature = "replicas")]
+#[allow(dead_code)]
+impl ReplicaSet {
+  /// Create a new empty replica set.
+  pub fn new() -> ReplicaSet {
+    ReplicaSet {
+      servers: HashMap::new(),
+    }
+  }
+
+  /// Add a replica node to the routing table.
+  pub fn add(&mut self, primary: Server, replica: Server) {
+    self.servers.entry(primary).or_default().add(replica);
+  }
+
+  /// Remove a replica node mapping from the routing table.
+  pub fn remove(&mut self, primary: &Server, replica: &Server) {
+    let should_remove = if let Some(router) = self.servers.get_mut(primary) {
+      router.remove(replica);
+      router.len() == 0
+    } else {
+      false
+    };
+
+    if should_remove {
+      self.servers.remove(primary);
+    }
+  }
+
+  /// Remove the replica from all routing sets.
+  pub fn remove_replica(&mut self, replica: &Server) {
+    self.servers = self
+      .servers
+      .drain()
+      .filter_map(|(primary, mut routing)| {
+        routing.remove(replica);
+
+        if routing.len() > 0 {
+          Some((primary, routing))
+        } else {
+          None
+        }
+      })
+      .collect();
+  }
+
+  /// Read the server ID of the next replica that should receive a command.
+  pub fn next_replica(&mut self, primary: &Server) -> Option<&Server> {
+    self.servers.get_mut(primary).and_then(|router| router.next())
+  }
+
+  /// Read all the replicas associated with the provided primary node.
+  pub fn replicas(&self, primary: &Server) -> impl Iterator<Item = &Server> {
+    self
+      .servers
+      .get(primary)
+      .map(|router| router.iter())
+      .into_iter()
+      .flatten()
+  }
+
+  /// Return a map of replica nodes to primary nodes.
+  pub fn to_map(&self) -> HashMap<Server, Server> {
+    let mut out = HashMap::with_capacity(self.servers.len());
+    for (primary, replicas) in self.servers.iter() {
+      for replica in replicas.iter() {
+        out.insert(replica.clone(), primary.clone());
+      }
+    }
+
+    out
+  }
+
+  /// Read the set of all known replica nodes for all primary nodes.
+  pub fn all_replicas(&self) -> Vec<Server> {
+    let mut out = Vec::with_capacity(self.servers.len());
+    for (_, replicas) in self.servers.iter() {
+      for replica in replicas.iter() {
+        out.push(replica.clone());
+      }
+    }
+
+    out
+  }
+
+  /// Clear the routing table.
+  pub fn clear(&mut self) {
+    self.servers.clear();
+  }
+}
+
+/// A struct for routing commands to replica nodes.
+#[cfg(feature = "replicas")]
+pub struct Replicas {
+  pub(crate) writers: HashMap<Server, RedisWriter>,
+  routing:            ReplicaSet,
+  buffer:             VecDeque<RedisCommand>,
+}
+
+#[cfg(feature = "replicas")]
+#[allow(dead_code)]
+impl Replicas {
+  pub fn new() -> Replicas {
+    Replicas {
+      writers: HashMap::new(),
+      routing: ReplicaSet::new(),
+      buffer:  VecDeque::new(),
+    }
+  }
+
+  /// Sync the connection map in place based on the cached routing table.
+  pub async fn sync_connections(&mut self, inner: &RefCount<RedisClientInner>) -> Result<(), RedisError> {
+    for (_, writer) in self.writers.drain() {
+      let commands = writer.graceful_close().await;
+      self.buffer.extend(commands);
+    }
+
+    for (replica, primary) in self.routing.to_map() {
+      self.add_connection(inner, primary, replica, false).await?;
+    }
+
+    Ok(())
+  }
+
+  /// Drop all connections and clear the cached routing table.
+  pub async fn clear_connections(&mut self, inner: &RefCount<RedisClientInner>) -> Result<(), RedisError> {
+    self.routing.clear();
+    self.sync_connections(inner).await
+  }
+
+  /// Clear the cached routing table without dropping connections.
+  pub fn clear_routing(&mut self) {
+    self.routing.clear();
+  }
+
+  /// Connect to the replica and add it to the cached routing table.
+  pub async fn add_connection(
+    &mut self,
+    inner: &RefCount<RedisClientInner>,
+    primary: Server,
+    replica: Server,
+    force: bool,
+  ) -> Result<(), RedisError> {
+    _debug!(
+      inner,
+      "Adding replica connection {} (replica) -> {} (primary)",
+      replica,
+      primary
+    );
+
+    if !inner.connection.replica.lazy_connections || force {
+      let mut transport = connection::create(inner, &replica, None).await?;
+      transport.setup(inner, None).await?;
+
+      let (_, writer) = if inner.config.server.is_clustered() {
+        transport.readonly(inner, None).await?;
+        connection::split(inner, transport, true, clustered::spawn_reader_task)?
+      } else {
+        connection::split(inner, transport, true, centralized::spawn_reader_task)?
+      };
+
+      self.writers.insert(replica.clone(), writer);
+    }
+
+    self.routing.add(primary, replica);
+    Ok(())
+  }
+
+  /// Drop the socket associated with the provided server.
+  pub async fn drop_writer(&mut self, replica: &Server) {
+    if let Some(writer) = self.writers.remove(replica) {
+      let commands = writer.graceful_close().await;
+      self.buffer.extend(commands);
+    }
+  }
+
+  /// Remove the replica from the routing table.
+  pub fn remove_replica(&mut self, replica: &Server) {
+    self.routing.remove_replica(replica);
+  }
+
+  /// Close the replica connection and optionally remove the replica from the routing table.
+  pub async fn remove_connection(
+    &mut self,
+    inner: &RefCount<RedisClientInner>,
+    primary: &Server,
+    replica: &Server,
+    keep_routable: bool,
+  ) -> Result<(), RedisError> {
+    _debug!(
+      inner,
+      "Removing replica connection {} (replica) -> {} (primary)",
+      replica,
+      primary
+    );
+    self.drop_writer(replica).await;
+
+    if !keep_routable {
+      self.routing.remove(primary, replica);
+    }
+    Ok(())
+  }
+
+  /// Check and flush all the sockets managed by the replica routing state.
+  pub async fn check_and_flush(&mut self) -> Result<(), RedisError> {
+    for (_, writer) in self.writers.iter_mut() {
+      writer.flush().await?;
+    }
+
+    Ok(())
+  }
+
+  /// Whether a working connection exists to any replica for the provided primary node.
+  pub fn has_replica_connection(&self, primary: &Server) -> bool {
+    for replica in self.routing.replicas(primary) {
+      if self.has_connection(replica) {
+        return true;
+      }
+    }
+
+    false
+  }
+
+  /// Whether a connection exists to the provided replica node.
+  pub fn has_connection(&self, replica: &Server) -> bool {
+    self.writers.get(replica).map(|w| w.is_working()).unwrap_or(false)
+  }
+
+  /// Return a map of `replica` -> `primary` server identifiers.
+  pub fn routing_table(&self) -> HashMap<Server, Server> {
+    self.routing.to_map()
+  }
+
+  /// Check the active connections and drop any without a working reader task.
+  pub async fn drop_broken_connections(&mut self) {
+    let mut new_writers = HashMap::with_capacity(self.writers.len());
+    for (server, writer) in self.writers.drain() {
+      if writer.is_working() {
+        new_writers.insert(server, writer);
+      } else {
+        let commands = writer.graceful_close().await;
+        self.buffer.extend(commands);
+        self.routing.remove_replica(&server);
+      }
+    }
+
+    self.writers = new_writers;
+  }
+
+  /// Read the set of all active connections.
+  pub fn active_connections(&self) -> Vec<Server> {
+    self
+      .writers
+      .iter()
+      .filter_map(|(server, writer)| {
+        if writer.is_working() {
+          Some(server.clone())
+        } else {
+          None
+        }
+      })
+      .collect()
+  }
+
+  /// Send a command to one of the replicas associated with the provided primary server.
+  pub async fn write(
+    &mut self,
+    inner: &RefCount<RedisClientInner>,
+    primary: &Server,
+    mut command: RedisCommand,
+    force_flush: bool,
+  ) -> Written {
+    let replica = match command.cluster_node {
+      Some(ref server) => server.clone(),
+      None => match self.routing.next_replica(primary) {
+        Some(replica) => replica.clone(),
+        None => {
+          // we do not know of any replica node associated with the primary node
+          return if inner.connection.replica.primary_fallback {
+            Written::Fallback(command)
+          } else {
+            command.finish(
+              inner,
+              Err(RedisError::new(RedisErrorKind::Replica, "Missing replica node.")),
+            );
+            Written::Ignore
+          };
+        },
+      },
+    };
+
+    _trace!(
+      inner,
+      "Found replica {} (primary: {}) for {} ({})",
+      replica,
+      primary,
+      command.kind.to_str_debug(),
+      command.debug_id()
+    );
+
+    let writer = match self.writers.get_mut(&replica) {
+      Some(writer) => writer,
+      None => {
+        // these errors indicate that we know a replica node should exist, but we are not connected or cannot
+        // connect to it. in this case we want to hide the error, trigger a reconnect, and retry the command later.
+        if inner.connection.replica.lazy_connections {
+          _debug!(inner, "Lazily adding {} replica connection", replica);
+          if let Err(e) = self.add_connection(inner, primary.clone(), replica.clone(), true).await {
+            // we tried connecting once but failed.
+            self.routing.remove_replica(&replica);
+            // since we didn't get to actually send the command
+            command.attempts_remaining += 1;
+            return Written::Disconnected((Some(replica.clone()), Some(command), e));
+          }
+
+          match self.writers.get_mut(&replica) {
+            Some(writer) => writer,
+            None => {
+              self.routing.remove_replica(&replica);
+              // the connection should be here if self.add_connection succeeded
+              return Written::Disconnected((
+                Some(replica.clone()),
+                Some(command),
+                RedisError::new(RedisErrorKind::Replica, "Missing connection."),
+              ));
+            },
+          }
+        } else {
+          // we don't have a connection to the replica and we're not configured to lazily create new ones
+          return Written::NotFound(command);
+        }
+      },
+    };
+    let (frame, should_flush) = match utils::prepare_command(inner, &writer.counters, &mut command) {
+      Ok((frame, should_flush)) => (frame, should_flush || force_flush),
+      Err(e) => {
+        _warn!(inner, "Frame encoding error for {}", command.kind.to_str_debug());
+        // do not retry commands that trigger frame encoding errors
+        command.finish(inner, Err(e));
+        return Written::Ignore;
+      },
+    };
+
+    let blocks_connection = command.blocks_connection();
+    _debug!(
+      inner,
+      "Sending {} ({}) to replica {}",
+      command.kind.to_str_debug(),
+      command.debug_id(),
+      replica
+    );
+    command.write_attempts += 1;
+
+    if !writer.is_working() {
+      let error = RedisError::new(RedisErrorKind::IO, "Connection closed.");
+
+      _debug!(
+        inner,
+        "Error sending replica command {}: {:?}",
+        command.kind.to_str_debug(),
+        error
+      );
+      self.routing.remove_replica(&writer.server);
+      return Written::Disconnected((Some(writer.server.clone()), Some(command), error));
+    }
+
+    writer.push_command(inner, command);
+    if let Err(err) = writer.write_frame(frame, should_flush, false).await {
+      self.routing.remove_replica(&writer.server);
+      Written::Disconnected((Some(writer.server.clone()), None, err))
+    } else {
+      if blocks_connection {
+        inner.backchannel.write().await.set_blocked(&writer.server);
+      }
+      Written::Sent((writer.server.clone(), should_flush))
+    }
+  }
+
+  /// Take the commands stored for retry later.
+  pub fn take_retry_buffer(&mut self) -> CommandBuffer {
+    self.buffer.drain(..).collect()
+  }
+}
+
+#[cfg(all(feature = "replicas", any(feature = "enable-native-tls", feature = "enable-rustls")))]
+pub fn map_replica_tls_names(inner: &RefCount<RedisClientInner>, primary: &Server, replica: &mut Server) {
+  let policy = match inner.config.tls {
+    Some(ref config) => &config.hostnames,
+    None => {
+      _trace!(inner, "Skip modifying TLS hostname for replicas.");
+      return;
+    },
+  };
+  if *policy == TlsHostMapping::None {
+    _trace!(inner, "Skip modifying TLS hostnames for replicas.");
+    return;
+  }
+
+  replica.set_tls_server_name(policy, &primary.host);
+}
+
+#[cfg(all(
+  feature = "replicas",
+  not(any(feature = "enable-native-tls", feature = "enable-rustls"))
+))]
+pub fn map_replica_tls_names(_: &RefCount<RedisClientInner>, _: &Server, _: &mut Server) {}
+
\ No newline at end of file diff --git a/doc/src/fred/router/responses.rs.html b/doc/src/fred/router/responses.rs.html new file mode 100644 index 00000000..2b553a27 --- /dev/null +++ b/doc/src/fred/router/responses.rs.html @@ -0,0 +1,671 @@ +responses.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  modules::inner::RedisClientInner,
+  protocol::{command::RedisCommand, types::Server, utils as protocol_utils, utils::pretty_error},
+  runtime::RefCount,
+  trace,
+  types::{ClientState, KeyspaceEvent, Message, RedisKey, RedisValue},
+  utils,
+};
+use redis_protocol::{
+  resp3::types::{BytesFrame as Resp3Frame, FrameKind, Resp3Frame as _Resp3Frame},
+  types::PUBSUB_PUSH_PREFIX,
+};
+use std::str;
+
+#[cfg(feature = "i-tracking")]
+use crate::types::Invalidation;
+
+const KEYSPACE_PREFIX: &str = "__keyspace@";
+const KEYEVENT_PREFIX: &str = "__keyevent@";
+#[cfg(feature = "i-tracking")]
+const INVALIDATION_CHANNEL: &str = "__redis__:invalidate";
+
+fn parse_keyspace_notification(channel: &str, message: &RedisValue) -> Option<KeyspaceEvent> {
+  if channel.starts_with(KEYEVENT_PREFIX) {
+    let parts: Vec<&str> = channel.splitn(2, '@').collect();
+    if parts.len() < 2 {
+      return None;
+    }
+
+    let suffix: Vec<&str> = parts[1].splitn(2, ':').collect();
+    if suffix.len() < 2 {
+      return None;
+    }
+
+    let db = suffix[0].replace("__", "").parse::<u8>().ok()?;
+    let operation = suffix[1].to_owned();
+    let key: RedisKey = message.clone().try_into().ok()?;
+
+    Some(KeyspaceEvent { db, key, operation })
+  } else if channel.starts_with(KEYSPACE_PREFIX) {
+    let parts: Vec<&str> = channel.splitn(2, '@').collect();
+    if parts.len() < 2 {
+      return None;
+    }
+
+    let suffix: Vec<&str> = parts[1].splitn(2, ':').collect();
+    if suffix.len() < 2 {
+      return None;
+    }
+
+    let db = suffix[0].replace("__", "").parse::<u8>().ok()?;
+    let key: RedisKey = suffix[1].to_owned().into();
+    let operation = message.as_string()?;
+
+    Some(KeyspaceEvent { db, key, operation })
+  } else {
+    None
+  }
+}
+
+#[cfg(feature = "i-tracking")]
+fn broadcast_pubsub_invalidation(inner: &RefCount<RedisClientInner>, message: Message, server: &Server) {
+  if let Some(invalidation) = Invalidation::from_message(message, server) {
+    inner.notifications.broadcast_invalidation(invalidation);
+  } else {
+    _debug!(
+      inner,
+      "Dropping pubsub message on invalidation channel that cannot be parsed as an invalidation message."
+    );
+  }
+}
+
+#[cfg(not(feature = "i-tracking"))]
+fn broadcast_pubsub_invalidation(_: &RefCount<RedisClientInner>, _: Message, _: &Server) {}
+
+#[cfg(feature = "i-tracking")]
+fn is_pubsub_invalidation(message: &Message) -> bool {
+  message.channel == INVALIDATION_CHANNEL
+}
+
+#[cfg(not(feature = "i-tracking"))]
+fn is_pubsub_invalidation(_: &Message) -> bool {
+  false
+}
+
+#[cfg(feature = "i-tracking")]
+fn broadcast_resp3_invalidation(inner: &RefCount<RedisClientInner>, server: &Server, frame: Resp3Frame) {
+  if let Resp3Frame::Push { mut data, .. } = frame {
+    if data.len() != 2 {
+      return;
+    }
+
+    // RESP3 example: Push { data: [BlobString { data: b"invalidate", attributes: None }, Array { data:
+    //                [BlobString { data: b"foo", attributes: None }], attributes: None }], attributes: None }
+    if let Resp3Frame::Array { data, .. } = data[1].take() {
+      inner.notifications.broadcast_invalidation(Invalidation {
+        keys:   data
+          .into_iter()
+          .filter_map(|f| f.as_bytes().map(|b| b.into()))
+          .collect(),
+        server: server.clone(),
+      })
+    }
+  }
+}
+
+#[cfg(not(feature = "i-tracking"))]
+fn broadcast_resp3_invalidation(_: &RefCount<RedisClientInner>, _: &Server, _: Resp3Frame) {}
+
+#[cfg(feature = "i-tracking")]
+fn is_resp3_invalidation(frame: &Resp3Frame) -> bool {
+  // RESP3 example: Push { data: [BlobString { data: b"invalidate", attributes: None }, Array { data:
+  //                [BlobString { data: b"foo", attributes: None }], attributes: None }], attributes: None }
+  if let Resp3Frame::Push { ref data, .. } = frame {
+    data
+      .first()
+      .and_then(|f| f.as_str())
+      .map(|s| s == "invalidate")
+      .unwrap_or(false)
+  } else {
+    false
+  }
+}
+
+// `SSUBSCRIBE` is intentionally not included so that we can handle MOVED errors. this works as long as we never
+// pipeline ssubscribe calls.
+fn is_subscribe_prefix(s: &str) -> bool {
+  s == "subscribe" || s == "psubscribe"
+}
+
+fn is_unsubscribe_prefix(s: &str) -> bool {
+  s == "unsubscribe" || s == "punsubscribe" || s == "sunsubscribe"
+}
+
+/// Whether the response frame represents a response to any of the subscription interface commands.
+fn is_subscription_response(frame: &Resp3Frame) -> bool {
+  match frame {
+    Resp3Frame::Array { ref data, .. } | Resp3Frame::Push { ref data, .. } => {
+      if data.len() >= 3 && data.len() <= 4 {
+        // check for ["pubsub", "punsubscribe"|"sunsubscribe", ..] or ["punsubscribe"|"sunsubscribe", ..]
+        (data[0].as_str().map(|s| s == PUBSUB_PUSH_PREFIX).unwrap_or(false)
+          && data[1]
+            .as_str()
+            .map(|s| is_subscribe_prefix(s) || is_unsubscribe_prefix(s))
+            .unwrap_or(false))
+          || (data[0]
+            .as_str()
+            .map(|s| is_subscribe_prefix(s) || is_unsubscribe_prefix(s))
+            .unwrap_or(false))
+      } else {
+        false
+      }
+    },
+    _ => false,
+  }
+}
+
+#[cfg(not(feature = "i-tracking"))]
+fn is_resp3_invalidation(_: &Resp3Frame) -> bool {
+  false
+}
+
+/// Check if the frame is part of a pubsub message, and if so route it to any listeners.
+///
+/// If not then return it to the caller for further processing.
+pub fn check_pubsub_message(
+  inner: &RefCount<RedisClientInner>,
+  server: &Server,
+  frame: Resp3Frame,
+) -> Option<Resp3Frame> {
+  if is_subscription_response(&frame) {
+    _debug!(inner, "Dropping unused subscription response.");
+    return None;
+  }
+  if is_resp3_invalidation(&frame) {
+    broadcast_resp3_invalidation(inner, server, frame);
+    return None;
+  }
+
+  let is_pubsub =
+    frame.is_normal_pubsub_message() || frame.is_pattern_pubsub_message() || frame.is_shard_pubsub_message();
+  if !is_pubsub {
+    return Some(frame);
+  }
+
+  let span = trace::create_pubsub_span(inner, &frame);
+  _trace!(inner, "Processing pubsub message from {}.", server);
+  let parsed_frame = if let Some(ref span) = span {
+    #[allow(clippy::let_unit_value)]
+    let _guard = span.enter();
+    protocol_utils::frame_to_pubsub(server, frame)
+  } else {
+    protocol_utils::frame_to_pubsub(server, frame)
+  };
+
+  let message = match parsed_frame {
+    Ok(data) => data,
+    Err(err) => {
+      _warn!(inner, "Invalid message on pubsub interface: {:?}", err);
+      return None;
+    },
+  };
+  if let Some(ref span) = span {
+    span.record("channel", &*message.channel);
+  }
+
+  if is_pubsub_invalidation(&message) {
+    broadcast_pubsub_invalidation(inner, message, server);
+  } else if let Some(event) = parse_keyspace_notification(&message.channel, &message.value) {
+    inner.notifications.broadcast_keyspace(event);
+  } else {
+    inner.notifications.broadcast_pubsub(message);
+  }
+
+  None
+}
+
+// TODO cleanup and rename
+// this is called by the reader task after a blocking command finishes in order to mark the connection as unblocked
+pub async fn check_and_set_unblocked_flag(inner: &RefCount<RedisClientInner>, command: &RedisCommand) {
+  if command.blocks_connection() {
+    inner.backchannel.write().await.set_unblocked();
+  }
+}
+
+/// Parse the response frame to see if it's an auth error.
+fn parse_redis_auth_error(frame: &Resp3Frame) -> Option<RedisError> {
+  if matches!(frame.kind(), FrameKind::SimpleError | FrameKind::BlobError) {
+    match protocol_utils::frame_to_results(frame.clone()) {
+      Ok(_) => None,
+      Err(e) => match e.kind() {
+        RedisErrorKind::Auth => Some(e),
+        _ => None,
+      },
+    }
+  } else {
+    None
+  }
+}
+
+#[cfg(feature = "custom-reconnect-errors")]
+fn check_global_reconnect_errors(inner: &RefCount<RedisClientInner>, frame: &Resp3Frame) -> Option<RedisError> {
+  if let Resp3Frame::SimpleError { ref data, .. } = frame {
+    for prefix in inner.connection.reconnect_errors.iter() {
+      if data.starts_with(prefix.to_str()) {
+        _warn!(inner, "Found reconnection error: {}", data);
+        let error = protocol_utils::pretty_error(data);
+        inner.notifications.broadcast_error(error.clone());
+        return Some(error);
+      }
+    }
+
+    None
+  } else {
+    None
+  }
+}
+
+#[cfg(not(feature = "custom-reconnect-errors"))]
+fn check_global_reconnect_errors(_: &RefCount<RedisClientInner>, _: &Resp3Frame) -> Option<RedisError> {
+  None
+}
+
+fn is_clusterdown_error(frame: &Resp3Frame) -> Option<&str> {
+  match frame {
+    Resp3Frame::SimpleError { data, .. } => {
+      if data.trim().starts_with("CLUSTERDOWN") {
+        Some(data)
+      } else {
+        None
+      }
+    },
+    Resp3Frame::BlobError { data, .. } => {
+      let parsed = match str::from_utf8(data) {
+        Ok(s) => s,
+        Err(_) => return None,
+      };
+
+      if parsed.trim().starts_with("CLUSTERDOWN") {
+        Some(parsed)
+      } else {
+        None
+      }
+    },
+    _ => None,
+  }
+}
+
+/// Check for special errors configured by the caller to initiate a reconnection process.
+pub fn check_special_errors(inner: &RefCount<RedisClientInner>, frame: &Resp3Frame) -> Option<RedisError> {
+  if inner.connection.reconnect_on_auth_error {
+    if let Some(auth_error) = parse_redis_auth_error(frame) {
+      return Some(auth_error);
+    }
+  }
+  if let Some(error) = is_clusterdown_error(frame) {
+    return Some(pretty_error(error));
+  }
+
+  check_global_reconnect_errors(inner, frame)
+}
+
+/// Handle an error in the reader task that should end the connection.
+pub fn broadcast_reader_error(inner: &RefCount<RedisClientInner>, server: &Server, error: Option<RedisError>) {
+  _warn!(inner, "Ending reader task from {} due to {:?}", server, error);
+
+  if inner.should_reconnect() {
+    inner.send_reconnect(Some(server.clone()), false, None);
+  }
+  if utils::read_locked(&inner.state) != ClientState::Disconnecting {
+    inner
+      .notifications
+      .broadcast_error(error.unwrap_or(RedisError::new_canceled()));
+  }
+}
+
+#[cfg(not(feature = "replicas"))]
+pub fn broadcast_replica_error(inner: &RefCount<RedisClientInner>, server: &Server, error: Option<RedisError>) {
+  broadcast_reader_error(inner, server, error);
+}
+
+#[cfg(feature = "replicas")]
+pub fn broadcast_replica_error(inner: &RefCount<RedisClientInner>, server: &Server, error: Option<RedisError>) {
+  _warn!(inner, "Ending replica reader task from {} due to {:?}", server, error);
+
+  if inner.should_reconnect() {
+    inner.send_replica_reconnect(server);
+  }
+  if utils::read_locked(&inner.state) != ClientState::Disconnecting {
+    inner
+      .notifications
+      .broadcast_error(error.unwrap_or(RedisError::new_canceled()));
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/router/sentinel.rs.html b/doc/src/fred/router/sentinel.rs.html new file mode 100644 index 00000000..bad428c4 --- /dev/null +++ b/doc/src/fred/router/sentinel.rs.html @@ -0,0 +1,693 @@ +sentinel.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+
#![allow(dead_code)]
+
+use crate::{
+  error::{RedisError, RedisErrorKind},
+  modules::inner::RedisClientInner,
+  protocol::{
+    command::{RedisCommand, RedisCommandKind},
+    connection::{self, RedisTransport, RedisWriter},
+    utils as protocol_utils,
+  },
+  router::{centralized, Connections},
+  runtime::RefCount,
+  types::{RedisValue, Server, ServerConfig},
+  utils,
+};
+use bytes_utils::Str;
+use std::collections::{HashMap, HashSet, VecDeque};
+
+pub static CONFIG: &str = "CONFIG";
+pub static SET: &str = "SET";
+pub static CKQUORUM: &str = "CKQUORUM";
+pub static FLUSHCONFIG: &str = "FLUSHCONFIG";
+pub static FAILOVER: &str = "FAILOVER";
+pub static GET_MASTER_ADDR_BY_NAME: &str = "GET-MASTER-ADDR-BY-NAME";
+pub static INFO_CACHE: &str = "INFO-CACHE";
+pub static MASTERS: &str = "MASTERS";
+pub static MASTER: &str = "MASTER";
+pub static MONITOR: &str = "MONITOR";
+pub static MYID: &str = "MYID";
+pub static PENDING_SCRIPTS: &str = "PENDING-SCRIPTS";
+pub static REMOVE: &str = "REMOVE";
+pub static REPLICAS: &str = "REPLICAS";
+pub static SENTINELS: &str = "SENTINELS";
+pub static SIMULATE_FAILURE: &str = "SIMULATE-FAILURE";
+
+macro_rules! stry (
+  ($expr:expr) => {
+    match $expr {
+      Ok(r) => r,
+      Err(mut e) => {
+        e.change_kind(RedisErrorKind::Sentinel);
+        return Err(e);
+      }
+    }
+  }
+);
+
+fn parse_sentinel_nodes_response(
+  inner: &RefCount<RedisClientInner>,
+  value: RedisValue,
+) -> Result<Vec<Server>, RedisError> {
+  let result_maps: Vec<HashMap<String, String>> = stry!(value.convert());
+  let mut out = Vec::with_capacity(result_maps.len());
+
+  for mut map in result_maps.into_iter() {
+    let ip = match map.remove("ip") {
+      Some(ip) => ip,
+      None => {
+        _warn!(inner, "Failed to read IP for sentinel node.");
+        return Err(RedisError::new(
+          RedisErrorKind::Sentinel,
+          "Failed to read sentinel node IP address.",
+        ));
+      },
+    };
+    let port = match map.get("port") {
+      Some(port) => port.parse::<u16>()?,
+      None => {
+        _warn!(inner, "Failed to read port for sentinel node.");
+        return Err(RedisError::new(
+          RedisErrorKind::Sentinel,
+          "Failed to read sentinel node port.",
+        ));
+      },
+    };
+
+    out.push(Server::new(ip, port));
+  }
+  Ok(out)
+}
+
+fn has_different_sentinel_nodes(old: &[(String, u16)], new: &[(String, u16)]) -> bool {
+  let mut old_set = HashSet::with_capacity(old.len());
+  let mut new_set = HashSet::with_capacity(new.len());
+
+  for (host, port) in old.iter() {
+    old_set.insert((host, port));
+  }
+  for (host, port) in new.iter() {
+    new_set.insert((host, port));
+  }
+
+  old_set.symmetric_difference(&new_set).count() > 0
+}
+
+#[cfg(feature = "sentinel-auth")]
+fn read_sentinel_auth(inner: &RefCount<RedisClientInner>) -> Result<(Option<String>, Option<String>), RedisError> {
+  match inner.config.server {
+    ServerConfig::Sentinel {
+      ref username,
+      ref password,
+      ..
+    } => Ok((username.clone(), password.clone())),
+    _ => Err(RedisError::new(
+      RedisErrorKind::Config,
+      "Expected sentinel server configuration.",
+    )),
+  }
+}
+
+#[cfg(not(feature = "sentinel-auth"))]
+fn read_sentinel_auth(inner: &RefCount<RedisClientInner>) -> Result<(Option<String>, Option<String>), RedisError> {
+  Ok((inner.config.username.clone(), inner.config.password.clone()))
+}
+
+/// Read the `(host, port)` tuples for the known sentinel nodes, and the credentials to use when connecting.
+fn read_sentinel_nodes_and_auth(
+  inner: &RefCount<RedisClientInner>,
+) -> Result<(Vec<Server>, (Option<String>, Option<String>)), RedisError> {
+  let (username, password) = read_sentinel_auth(inner)?;
+  let hosts = match inner.server_state.read().kind.read_sentinel_nodes(&inner.config.server) {
+    Some(hosts) => hosts,
+    None => {
+      return Err(RedisError::new(
+        RedisErrorKind::Sentinel,
+        "Failed to read cached sentinel nodes.",
+      ))
+    },
+  };
+
+  Ok((hosts, (username, password)))
+}
+
+/// Read the set of sentinel nodes via `SENTINEL sentinels`.
+async fn read_sentinels(
+  inner: &RefCount<RedisClientInner>,
+  sentinel: &mut RedisTransport,
+) -> Result<Vec<Server>, RedisError> {
+  let service_name = read_service_name(inner)?;
+
+  let command = RedisCommand::new(RedisCommandKind::Sentinel, vec![
+    static_val!(SENTINELS),
+    service_name.into(),
+  ]);
+  let frame = sentinel.request_response(command, false).await?;
+  let response = stry!(protocol_utils::frame_to_results(frame));
+  _trace!(inner, "Read sentinel `sentinels` response: {:?}", response);
+  let sentinel_nodes = stry!(parse_sentinel_nodes_response(inner, response));
+
+  if sentinel_nodes.is_empty() {
+    _warn!(inner, "Failed to read any known sentinel nodes.")
+  }
+
+  Ok(sentinel_nodes)
+}
+
+/// Connect to any of the sentinel nodes provided on the associated `RedisConfig`.
+async fn connect_to_sentinel(inner: &RefCount<RedisClientInner>) -> Result<RedisTransport, RedisError> {
+  let (hosts, (username, password)) = read_sentinel_nodes_and_auth(inner)?;
+
+  for server in hosts.into_iter() {
+    _debug!(inner, "Connecting to sentinel {}", server);
+    let mut transport = try_or_continue!(connection::create(inner, &server, None).await);
+    try_or_continue!(
+      utils::timeout(
+        transport.authenticate(&inner.id, username.clone(), password.clone(), false),
+        inner.internal_command_timeout()
+      )
+      .await
+    );
+
+    return Ok(transport);
+  }
+
+  Err(RedisError::new(
+    RedisErrorKind::Sentinel,
+    "Failed to connect to all sentinel nodes.",
+  ))
+}
+
+fn read_service_name(inner: &RefCount<RedisClientInner>) -> Result<String, RedisError> {
+  match inner.config.server {
+    ServerConfig::Sentinel { ref service_name, .. } => Ok(service_name.to_owned()),
+    _ => Err(RedisError::new(
+      RedisErrorKind::Sentinel,
+      "Missing sentinel service name.",
+    )),
+  }
+}
+
+/// Read the `(host, port)` tuple for the primary Redis server, as identified by the `SENTINEL
+/// get-master-addr-by-name` command, then return a connection to that node.
+async fn discover_primary_node(
+  inner: &RefCount<RedisClientInner>,
+  sentinel: &mut RedisTransport,
+) -> Result<RedisTransport, RedisError> {
+  let service_name = read_service_name(inner)?;
+  let command = RedisCommand::new(RedisCommandKind::Sentinel, vec![
+    static_val!(GET_MASTER_ADDR_BY_NAME),
+    service_name.into(),
+  ]);
+  let frame = utils::timeout(
+    sentinel.request_response(command, false),
+    inner.internal_command_timeout(),
+  )
+  .await?;
+  let response = stry!(protocol_utils::frame_to_results(frame));
+  let server = if response.is_null() {
+    return Err(RedisError::new(
+      RedisErrorKind::Sentinel,
+      "Missing primary address in response from sentinel node.",
+    ));
+  } else {
+    let (host, port): (Str, u16) = stry!(response.convert());
+    Server {
+      host,
+      port,
+      #[cfg(any(
+        feature = "enable-rustls",
+        feature = "enable-native-tls",
+        feature = "enable-rustls-ring"
+      ))]
+      tls_server_name: None,
+    }
+  };
+
+  let mut transport = stry!(connection::create(inner, &server, None).await);
+  stry!(transport.setup(inner, None).await);
+  Ok(transport)
+}
+
+/// Verify that the Redis server is a primary node and not a replica.
+async fn check_primary_node_role(
+  inner: &RefCount<RedisClientInner>,
+  transport: &mut RedisTransport,
+) -> Result<(), RedisError> {
+  let command = RedisCommand::new(RedisCommandKind::Role, Vec::new());
+  _debug!(inner, "Checking role for redis server at {}", transport.server);
+
+  let frame = stry!(transport.request_response(command, inner.is_resp3()).await);
+  let response = stry!(protocol_utils::frame_to_results(frame));
+
+  if let RedisValue::Array(values) = response {
+    if let Some(first) = values.first() {
+      let is_master = first.as_str().map(|s| s == "master").unwrap_or(false);
+
+      if is_master {
+        Ok(())
+      } else {
+        Err(RedisError::new(
+          RedisErrorKind::Sentinel,
+          format!("Invalid role: {:?}", first.as_str()),
+        ))
+      }
+    } else {
+      Err(RedisError::new(RedisErrorKind::Sentinel, "Invalid role response."))
+    }
+  } else {
+    Err(RedisError::new(
+      RedisErrorKind::Sentinel,
+      "Could not read redis server role.",
+    ))
+  }
+}
+
+/// Update the cached backchannel state with the new connection information, disconnecting the old connection if
+/// needed.
+async fn update_sentinel_backchannel(
+  inner: &RefCount<RedisClientInner>,
+  transport: &RedisTransport,
+) -> Result<(), RedisError> {
+  let mut backchannel = inner.backchannel.write().await;
+  backchannel.check_and_disconnect(inner, Some(&transport.server)).await;
+  backchannel.connection_ids.clear();
+  if let Some(id) = transport.id {
+    backchannel.connection_ids.insert(transport.server.clone(), id);
+  }
+
+  Ok(())
+}
+
+/// Update the cached client and connection state with the latest sentinel state.
+///
+/// This does the following:
+/// * Call `SENTINEL sentinels` on the sentinels
+/// * Store the updated sentinel node list on `inner`.
+/// * Update the primary node on `inner`.
+/// * Update the cached backchannel information.
+/// * Split and store the primary node transport on `writer`.
+async fn update_cached_client_state(
+  inner: &RefCount<RedisClientInner>,
+  writer: &mut Option<RedisWriter>,
+  mut sentinel: RedisTransport,
+  transport: RedisTransport,
+) -> Result<(), RedisError> {
+  let sentinels = read_sentinels(inner, &mut sentinel).await?;
+  inner
+    .server_state
+    .write()
+    .kind
+    .update_sentinel_nodes(&transport.server, sentinels);
+  let _ = update_sentinel_backchannel(inner, &transport).await;
+
+  let (_, _writer) = connection::split(inner, transport, false, centralized::spawn_reader_task)?;
+  *writer = Some(_writer);
+  Ok(())
+}
+
+/// Initialize fresh connections to the server, dropping any old connections and saving in-flight commands on
+/// `buffer`.
+///
+/// <https://redis.io/docs/reference/sentinel-clients/>
+pub async fn initialize_connection(
+  inner: &RefCount<RedisClientInner>,
+  connections: &mut Connections,
+  buffer: &mut VecDeque<RedisCommand>,
+) -> Result<(), RedisError> {
+  _debug!(inner, "Initializing sentinel connection.");
+  let commands = connections.disconnect_all(inner).await;
+  buffer.extend(commands);
+
+  match connections {
+    Connections::Sentinel { writer } => {
+      let mut sentinel = connect_to_sentinel(inner).await?;
+      let mut transport = discover_primary_node(inner, &mut sentinel).await?;
+      let server = transport.server.clone();
+
+      utils::timeout(
+        Box::pin(async {
+          check_primary_node_role(inner, &mut transport).await?;
+          update_cached_client_state(inner, writer, sentinel, transport).await?;
+          Ok::<_, RedisError>(())
+        }),
+        inner.internal_command_timeout(),
+      )
+      .await?;
+
+      inner.notifications.broadcast_reconnect(server);
+      Ok(())
+    },
+    _ => Err(RedisError::new(
+      RedisErrorKind::Config,
+      "Expected sentinel connections.",
+    )),
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/router/transactions.rs.html b/doc/src/fred/router/transactions.rs.html new file mode 100644 index 00000000..838ca373 --- /dev/null +++ b/doc/src/fred/router/transactions.rs.html @@ -0,0 +1,681 @@ +transactions.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  interfaces::Resp3Frame,
+  modules::inner::RedisClientInner,
+  protocol::{
+    command::{ClusterErrorKind, RedisCommand, RedisCommandKind, ResponseSender, RouterReceiver, RouterResponse},
+    responders::ResponseKind,
+  },
+  router::{utils, Router, Written},
+  runtime::RefCount,
+  types::{ClusterHash, Server},
+  utils as client_utils,
+};
+
+/// An internal enum describing the result of an attempt to send a transaction command.
+#[derive(Debug)]
+enum TransactionResponse {
+  /// Retry the entire transaction again after reconnecting or resetting connections.
+  ///
+  /// Returned in response to a write error or a connection closing.
+  Retry(RedisError),
+  /// Send `DISCARD` and retry the entire transaction against the provided server/hash slot.
+  Redirection((ClusterErrorKind, u16, Server)),
+  /// Finish the transaction with the associated response.
+  Finished(Resp3Frame),
+  /// Continue the transaction.
+  ///
+  /// Note: if `abort_on_error` is true the transaction will continue even after errors from the server. If `false`
+  /// the error will instead be returned as a `Result::Err` that ends the transaction.
+  Continue,
+}
+
+/// Write a command in the context of a transaction and process the router response.
+///
+/// Returns the command result policy or a fatal error that should end the transaction.
+async fn write_command(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  server: &Server,
+  command: RedisCommand,
+  abort_on_error: bool,
+  rx: RouterReceiver,
+) -> Result<TransactionResponse, RedisError> {
+  _trace!(
+    inner,
+    "Sending trx command {} ({}) to {}",
+    command.kind.to_str_debug(),
+    command.debug_id(),
+    server
+  );
+
+  let timeout_dur = command.timeout_dur.unwrap_or_else(|| inner.default_command_timeout());
+  let result = match router.write_direct(command, server).await {
+    Written::Error((error, _)) => Err(error),
+    Written::Disconnected((_, _, error)) => Err(error),
+    Written::NotFound(_) => Err(RedisError::new(RedisErrorKind::Cluster, "Connection not found.")),
+    _ => Ok(()),
+  };
+  if let Err(e) = result {
+    // TODO check fail fast and exit early w/o retry here?
+    _debug!(inner, "Error writing trx command: {:?}", e);
+    return Ok(TransactionResponse::Retry(e));
+  }
+
+  match client_utils::timeout(rx, timeout_dur).await? {
+    RouterResponse::Continue => Ok(TransactionResponse::Continue),
+    RouterResponse::Ask((slot, server, _)) => {
+      Ok(TransactionResponse::Redirection((ClusterErrorKind::Ask, slot, server)))
+    },
+    RouterResponse::Moved((slot, server, _)) => Ok(TransactionResponse::Redirection((
+      ClusterErrorKind::Moved,
+      slot,
+      server,
+    ))),
+    RouterResponse::ConnectionClosed((err, _)) => Ok(TransactionResponse::Retry(err)),
+    RouterResponse::TransactionError((err, _)) => {
+      if abort_on_error {
+        Err(err)
+      } else {
+        Ok(TransactionResponse::Continue)
+      }
+    },
+    RouterResponse::TransactionResult(frame) => Ok(TransactionResponse::Finished(frame)),
+  }
+}
+
+/// Send EXEC to the provided server.
+async fn send_exec(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  server: &Server,
+  id: u64,
+) -> Result<TransactionResponse, RedisError> {
+  let mut command = RedisCommand::new(RedisCommandKind::Exec, vec![]);
+  command.can_pipeline = false;
+  command.skip_backpressure = true;
+  command.transaction_id = Some(id);
+  let rx = command.create_router_channel();
+
+  write_command(inner, router, server, command, true, rx).await
+}
+
+/// Send DISCARD to the provided server.
+async fn send_discard(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  server: &Server,
+  id: u64,
+) -> Result<TransactionResponse, RedisError> {
+  let mut command = RedisCommand::new(RedisCommandKind::Discard, vec![]);
+  command.can_pipeline = false;
+  command.skip_backpressure = true;
+  command.transaction_id = Some(id);
+  let rx = command.create_router_channel();
+
+  write_command(inner, router, server, command, true, rx).await
+}
+
+fn update_hash_slot(commands: &mut [RedisCommand], slot: u16) {
+  for command in commands.iter_mut() {
+    command.hasher = ClusterHash::Custom(slot);
+  }
+}
+
+/// Run the transaction, following cluster redirects and reconnecting as needed.
+// this would be a lot cleaner with GATs if we could abstract the inner loops with async closures
+#[allow(unused_mut)]
+pub async fn run(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  mut commands: Vec<RedisCommand>,
+  watched: Option<RedisCommand>,
+  id: u64,
+  abort_on_error: bool,
+  mut tx: ResponseSender,
+) -> Result<(), RedisError> {
+  if commands.is_empty() {
+    let _ = tx.send(Ok(Resp3Frame::Null));
+    return Ok(());
+  }
+  // each of the commands should have the same options
+  let max_attempts = if commands[0].attempts_remaining == 0 {
+    inner.max_command_attempts()
+  } else {
+    commands[0].attempts_remaining
+  };
+  let max_redirections = if commands[0].redirections_remaining == 0 {
+    inner.connection.max_redirections
+  } else {
+    commands[0].redirections_remaining
+  };
+
+  let mut attempted = 0;
+  let mut redirections = 0;
+  'outer: loop {
+    _debug!(inner, "Starting transaction {} (attempted: {})", id, attempted);
+
+    let server = if let Some(server) = commands[0].cluster_node.as_ref() {
+      server.clone()
+    } else {
+      match router.find_connection(&commands[0]) {
+        Some(server) => server.clone(),
+        None => {
+          if inner.config.server.is_clustered() {
+            // optimistically sync the cluster, then fall back to a full reconnect
+            if router.sync_cluster().await.is_err() {
+              utils::delay_cluster_sync(inner).await?;
+              utils::reconnect_with_policy(inner, router).await?
+            }
+          } else {
+            utils::reconnect_with_policy(inner, router).await?
+          };
+
+          continue;
+        },
+      }
+    };
+    let mut idx = 0;
+
+    // send the WATCH command before any of the trx commands
+    if let Some(watch) = watched.as_ref() {
+      let watch = watch.duplicate(ResponseKind::Skip);
+      let rx = watch.create_router_channel();
+
+      _debug!(
+        inner,
+        "Sending WATCH for {} keys in trx {} to {}",
+        watch.args().len(),
+        id,
+        server
+      );
+      match write_command(inner, router, &server, watch, false, rx).await {
+        Ok(TransactionResponse::Continue) => {
+          _debug!(inner, "Successfully sent WATCH command before transaction {}.", id);
+        },
+        Ok(TransactionResponse::Retry(error)) => {
+          _debug!(inner, "Retrying trx {} after WATCH error: {:?}.", id, error);
+
+          attempted += 1;
+          if attempted >= max_attempts {
+            let _ = tx.send(Err(error));
+            return Ok(());
+          } else {
+            utils::reconnect_with_policy(inner, router).await?;
+          }
+
+          continue 'outer;
+        },
+        Ok(TransactionResponse::Redirection((kind, slot, server))) => {
+          redirections += 1;
+          if redirections > max_redirections {
+            let _ = tx.send(Err(RedisError::new(
+              RedisErrorKind::Cluster,
+              "Too many cluster redirections.",
+            )));
+            return Ok(());
+          }
+
+          _debug!(inner, "Recv {} redirection to {} for WATCH in trx {}", kind, server, id);
+          update_hash_slot(&mut commands, slot);
+          utils::delay_cluster_sync(inner).await?;
+          utils::cluster_redirect_with_policy(inner, router, kind, slot, &server).await?;
+          continue 'outer;
+        },
+        Ok(TransactionResponse::Finished(frame)) => {
+          _warn!(inner, "Unexpected trx finished frame after WATCH.");
+          let _ = tx.send(Ok(frame));
+          return Ok(());
+        },
+        Err(error) => {
+          let _ = tx.send(Err(error));
+          return Ok(());
+        },
+      };
+    }
+
+    if attempted > 0 {
+      inner.counters.incr_redelivery_count();
+    }
+    // send each of the commands. the first one is always MULTI
+    'inner: while idx < commands.len() {
+      let command = commands[idx].duplicate(ResponseKind::Skip);
+      let rx = command.create_router_channel();
+
+      match write_command(inner, router, &server, command, abort_on_error, rx).await {
+        Ok(TransactionResponse::Continue) => {
+          idx += 1;
+          continue 'inner;
+        },
+        Ok(TransactionResponse::Retry(error)) => {
+          _debug!(inner, "Retrying trx {} after error: {:?}", id, error);
+          if let Err(e) = send_discard(inner, router, &server, id).await {
+            _warn!(inner, "Error sending DISCARD in trx {}: {:?}", id, e);
+          }
+
+          attempted += 1;
+          if attempted >= max_attempts {
+            let _ = tx.send(Err(error));
+            return Ok(());
+          } else {
+            utils::reconnect_with_policy(inner, router).await?;
+          }
+
+          continue 'outer;
+        },
+        Ok(TransactionResponse::Redirection((kind, slot, server))) => {
+          redirections += 1;
+          if redirections > max_redirections {
+            let _ = tx.send(Err(RedisError::new(
+              RedisErrorKind::Cluster,
+              "Too many cluster redirections.",
+            )));
+            return Ok(());
+          }
+
+          update_hash_slot(&mut commands, slot);
+          if let Err(e) = send_discard(inner, router, &server, id).await {
+            _warn!(inner, "Error sending DISCARD in trx {}: {:?}", id, e);
+          }
+          utils::cluster_redirect_with_policy(inner, router, kind, slot, &server).await?;
+
+          continue 'outer;
+        },
+        Ok(TransactionResponse::Finished(frame)) => {
+          let _ = tx.send(Ok(frame));
+          return Ok(());
+        },
+        Err(error) => {
+          // fatal errors that end the transaction
+          let _ = send_discard(inner, router, &server, id).await;
+          let _ = tx.send(Err(error));
+          return Ok(());
+        },
+      }
+    }
+
+    match send_exec(inner, router, &server, id).await {
+      Ok(TransactionResponse::Finished(frame)) => {
+        let _ = tx.send(Ok(frame));
+        return Ok(());
+      },
+      Ok(TransactionResponse::Retry(error)) => {
+        _debug!(inner, "Retrying trx {} after error: {:?}", id, error);
+        if let Err(e) = send_discard(inner, router, &server, id).await {
+          _warn!(inner, "Error sending DISCARD in trx {}: {:?}", id, e);
+        }
+
+        attempted += 1;
+        if attempted >= max_attempts {
+          let _ = tx.send(Err(error));
+          return Ok(());
+        } else {
+          utils::reconnect_with_policy(inner, router).await?;
+        }
+
+        continue 'outer;
+      },
+      Ok(TransactionResponse::Redirection((kind, slot, dest))) => {
+        // doesn't make sense on EXEC, but return it as an error so it isn't lost
+        let _ = send_discard(inner, router, &server, id).await;
+        let _ = tx.send(Err(RedisError::new(
+          RedisErrorKind::Cluster,
+          format!("{} {} {}", kind, slot, dest),
+        )));
+        return Ok(());
+      },
+      Ok(TransactionResponse::Continue) => {
+        _warn!(inner, "Invalid final response to transaction {}", id);
+        let _ = send_discard(inner, router, &server, id).await;
+        let _ = tx.send(Err(RedisError::new_canceled()));
+        return Ok(());
+      },
+      Err(error) => {
+        let _ = send_discard(inner, router, &server, id).await;
+        let _ = tx.send(Err(error));
+        return Ok(());
+      },
+    };
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/router/types.rs.html b/doc/src/fred/router/types.rs.html new file mode 100644 index 00000000..7070da88 --- /dev/null +++ b/doc/src/fred/router/types.rs.html @@ -0,0 +1,35 @@ +types.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
use crate::protocol::types::Server;
+
+/// Options describing how to change connections in a cluster.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct ClusterChange {
+  pub add:    Vec<Server>,
+  pub remove: Vec<Server>,
+}
+
+impl Default for ClusterChange {
+  fn default() -> Self {
+    ClusterChange {
+      add:    Vec::new(),
+      remove: Vec::new(),
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/router/utils.rs.html b/doc/src/fred/router/utils.rs.html new file mode 100644 index 00000000..88814113 --- /dev/null +++ b/doc/src/fred/router/utils.rs.html @@ -0,0 +1,1207 @@ +utils.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  interfaces,
+  modules::inner::RedisClientInner,
+  protocol::{
+    command::{RedisCommand, RouterCommand, RouterResponse},
+    connection::{RedisWriter, SharedBuffer, SplitStreamKind},
+    responders::ResponseKind,
+    types::*,
+    utils as protocol_utils,
+  },
+  router::{utils, Backpressure, Counters, Router, Written},
+  runtime::{oneshot_channel, sleep, RefCount},
+  types::*,
+  utils as client_utils,
+};
+use futures::TryStreamExt;
+use std::{
+  cmp,
+  time::{Duration, Instant},
+};
+
+#[cfg(feature = "transactions")]
+use crate::protocol::command::ClusterErrorKind;
+
+/// Check the connection state and command flags to determine the backpressure policy to apply, if any.
+pub fn check_backpressure(
+  inner: &RefCount<RedisClientInner>,
+  counters: &Counters,
+  command: &RedisCommand,
+) -> Result<Option<Backpressure>, RedisError> {
+  if command.skip_backpressure {
+    return Ok(None);
+  }
+  let in_flight = client_utils::read_atomic(&counters.in_flight);
+
+  inner.with_perf_config(|perf_config| {
+    // TODO clean this up and write better docs
+    if in_flight as u64 > perf_config.backpressure.max_in_flight_commands {
+      if perf_config.backpressure.disable_auto_backpressure {
+        Err(RedisError::new_backpressure())
+      } else {
+        match perf_config.backpressure.policy {
+          BackpressurePolicy::Drain => Ok(Some(Backpressure::Block)),
+          BackpressurePolicy::Sleep {
+            disable_backpressure_scaling,
+            min_sleep_duration,
+          } => {
+            let duration = if disable_backpressure_scaling {
+              min_sleep_duration
+            } else {
+              Duration::from_millis(cmp::max(min_sleep_duration.as_millis() as u64, in_flight as u64))
+            };
+
+            Ok(Some(Backpressure::Wait(duration)))
+          },
+        }
+      }
+    } else {
+      Ok(None)
+    }
+  })
+}
+
+#[cfg(feature = "partial-tracing")]
+fn set_command_trace(inner: &RefCount<RedisClientInner>, command: &mut RedisCommand) {
+  if inner.should_trace() {
+    crate::trace::set_network_span(inner, command, true);
+  }
+}
+
+#[cfg(not(feature = "partial-tracing"))]
+fn set_command_trace(_inner: &RefCount<RedisClientInner>, _: &mut RedisCommand) {}
+
+/// Prepare the command, updating flags in place.
+///
+/// Returns the RESP frame and whether the socket should be flushed.
+pub fn prepare_command(
+  inner: &RefCount<RedisClientInner>,
+  counters: &Counters,
+  command: &mut RedisCommand,
+) -> Result<(ProtocolFrame, bool), RedisError> {
+  let frame = protocol_utils::encode_frame(inner, command)?;
+
+  // flush the socket under any of the following conditions:
+  // * we don't know of any queued commands following this command
+  // * we've fed up to the max feed count commands already
+  // * the command closes the connection
+  // * the command ends a transaction
+  // * the command does some form of authentication
+  // * the command goes to multiple sockets at once
+  // * the command blocks the router command loop
+  let should_flush = counters.should_send(inner)
+    || command.kind.should_flush()
+    || command.is_all_cluster_nodes()
+    || command.has_router_channel();
+
+  command.network_start = Some(Instant::now());
+  set_command_trace(inner, command);
+
+  Ok((frame, should_flush))
+}
+
+/// Write a command on the provided writer half of a socket.
+pub async fn write_command(
+  inner: &RefCount<RedisClientInner>,
+  writer: &mut RedisWriter,
+  mut command: RedisCommand,
+  force_flush: bool,
+) -> Written {
+  _trace!(
+    inner,
+    "Writing {} ({}). Timed out: {}, Force flush: {}",
+    command.kind.to_str_debug(),
+    command.debug_id(),
+    client_utils::read_bool_atomic(&command.timed_out),
+    force_flush
+  );
+  if client_utils::read_bool_atomic(&command.timed_out) {
+    _debug!(
+      inner,
+      "Ignore writing timed out command: {}",
+      command.kind.to_str_debug()
+    );
+    return Written::Ignore;
+  }
+
+  match check_backpressure(inner, &writer.counters, &command) {
+    Ok(Some(backpressure)) => {
+      _trace!(inner, "Returning backpressure for {}", command.kind.to_str_debug());
+      return Written::Backpressure((command, backpressure));
+    },
+    Err(e) => {
+      // return manual backpressure errors directly to the caller
+      command.finish(inner, Err(e));
+      return Written::Ignore;
+    },
+    _ => {},
+  };
+
+  let (frame, should_flush) = match prepare_command(inner, &writer.counters, &mut command) {
+    Ok((frame, should_flush)) => (frame, should_flush || force_flush),
+    Err(e) => {
+      _warn!(inner, "Frame encoding error for {}", command.kind.to_str_debug());
+      // do not retry commands that trigger frame encoding errors
+      command.finish(inner, Err(e));
+      return Written::Ignore;
+    },
+  };
+
+  _trace!(
+    inner,
+    "Sending command {} ({}) to {}",
+    command.kind.to_str_debug(),
+    command.debug_id(),
+    writer.server
+  );
+  command.write_attempts += 1;
+
+  if !writer.is_working() {
+    let error = RedisError::new(RedisErrorKind::IO, "Connection closed.");
+    _debug!(inner, "Error sending command: {:?}", error);
+    return Written::Disconnected((Some(writer.server.clone()), Some(command), error));
+  }
+
+  let no_incr = command.has_no_responses();
+  writer.push_command(inner, command);
+  if let Err(err) = writer.write_frame(frame, should_flush, no_incr).await {
+    Written::Disconnected((Some(writer.server.clone()), None, err))
+  } else {
+    Written::Sent((writer.server.clone(), should_flush))
+  }
+}
+
+/// Check the shared connection command buffer to see if the oldest command blocks the router task on a
+/// response (not pipelined).
+pub fn check_blocked_router(inner: &RefCount<RedisClientInner>, buffer: &SharedBuffer, error: &Option<RedisError>) {
+  let command = match buffer.pop() {
+    Some(cmd) => cmd,
+    None => return,
+  };
+  if command.has_router_channel() {
+    #[allow(unused_mut)]
+    let mut tx = match command.take_router_tx() {
+      Some(tx) => tx,
+      None => return,
+    };
+    let error = error
+      .clone()
+      .unwrap_or(RedisError::new(RedisErrorKind::IO, "Connection Closed"));
+
+    if let Err(_) = tx.send(RouterResponse::ConnectionClosed((error, command))) {
+      _warn!(inner, "Failed to send router connection closed error.");
+    }
+  } else {
+    // this is safe to rearrange since the connection has closed and we can't guarantee command ordering when
+    // connections close while an entire pipeline is in flight
+    buffer.push(command);
+  }
+}
+
+/// Filter the shared buffer, removing commands that reached the max number of attempts and responding to each caller
+/// with the underlying error.
+pub fn check_final_write_attempt(
+  inner: &RefCount<RedisClientInner>,
+  buffer: &SharedBuffer,
+  error: &Option<RedisError>,
+) {
+  buffer
+    .drain()
+    .into_iter()
+    .filter_map(|command| {
+      if command.should_finish_with_error(inner) {
+        command.finish(
+          inner,
+          Err(
+            error
+              .clone()
+              .unwrap_or(RedisError::new(RedisErrorKind::IO, "Connection Closed")),
+          ),
+        );
+
+        None
+      } else {
+        Some(command)
+      }
+    })
+    .for_each(|command| {
+      buffer.push(command);
+    });
+}
+
+/// Read the next reconnection delay for the client.
+pub fn next_reconnection_delay(inner: &RefCount<RedisClientInner>) -> Result<Duration, RedisError> {
+  inner
+    .policy
+    .write()
+    .as_mut()
+    .and_then(|policy| policy.next_delay())
+    .map(Duration::from_millis)
+    .ok_or_else(|| RedisError::new(RedisErrorKind::Canceled, "Max reconnection attempts reached."))
+}
+
+/// Attempt to reconnect and replay queued commands.
+pub async fn reconnect_once(inner: &RefCount<RedisClientInner>, router: &mut Router) -> Result<(), RedisError> {
+  client_utils::set_client_state(&inner.state, ClientState::Connecting);
+  if let Err(e) = Box::pin(router.connect()).await {
+    _debug!(inner, "Failed reconnecting with error: {:?}", e);
+    client_utils::set_client_state(&inner.state, ClientState::Disconnected);
+    inner.notifications.broadcast_error(e.clone());
+    Err(e)
+  } else {
+    #[cfg(feature = "replicas")]
+    if let Err(e) = router.refresh_replica_routing().await {
+      _warn!(inner, "Error syncing replicas: {:?}", e);
+      if !inner.ignore_replica_reconnect_errors() {
+        client_utils::set_client_state(&inner.state, ClientState::Disconnected);
+        inner.notifications.broadcast_error(e.clone());
+        return Err(e);
+      }
+    }
+    // try to flush any previously in-flight commands
+    router.retry_buffer().await;
+
+    client_utils::set_client_state(&inner.state, ClientState::Connected);
+    inner.notifications.broadcast_connect(Ok(()));
+    inner.reset_reconnection_attempts();
+    Ok(())
+  }
+}
+
+/// Reconnect to the server(s) until the max reconnect policy attempts are reached.
+///
+/// Errors from this function should end the connection task.
+pub async fn reconnect_with_policy(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+) -> Result<(), RedisError> {
+  let mut delay = utils::next_reconnection_delay(inner)?;
+
+  loop {
+    if !delay.is_zero() {
+      _debug!(inner, "Sleeping for {} ms.", delay.as_millis());
+      inner.wait_with_interrupt(delay).await?;
+    }
+
+    if let Err(e) = reconnect_once(inner, router).await {
+      if e.should_not_reconnect() {
+        return Err(e);
+      }
+
+      delay = match next_reconnection_delay(inner) {
+        Ok(delay) => delay,
+        Err(_) => return Err(e),
+      };
+
+      continue;
+    } else {
+      break;
+    }
+  }
+
+  Ok(())
+}
+
+/// Attempt to follow a cluster redirect, reconnecting as needed until the max reconnections attempts is reached.
+#[cfg(feature = "transactions")]
+pub async fn cluster_redirect_with_policy(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  kind: ClusterErrorKind,
+  slot: u16,
+  server: &Server,
+) -> Result<(), RedisError> {
+  let mut delay = inner.connection.cluster_cache_update_delay;
+
+  loop {
+    if !delay.is_zero() {
+      _debug!(inner, "Sleeping for {} ms.", delay.as_millis());
+      inner.wait_with_interrupt(delay).await?;
+    }
+
+    if let Err(e) = router.cluster_redirection(&kind, slot, server).await {
+      delay = next_reconnection_delay(inner).map_err(|_| e)?;
+
+      continue;
+    } else {
+      break;
+    }
+  }
+
+  Ok(())
+}
+
+/// Repeatedly try to send `ASKING` to the provided server, reconnecting as needed.f
+///
+/// Errors from this function should end the connection task.
+pub async fn send_asking_with_policy(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  server: &Server,
+  slot: u16,
+) -> Result<(), RedisError> {
+  let mut delay = inner.connection.cluster_cache_update_delay;
+
+  loop {
+    if !delay.is_zero() {
+      _debug!(inner, "Sleeping for {} ms.", delay.as_millis());
+      inner.wait_with_interrupt(delay).await?;
+    }
+
+    if !router.connections.has_server_connection(server) {
+      if let Err(e) = router.sync_cluster().await {
+        _debug!(inner, "Error syncing cluster before ASKING: {:?}", e);
+        delay = utils::next_reconnection_delay(inner)?;
+        continue;
+      }
+    }
+
+    let mut command = RedisCommand::new_asking(slot);
+    let (tx, rx) = oneshot_channel();
+    command.skip_backpressure = true;
+    command.response = ResponseKind::Respond(Some(tx));
+
+    let result = match router.write_direct(command, server).await {
+      Written::Error((error, _)) => Err(error),
+      Written::Disconnected((_, _, error)) => Err(error),
+      Written::NotFound(_) => Err(RedisError::new(RedisErrorKind::Cluster, "Connection not found.")),
+      _ => Ok(()),
+    };
+
+    if let Err(error) = result {
+      if error.should_not_reconnect() {
+        break;
+      } else if let Err(_) = reconnect_once(inner, router).await {
+        delay = utils::next_reconnection_delay(inner)?;
+        continue;
+      } else {
+        delay = Duration::from_millis(0);
+        continue;
+      }
+    } else {
+      match client_utils::timeout(rx, inner.internal_command_timeout()).await {
+        Ok(Err(e)) => {
+          // error writing the command
+          _debug!(inner, "Reconnect once after error from ASKING: {:?}", e);
+          if let Err(_) = reconnect_once(inner, router).await {
+            delay = utils::next_reconnection_delay(inner)?;
+            continue;
+          } else {
+            delay = Duration::from_millis(0);
+            continue;
+          }
+        },
+        Err(e) => {
+          // command was dropped due to connection closing
+          _debug!(inner, "Reconnect once after rx error from ASKING: {:?}", e);
+          if let Err(_) = reconnect_once(inner, router).await {
+            delay = utils::next_reconnection_delay(inner)?;
+            continue;
+          } else {
+            delay = Duration::from_millis(0);
+            continue;
+          }
+        },
+        _ => break,
+      }
+    }
+  }
+
+  inner.reset_reconnection_attempts();
+  Ok(())
+}
+
+#[cfg(feature = "replicas")]
+async fn sync_cluster_replicas(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  reset: bool,
+) -> Result<(), RedisError> {
+  if reset {
+    router.replicas.clear_connections(inner).await?;
+  }
+
+  if inner.config.server.is_clustered() {
+    router.sync_cluster().await
+  } else {
+    router.sync_replicas().await
+  }
+}
+
+/// Repeatedly try to sync the cluster state, reconnecting as needed until the max reconnection attempts is reached.
+#[cfg(feature = "replicas")]
+pub async fn sync_replicas_with_policy(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+  reset: bool,
+) -> Result<(), RedisError> {
+  let mut delay = Duration::from_millis(0);
+
+  loop {
+    if !delay.is_zero() {
+      _debug!(inner, "Sleeping for {} ms.", delay.as_millis());
+      inner.wait_with_interrupt(delay).await?;
+    }
+
+    if let Err(e) = sync_cluster_replicas(inner, router, reset).await {
+      _warn!(inner, "Error syncing replicas: {:?}", e);
+
+      if e.should_not_reconnect() {
+        break;
+      } else {
+        // return the underlying error on the last attempt
+        delay = match utils::next_reconnection_delay(inner) {
+          Ok(delay) => delay,
+          Err(_) => return Err(e),
+        };
+
+        continue;
+      }
+    } else {
+      break;
+    }
+  }
+
+  Ok(())
+}
+
+/// Wait for `inner.connection.cluster_cache_update_delay`.
+pub async fn delay_cluster_sync(inner: &RefCount<RedisClientInner>) -> Result<(), RedisError> {
+  if inner.config.server.is_clustered() && !inner.connection.cluster_cache_update_delay.is_zero() {
+    inner
+      .wait_with_interrupt(inner.connection.cluster_cache_update_delay)
+      .await
+  } else {
+    Ok(())
+  }
+}
+
+/// Repeatedly try to sync the cluster state, reconnecting as needed until the max reconnection attempts is reached.
+///
+/// Errors from this function should end the connection task.
+pub async fn sync_cluster_with_policy(
+  inner: &RefCount<RedisClientInner>,
+  router: &mut Router,
+) -> Result<(), RedisError> {
+  let mut delay = Duration::from_millis(0);
+
+  loop {
+    if !delay.is_zero() {
+      _debug!(inner, "Sleeping for {} ms.", delay.as_millis());
+      inner.wait_with_interrupt(delay).await?;
+    }
+
+    if let Err(e) = router.sync_cluster().await {
+      _warn!(inner, "Error syncing cluster after redirect: {:?}", e);
+
+      if e.should_not_reconnect() {
+        break;
+      } else {
+        // return the underlying error on the last attempt
+        delay = match utils::next_reconnection_delay(inner) {
+          Ok(delay) => delay,
+          Err(_) => return Err(e),
+        };
+
+        continue;
+      }
+    } else {
+      break;
+    }
+  }
+
+  Ok(())
+}
+
+pub fn defer_reconnect(inner: &RefCount<RedisClientInner>) {
+  if inner.config.server.is_clustered() {
+    let (tx, _) = oneshot_channel();
+    let cmd = RouterCommand::SyncCluster { tx };
+    if let Err(_) = interfaces::send_to_router(inner, cmd) {
+      _warn!(inner, "Failed to send deferred cluster sync.")
+    }
+  } else {
+    let cmd = RouterCommand::Reconnect {
+      server:                               None,
+      tx:                                   None,
+      force:                                false,
+      #[cfg(feature = "replicas")]
+      replica:                              false,
+    };
+    if let Err(_) = interfaces::send_to_router(inner, cmd) {
+      _warn!(inner, "Failed to send deferred cluster sync.")
+    }
+  }
+}
+
+/// Attempt to read the next frame from the reader half of a connection.
+pub async fn next_frame(
+  inner: &RefCount<RedisClientInner>,
+  conn: &mut SplitStreamKind,
+  server: &Server,
+  buffer: &SharedBuffer,
+) -> Result<Option<ProtocolFrame>, RedisError> {
+  if let Some(ref max_resp_latency) = inner.connection.unresponsive.max_timeout {
+    // These shenanigans were implemented in an attempt to strike a balance between a few recent changes.
+    //
+    // The entire request-response path can be lock-free if we use crossbeam-queue types under the shared buffer
+    // between socket halves, but these types do not support `peek` or `push_front`. Unfortunately this really limits
+    // or prevents most forms of conditional `pop_front` use cases. There are 3-4 places in the code where this
+    // matters, and this is one of them.
+    //
+    // The `UnresponsiveConfig` interface implements a heuristic where callers can express that a connection should be
+    // considered unresponsive if a command waits too long for a response. Before switching to crossbeam types we used
+    // a `Mutex<VecDeque>` container which made this scenario easier to implement, but with crossbeam types it's more
+    // complicated.
+    //
+    // The approach here implements a ~~hack~~ heuristic where we measure the time since first noticing a new
+    // frame in the shared buffer from the reader task's perspective. This only works because we use `Stream::next`
+    // which is noted to be cancellation-safe in the tokio::select! docs. With this implementation the worst case
+    // error margin is an extra `interval`.
+
+    let mut last_frame_sent: Option<Instant> = None;
+    'outer: loop {
+      tokio::select! {
+        // prefer polling the connection first
+        biased;
+        frame = conn.try_next() => return frame,
+
+        // repeatedly check the duration since we first noticed a pending frame
+        _ = sleep(inner.connection.unresponsive.interval) => {
+          _trace!(inner, "Checking unresponsive connection to {}", server);
+
+          // continue early if the buffer is empty or we're waiting on a blocking command. this isn't ideal, but
+          // this strategy just doesn't work well with blocking commands.
+          let buffer_len = buffer.len();
+          if buffer_len == 0 || buffer.is_blocked() {
+            last_frame_sent = None;
+            continue 'outer;
+          } else if buffer_len > 0 && last_frame_sent.is_none() {
+            _trace!(inner, "Observed new request frame in unresponsive loop");
+            last_frame_sent = Some(Instant::now());
+          }
+
+          if let Some(ref last_frame_sent) = last_frame_sent {
+            let latency = Instant::now().saturating_duration_since(*last_frame_sent);
+            if latency > *max_resp_latency {
+              _warn!(inner, "Unresponsive connection to {} after {:?}", server, latency);
+              inner.notifications.broadcast_unresponsive(server.clone());
+              return Err(RedisError::new(RedisErrorKind::IO, "Unresponsive connection."))
+            }
+          }
+        },
+      }
+    }
+  } else {
+    _trace!(inner, "Skip waiting on interrupt rx.");
+    conn.try_next().await
+  }
+}
+
+#[cfg(test)]
+mod tests {}
+
\ No newline at end of file diff --git a/doc/src/fred/trace/disabled.rs.html b/doc/src/fred/trace/disabled.rs.html new file mode 100644 index 00000000..3d6820a4 --- /dev/null +++ b/doc/src/fred/trace/disabled.rs.html @@ -0,0 +1,69 @@ +disabled.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+
#![allow(dead_code)]
+
+#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))]
+use crate::modules::inner::RedisClientInner;
+#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))]
+use crate::protocol::command::RedisCommand;
+#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))]
+use crate::runtime::RefCount;
+#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))]
+use redis_protocol::resp3::types::BytesFrame as Frame;
+
+/// Fake span for mocking tracing functions.
+#[cfg(not(feature = "full-tracing"))]
+pub struct Span {}
+
+#[cfg(not(feature = "full-tracing"))]
+impl Span {
+  pub fn enter(&self) {}
+
+  pub fn record<Q: ?Sized, V: ?Sized>(&self, _field: &Q, _value: &V) -> &Self {
+    self
+  }
+}
+
+#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))]
+pub fn set_network_span(_inner: &RefCount<RedisClientInner>, _command: &mut RedisCommand, _flush: bool) {}
+
+#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))]
+pub fn create_pubsub_span(_inner: &RefCount<RedisClientInner>, _frame: &Frame) -> Option<Span> {
+  Some(Span {})
+}
+
+#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))]
+pub fn backpressure_event(_cmd: &RedisCommand, _: Option<u128>) {}
+
\ No newline at end of file diff --git a/doc/src/fred/trace/enabled.rs.html b/doc/src/fred/trace/enabled.rs.html new file mode 100644 index 00000000..65cc44dd --- /dev/null +++ b/doc/src/fred/trace/enabled.rs.html @@ -0,0 +1,243 @@ +enabled.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+
use crate::{modules::inner::RedisClientInner, protocol::command::RedisCommand, runtime::RefCount};
+use redis_protocol::resp3::types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame};
+use std::{fmt, ops::Deref};
+pub use tracing::span::Span;
+use tracing::{event, field::Empty, Id as TraceId, Level};
+
+#[cfg(not(feature = "full-tracing"))]
+use crate::trace::disabled::Span as FakeSpan;
+
+/// Struct for storing spans used by the client when sending a command.
+pub struct CommandTraces {
+  pub cmd:     Option<Span>,
+  pub network: Option<Span>,
+  #[cfg(feature = "full-tracing")]
+  pub queued:  Option<Span>,
+  #[cfg(not(feature = "full-tracing"))]
+  pub queued:  Option<FakeSpan>,
+}
+
+/// Enter the network span when the command is dropped after receiving a response.
+impl Drop for CommandTraces {
+  fn drop(&mut self) {
+    if let Some(span) = self.network.take() {
+      let _enter = span.enter();
+    }
+  }
+}
+
+impl Default for CommandTraces {
+  fn default() -> Self {
+    CommandTraces {
+      cmd:     None,
+      queued:  None,
+      network: None,
+    }
+  }
+}
+
+impl fmt::Debug for CommandTraces {
+  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    write!(f, "[Command Traces]")
+  }
+}
+
+pub fn set_network_span(inner: &RefCount<RedisClientInner>, command: &mut RedisCommand, flush: bool) {
+  trace!("Setting network span from command {}", command.debug_id());
+  let span = fspan!(command, inner.tracing_span_level(), "fred.rtt", "cmd.flush" = flush);
+  span.in_scope(|| {});
+  command.traces.network = Some(span);
+}
+
+pub fn record_response_size(span: &Span, frame: &Resp3Frame) {
+  #[allow(clippy::needless_borrows_for_generic_args)]
+  span.record("cmd.res", &frame.encode_len());
+}
+
+pub fn create_command_span(inner: &RefCount<RedisClientInner>) -> Span {
+  span_lvl!(
+    inner.tracing_span_level(),
+    "fred.command",
+    module = "fred",
+    "client.id" = &inner.id.deref(),
+    "cmd.name" = Empty,
+    "cmd.req" = Empty,
+    "cmd.res" = Empty
+  )
+}
+
+#[cfg(feature = "full-tracing")]
+pub fn create_args_span(parent: Option<TraceId>, inner: &RefCount<RedisClientInner>) -> Span {
+  span_lvl!(inner.full_tracing_span_level(), parent: parent, "fred.prepare", "cmd.args" = Empty)
+}
+
+#[cfg(not(feature = "full-tracing"))]
+pub fn create_args_span(_parent: Option<TraceId>, _inner: &RefCount<RedisClientInner>) -> FakeSpan {
+  FakeSpan {}
+}
+
+#[cfg(feature = "full-tracing")]
+pub fn create_queued_span(parent: Option<TraceId>, inner: &RefCount<RedisClientInner>) -> Span {
+  let buf_len = inner.counters.read_cmd_buffer_len();
+  span_lvl!(inner.full_tracing_span_level(), parent: parent, "fred.queued", buf_len)
+}
+
+#[cfg(not(feature = "full-tracing"))]
+pub fn create_queued_span(_parent: Option<TraceId>, _inner: &RefCount<RedisClientInner>) -> FakeSpan {
+  FakeSpan {}
+}
+
+#[cfg(feature = "full-tracing")]
+pub fn create_pubsub_span(inner: &RefCount<RedisClientInner>, frame: &Resp3Frame) -> Option<Span> {
+  if inner.should_trace() {
+    let span = span_lvl!(
+      inner.full_tracing_span_level(),
+      parent: None,
+      "fred.pubsub",
+      module = "fred",
+      "client.id" = &inner.id.deref(),
+      "cmd.res" = &frame.encode_len(),
+      "msg.channel" = Empty
+    );
+
+    Some(span)
+  } else {
+    None
+  }
+}
+
+#[cfg(not(feature = "full-tracing"))]
+pub fn create_pubsub_span(_inner: &RefCount<RedisClientInner>, _frame: &Resp3Frame) -> Option<FakeSpan> {
+  Some(FakeSpan {})
+}
+
+pub fn backpressure_event(cmd: &RedisCommand, duration: Option<u128>) {
+  let id = cmd.traces.cmd.as_ref().and_then(|c| c.id());
+  if let Some(duration) = duration {
+    event!(parent: id, Level::INFO, "fred.backpressure duration={}", duration);
+  } else {
+    event!(parent: id, Level::INFO, "fred.backpressure drain");
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/trace/mod.rs.html b/doc/src/fred/trace/mod.rs.html new file mode 100644 index 00000000..68b4dd5e --- /dev/null +++ b/doc/src/fred/trace/mod.rs.html @@ -0,0 +1,21 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
#![doc = include_str!("./README.md")]
+
+pub mod disabled;
+#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))]
+mod enabled;
+
+#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))]
+pub use disabled::*;
+#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))]
+pub use enabled::*;
+
\ No newline at end of file diff --git a/doc/src/fred/types/args.rs.html b/doc/src/fred/types/args.rs.html new file mode 100644 index 00000000..3d19d252 --- /dev/null +++ b/doc/src/fred/types/args.rs.html @@ -0,0 +1,3409 @@ +args.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
+1265
+1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+1274
+1275
+1276
+1277
+1278
+1279
+1280
+1281
+1282
+1283
+1284
+1285
+1286
+1287
+1288
+1289
+1290
+1291
+1292
+1293
+1294
+1295
+1296
+1297
+1298
+1299
+1300
+1301
+1302
+1303
+1304
+1305
+1306
+1307
+1308
+1309
+1310
+1311
+1312
+1313
+1314
+1315
+1316
+1317
+1318
+1319
+1320
+1321
+1322
+1323
+1324
+1325
+1326
+1327
+1328
+1329
+1330
+1331
+1332
+1333
+1334
+1335
+1336
+1337
+1338
+1339
+1340
+1341
+1342
+1343
+1344
+1345
+1346
+1347
+1348
+1349
+1350
+1351
+1352
+1353
+1354
+1355
+1356
+1357
+1358
+1359
+1360
+1361
+1362
+1363
+1364
+1365
+1366
+1367
+1368
+1369
+1370
+1371
+1372
+1373
+1374
+1375
+1376
+1377
+1378
+1379
+1380
+1381
+1382
+1383
+1384
+1385
+1386
+1387
+1388
+1389
+1390
+1391
+1392
+1393
+1394
+1395
+1396
+1397
+1398
+1399
+1400
+1401
+1402
+1403
+1404
+1405
+1406
+1407
+1408
+1409
+1410
+1411
+1412
+1413
+1414
+1415
+1416
+1417
+1418
+1419
+1420
+1421
+1422
+1423
+1424
+1425
+1426
+1427
+1428
+1429
+1430
+1431
+1432
+1433
+1434
+1435
+1436
+1437
+1438
+1439
+1440
+1441
+1442
+1443
+1444
+1445
+1446
+1447
+1448
+1449
+1450
+1451
+1452
+1453
+1454
+1455
+1456
+1457
+1458
+1459
+1460
+1461
+1462
+1463
+1464
+1465
+1466
+1467
+1468
+1469
+1470
+1471
+1472
+1473
+1474
+1475
+1476
+1477
+1478
+1479
+1480
+1481
+1482
+1483
+1484
+1485
+1486
+1487
+1488
+1489
+1490
+1491
+1492
+1493
+1494
+1495
+1496
+1497
+1498
+1499
+1500
+1501
+1502
+1503
+1504
+1505
+1506
+1507
+1508
+1509
+1510
+1511
+1512
+1513
+1514
+1515
+1516
+1517
+1518
+1519
+1520
+1521
+1522
+1523
+1524
+1525
+1526
+1527
+1528
+1529
+1530
+1531
+1532
+1533
+1534
+1535
+1536
+1537
+1538
+1539
+1540
+1541
+1542
+1543
+1544
+1545
+1546
+1547
+1548
+1549
+1550
+1551
+1552
+1553
+1554
+1555
+1556
+1557
+1558
+1559
+1560
+1561
+1562
+1563
+1564
+1565
+1566
+1567
+1568
+1569
+1570
+1571
+1572
+1573
+1574
+1575
+1576
+1577
+1578
+1579
+1580
+1581
+1582
+1583
+1584
+1585
+1586
+1587
+1588
+1589
+1590
+1591
+1592
+1593
+1594
+1595
+1596
+1597
+1598
+1599
+1600
+1601
+1602
+1603
+1604
+1605
+1606
+1607
+1608
+1609
+1610
+1611
+1612
+1613
+1614
+1615
+1616
+1617
+1618
+1619
+1620
+1621
+1622
+1623
+1624
+1625
+1626
+1627
+1628
+1629
+1630
+1631
+1632
+1633
+1634
+1635
+1636
+1637
+1638
+1639
+1640
+1641
+1642
+1643
+1644
+1645
+1646
+1647
+1648
+1649
+1650
+1651
+1652
+1653
+1654
+1655
+1656
+1657
+1658
+1659
+1660
+1661
+1662
+1663
+1664
+1665
+1666
+1667
+1668
+1669
+1670
+1671
+1672
+1673
+1674
+1675
+1676
+1677
+1678
+1679
+1680
+1681
+1682
+1683
+1684
+1685
+1686
+1687
+1688
+1689
+1690
+1691
+1692
+1693
+1694
+1695
+1696
+1697
+1698
+1699
+1700
+1701
+1702
+1703
+1704
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  interfaces::{ClientLike, Resp3Frame},
+  protocol::{connection::OK, utils as protocol_utils},
+  types::{FromRedis, FromRedisKey, Server, QUEUED},
+  utils,
+};
+use bytes::Bytes;
+use bytes_utils::Str;
+use float_cmp::approx_eq;
+use redis_protocol::resp2::types::NULL;
+use std::{
+  borrow::Cow,
+  collections::{BTreeMap, HashMap, HashSet, VecDeque},
+  convert::{TryFrom, TryInto},
+  fmt,
+  hash::{Hash, Hasher},
+  iter::FromIterator,
+  mem,
+  ops::{Deref, DerefMut},
+  str,
+};
+
+#[cfg(feature = "i-scripts")]
+use crate::types::Function;
+#[cfg(feature = "i-geo")]
+use crate::types::{GeoPosition, GeoRadiusInfo};
+#[cfg(feature = "i-streams")]
+use crate::types::{XReadResponse, XReadValue};
+#[cfg(feature = "serde-json")]
+use serde_json::Value;
+
+static TRUE_STR: Str = utils::static_str("true");
+static FALSE_STR: Str = utils::static_str("false");
+
+macro_rules! impl_string_or_number(
+    ($t:ty) => {
+        impl From<$t> for StringOrNumber {
+            fn from(val: $t) -> Self {
+                StringOrNumber::Number(val as i64)
+            }
+        }
+    }
+);
+
+macro_rules! impl_from_str_for_redis_key(
+    ($t:ty) => {
+        impl From<$t> for RedisKey {
+            fn from(val: $t) -> Self {
+                RedisKey { key: val.to_string().into() }
+            }
+        }
+    }
+);
+
+/// An argument representing a string or number.
+#[derive(Clone, Debug)]
+pub enum StringOrNumber {
+  String(Str),
+  Number(i64),
+  Double(f64),
+}
+
+impl PartialEq for StringOrNumber {
+  fn eq(&self, other: &Self) -> bool {
+    match *self {
+      StringOrNumber::String(ref s) => match *other {
+        StringOrNumber::String(ref _s) => s == _s,
+        _ => false,
+      },
+      StringOrNumber::Number(ref i) => match *other {
+        StringOrNumber::Number(ref _i) => *i == *_i,
+        _ => false,
+      },
+      StringOrNumber::Double(ref d) => match *other {
+        StringOrNumber::Double(ref _d) => utils::f64_eq(*d, *_d),
+        _ => false,
+      },
+    }
+  }
+}
+
+impl Eq for StringOrNumber {}
+
+impl StringOrNumber {
+  /// An optimized way to convert from `&'static str` that avoids copying or moving the underlying bytes.
+  pub fn from_static_str(s: &'static str) -> Self {
+    StringOrNumber::String(utils::static_str(s))
+  }
+
+  #[cfg(feature = "i-streams")]
+  pub(crate) fn into_arg(self) -> RedisValue {
+    match self {
+      StringOrNumber::String(s) => RedisValue::String(s),
+      StringOrNumber::Number(n) => RedisValue::Integer(n),
+      StringOrNumber::Double(f) => RedisValue::Double(f),
+    }
+  }
+}
+
+impl TryFrom<RedisValue> for StringOrNumber {
+  type Error = RedisError;
+
+  fn try_from(value: RedisValue) -> Result<Self, Self::Error> {
+    let val = match value {
+      RedisValue::String(s) => StringOrNumber::String(s),
+      RedisValue::Integer(i) => StringOrNumber::Number(i),
+      RedisValue::Double(f) => StringOrNumber::Double(f),
+      RedisValue::Bytes(b) => StringOrNumber::String(Str::from_inner(b)?),
+      _ => return Err(RedisError::new(RedisErrorKind::InvalidArgument, "")),
+    };
+
+    Ok(val)
+  }
+}
+
+impl<'a> From<&'a str> for StringOrNumber {
+  fn from(s: &'a str) -> Self {
+    StringOrNumber::String(s.into())
+  }
+}
+
+impl From<String> for StringOrNumber {
+  fn from(s: String) -> Self {
+    StringOrNumber::String(s.into())
+  }
+}
+
+impl From<Str> for StringOrNumber {
+  fn from(s: Str) -> Self {
+    StringOrNumber::String(s)
+  }
+}
+
+impl_string_or_number!(i8);
+impl_string_or_number!(i16);
+impl_string_or_number!(i32);
+impl_string_or_number!(i64);
+impl_string_or_number!(isize);
+impl_string_or_number!(u8);
+impl_string_or_number!(u16);
+impl_string_or_number!(u32);
+impl_string_or_number!(u64);
+impl_string_or_number!(usize);
+
+impl From<f32> for StringOrNumber {
+  fn from(f: f32) -> Self {
+    StringOrNumber::Double(f as f64)
+  }
+}
+
+impl From<f64> for StringOrNumber {
+  fn from(f: f64) -> Self {
+    StringOrNumber::Double(f)
+  }
+}
+
+/// A key in Redis.
+#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct RedisKey {
+  key: Bytes,
+}
+
+impl RedisKey {
+  /// Create a new `RedisKey` from static bytes without copying.
+  pub const fn from_static(b: &'static [u8]) -> Self {
+    RedisKey {
+      key: Bytes::from_static(b),
+    }
+  }
+
+  /// Create a new `RedisKey` from a `&'static str` without copying.
+  pub const fn from_static_str(b: &'static str) -> Self {
+    RedisKey {
+      key: Bytes::from_static(b.as_bytes()),
+    }
+  }
+
+  /// Read the key as a str slice if it can be parsed as a UTF8 string.
+  pub fn as_str(&self) -> Option<&str> {
+    str::from_utf8(&self.key).ok()
+  }
+
+  /// Read the key as a byte slice.
+  pub fn as_bytes(&self) -> &[u8] {
+    &self.key
+  }
+
+  /// Read the inner `Bytes` struct.
+  pub fn inner(&self) -> &Bytes {
+    &self.key
+  }
+
+  /// Read the key as a lossy UTF8 string with `String::from_utf8_lossy`.
+  pub fn as_str_lossy(&self) -> Cow<str> {
+    String::from_utf8_lossy(&self.key)
+  }
+
+  /// Convert the key to a UTF8 string, if possible.
+  pub fn into_string(self) -> Option<String> {
+    String::from_utf8(self.key.to_vec()).ok()
+  }
+
+  /// Read the inner bytes making up the key.
+  pub fn into_bytes(self) -> Bytes {
+    self.key
+  }
+
+  /// Parse and return the key as a `Str` without copying the inner contents.
+  pub fn as_bytes_str(&self) -> Option<Str> {
+    Str::from_inner(self.key.clone()).ok()
+  }
+
+  /// Hash the key to find the associated cluster [hash slot](https://redis.io/topics/cluster-spec#keys-distribution-model).
+  pub fn cluster_hash(&self) -> u16 {
+    redis_protocol::redis_keyslot(&self.key)
+  }
+
+  /// Read the `host:port` of the cluster node that owns the key if the client is clustered and the cluster state is
+  /// known.
+  pub fn cluster_owner<C>(&self, client: &C) -> Option<Server>
+  where
+    C: ClientLike,
+  {
+    if client.is_clustered() {
+      let hash_slot = self.cluster_hash();
+      client
+        .inner()
+        .with_cluster_state(|state| Ok(state.get_server(hash_slot).cloned()))
+        .ok()
+        .and_then(|server| server)
+    } else {
+      None
+    }
+  }
+
+  /// Replace this key with an empty byte array, returning the bytes from the original key.
+  pub fn take(&mut self) -> Bytes {
+    self.key.split_to(self.key.len())
+  }
+
+  /// Attempt to convert the key to any type that implements [FromRedisKey](crate::types::FromRedisKey).
+  ///
+  /// See the [RedisValue::convert](crate::types::RedisValue::convert) documentation for more information.
+  pub fn convert<K>(self) -> Result<K, RedisError>
+  where
+    K: FromRedisKey,
+  {
+    K::from_key(self)
+  }
+}
+
+impl TryFrom<RedisValue> for RedisKey {
+  type Error = RedisError;
+
+  fn try_from(value: RedisValue) -> Result<Self, Self::Error> {
+    let val = match value {
+      RedisValue::String(s) => RedisKey { key: s.into_inner() },
+      RedisValue::Integer(i) => RedisKey {
+        key: i.to_string().into(),
+      },
+      RedisValue::Double(f) => RedisKey {
+        key: f.to_string().into(),
+      },
+      RedisValue::Bytes(b) => RedisKey { key: b },
+      RedisValue::Boolean(b) => match b {
+        true => RedisKey {
+          key: TRUE_STR.clone().into_inner(),
+        },
+        false => RedisKey {
+          key: FALSE_STR.clone().into_inner(),
+        },
+      },
+      RedisValue::Queued => utils::static_str(QUEUED).into(),
+      _ => {
+        return Err(RedisError::new(
+          RedisErrorKind::InvalidArgument,
+          "Cannot convert to key.",
+        ))
+      },
+    };
+
+    Ok(val)
+  }
+}
+
+impl From<Bytes> for RedisKey {
+  fn from(b: Bytes) -> Self {
+    RedisKey { key: b }
+  }
+}
+
+impl From<Box<[u8]>> for RedisKey {
+  fn from(b: Box<[u8]>) -> Self {
+    RedisKey { key: b.into() }
+  }
+}
+
+impl<'a> From<&'a [u8]> for RedisKey {
+  fn from(b: &'a [u8]) -> Self {
+    RedisKey { key: b.to_vec().into() }
+  }
+}
+
+impl From<String> for RedisKey {
+  fn from(s: String) -> Self {
+    RedisKey { key: s.into() }
+  }
+}
+
+impl From<&str> for RedisKey {
+  fn from(s: &str) -> Self {
+    RedisKey {
+      key: s.as_bytes().to_vec().into(),
+    }
+  }
+}
+
+impl From<&String> for RedisKey {
+  fn from(s: &String) -> Self {
+    RedisKey { key: s.clone().into() }
+  }
+}
+
+impl From<Str> for RedisKey {
+  fn from(s: Str) -> Self {
+    RedisKey { key: s.into_inner() }
+  }
+}
+
+impl From<&Str> for RedisKey {
+  fn from(s: &Str) -> Self {
+    RedisKey { key: s.inner().clone() }
+  }
+}
+
+impl From<&RedisKey> for RedisKey {
+  fn from(k: &RedisKey) -> RedisKey {
+    k.clone()
+  }
+}
+
+impl From<bool> for RedisKey {
+  fn from(b: bool) -> Self {
+    match b {
+      true => RedisKey::from_static_str("true"),
+      false => RedisKey::from_static_str("false"),
+    }
+  }
+}
+
+impl_from_str_for_redis_key!(u8);
+impl_from_str_for_redis_key!(u16);
+impl_from_str_for_redis_key!(u32);
+impl_from_str_for_redis_key!(u64);
+impl_from_str_for_redis_key!(u128);
+impl_from_str_for_redis_key!(usize);
+impl_from_str_for_redis_key!(i8);
+impl_from_str_for_redis_key!(i16);
+impl_from_str_for_redis_key!(i32);
+impl_from_str_for_redis_key!(i64);
+impl_from_str_for_redis_key!(i128);
+impl_from_str_for_redis_key!(isize);
+impl_from_str_for_redis_key!(f32);
+impl_from_str_for_redis_key!(f64);
+
+/// A map of `(RedisKey, RedisValue)` pairs.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct RedisMap {
+  pub(crate) inner: HashMap<RedisKey, RedisValue>,
+}
+
+impl RedisMap {
+  /// Create a new empty map.
+  pub fn new() -> Self {
+    RedisMap { inner: HashMap::new() }
+  }
+
+  /// Replace the value an empty map, returning the original value.
+  pub fn take(&mut self) -> Self {
+    RedisMap {
+      inner: mem::take(&mut self.inner),
+    }
+  }
+
+  /// Read the number of (key, value) pairs in the map.
+  pub fn len(&self) -> usize {
+    self.inner.len()
+  }
+
+  /// Take the inner `HashMap`.
+  pub fn inner(self) -> HashMap<RedisKey, RedisValue> {
+    self.inner
+  }
+}
+
+impl Deref for RedisMap {
+  type Target = HashMap<RedisKey, RedisValue>;
+
+  fn deref(&self) -> &Self::Target {
+    &self.inner
+  }
+}
+
+impl DerefMut for RedisMap {
+  fn deref_mut(&mut self) -> &mut Self::Target {
+    &mut self.inner
+  }
+}
+
+impl<'a> From<&'a RedisMap> for RedisMap {
+  fn from(vals: &'a RedisMap) -> Self {
+    vals.clone()
+  }
+}
+
+impl<K, V> TryFrom<HashMap<K, V>> for RedisMap
+where
+  K: TryInto<RedisKey>,
+  K::Error: Into<RedisError>,
+  V: TryInto<RedisValue>,
+  V::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(value: HashMap<K, V>) -> Result<Self, Self::Error> {
+    Ok(RedisMap {
+      inner: utils::into_redis_map(value.into_iter())?,
+    })
+  }
+}
+
+impl<K, V> TryFrom<BTreeMap<K, V>> for RedisMap
+where
+  K: TryInto<RedisKey>,
+  K::Error: Into<RedisError>,
+  V: TryInto<RedisValue>,
+  V::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(value: BTreeMap<K, V>) -> Result<Self, Self::Error> {
+    Ok(RedisMap {
+      inner: utils::into_redis_map(value.into_iter())?,
+    })
+  }
+}
+
+impl From<()> for RedisMap {
+  fn from(_: ()) -> Self {
+    RedisMap::new()
+  }
+}
+
+impl<K, V> TryFrom<(K, V)> for RedisMap
+where
+  K: TryInto<RedisKey>,
+  K::Error: Into<RedisError>,
+  V: TryInto<RedisValue>,
+  V::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from((key, value): (K, V)) -> Result<Self, Self::Error> {
+    let mut inner = HashMap::with_capacity(1);
+    inner.insert(to!(key)?, to!(value)?);
+    Ok(RedisMap { inner })
+  }
+}
+
+impl<K, V> TryFrom<Vec<(K, V)>> for RedisMap
+where
+  K: TryInto<RedisKey>,
+  K::Error: Into<RedisError>,
+  V: TryInto<RedisValue>,
+  V::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(values: Vec<(K, V)>) -> Result<Self, Self::Error> {
+    let mut inner = HashMap::with_capacity(values.len());
+    for (key, value) in values.into_iter() {
+      inner.insert(to!(key)?, to!(value)?);
+    }
+    Ok(RedisMap { inner })
+  }
+}
+
+impl<K, V, const N: usize> TryFrom<[(K, V); N]> for RedisMap
+where
+  K: TryInto<RedisKey>,
+  K::Error: Into<RedisError>,
+  V: TryInto<RedisValue>,
+  V::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(value: [(K, V); N]) -> Result<Self, Self::Error> {
+    let mut inner = HashMap::with_capacity(value.len());
+    for (key, value) in value.into_iter() {
+      inner.insert(to!(key)?, to!(value)?);
+    }
+
+    Ok(RedisMap { inner })
+  }
+}
+impl<'a, K, V, const N: usize> TryFrom<&'a [(K, V); N]> for RedisMap
+where
+  K: TryInto<RedisKey> + Clone,
+  K::Error: Into<RedisError>,
+  V: TryInto<RedisValue> + Clone,
+  V::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(value: &'a [(K, V); N]) -> Result<Self, Self::Error> {
+    let mut inner = HashMap::with_capacity(value.len());
+    for (key, value) in value.iter() {
+      let (key, value) = (key.clone(), value.clone());
+      inner.insert(to!(key)?, to!(value)?);
+    }
+
+    Ok(RedisMap { inner })
+  }
+}
+
+impl<K, V> TryFrom<VecDeque<(K, V)>> for RedisMap
+where
+  K: TryInto<RedisKey>,
+  K::Error: Into<RedisError>,
+  V: TryInto<RedisValue>,
+  V::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(values: VecDeque<(K, V)>) -> Result<Self, Self::Error> {
+    let mut inner = HashMap::with_capacity(values.len());
+    for (key, value) in values.into_iter() {
+      inner.insert(to!(key)?, to!(value)?);
+    }
+    Ok(RedisMap { inner })
+  }
+}
+
+impl<K, V> FromIterator<(K, V)> for RedisMap
+where
+  K: Into<RedisKey>,
+  V: Into<RedisValue>,
+{
+  fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
+    Self {
+      inner: HashMap::from_iter(iter.into_iter().map(|(k, v)| (k.into(), v.into()))),
+    }
+  }
+}
+
+/// The kind of value from Redis.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum RedisValueKind {
+  Boolean,
+  Integer,
+  Double,
+  String,
+  Bytes,
+  Null,
+  Queued,
+  Map,
+  Array,
+}
+
+impl fmt::Display for RedisValueKind {
+  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    let s = match *self {
+      RedisValueKind::Boolean => "Boolean",
+      RedisValueKind::Integer => "Integer",
+      RedisValueKind::Double => "Double",
+      RedisValueKind::String => "String",
+      RedisValueKind::Bytes => "Bytes",
+      RedisValueKind::Null => "nil",
+      RedisValueKind::Queued => "Queued",
+      RedisValueKind::Map => "Map",
+      RedisValueKind::Array => "Array",
+    };
+
+    write!(f, "{}", s)
+  }
+}
+
+/// A value used in a Redis command.
+#[derive(Clone, Debug)]
+pub enum RedisValue {
+  /// A boolean value.
+  Boolean(bool),
+  /// An integer value.
+  Integer(i64),
+  /// A double floating point number.
+  Double(f64),
+  /// A string value.
+  String(Str),
+  /// A byte array value.
+  Bytes(Bytes),
+  /// A `nil` value.
+  Null,
+  /// A special value used to indicate a MULTI block command was received by the server.
+  Queued,
+  /// A map of key/value pairs, primarily used in RESP3 mode.
+  Map(RedisMap),
+  /// An ordered list of values.
+  ///
+  /// In RESP2 mode the server usually sends map structures as an array of key/value pairs.
+  Array(Vec<RedisValue>),
+}
+
+#[allow(clippy::match_like_matches_macro)]
+impl PartialEq for RedisValue {
+  fn eq(&self, other: &Self) -> bool {
+    use RedisValue::*;
+
+    match self {
+      Boolean(ref s) => match other {
+        Boolean(ref o) => *s == *o,
+        _ => false,
+      },
+      Integer(ref s) => match other {
+        Integer(ref o) => *s == *o,
+        _ => false,
+      },
+      Double(ref s) => match other {
+        Double(ref o) => approx_eq!(f64, *s, *o, ulps = 2),
+        _ => false,
+      },
+      String(ref s) => match other {
+        String(ref o) => s == o,
+        _ => false,
+      },
+      Bytes(ref s) => match other {
+        Bytes(ref o) => s == o,
+        _ => false,
+      },
+      Null => match other {
+        Null => true,
+        _ => false,
+      },
+      Queued => match other {
+        Queued => true,
+        _ => false,
+      },
+      Map(ref s) => match other {
+        Map(ref o) => s == o,
+        _ => false,
+      },
+      Array(ref s) => match other {
+        Array(ref o) => s == o,
+        _ => false,
+      },
+    }
+  }
+}
+
+impl Eq for RedisValue {}
+
+impl RedisValue {
+  /// Create a new `RedisValue::Bytes` from a static byte slice without copying.
+  pub fn from_static(b: &'static [u8]) -> Self {
+    RedisValue::Bytes(Bytes::from_static(b))
+  }
+
+  /// Create a new `RedisValue::String` from a static `str` without copying.
+  pub fn from_static_str(s: &'static str) -> Self {
+    RedisValue::String(utils::static_str(s))
+  }
+
+  /// Create a new `RedisValue` with the `OK` status.
+  pub fn new_ok() -> Self {
+    Self::from_static_str(OK)
+  }
+
+  /// Whether the value is a simple string OK value.
+  pub fn is_ok(&self) -> bool {
+    match *self {
+      RedisValue::String(ref s) => *s == OK,
+      _ => false,
+    }
+  }
+
+  /// Attempt to convert the value into an integer, returning the original string as an error if the parsing fails.
+  pub fn into_integer(self) -> Result<RedisValue, RedisValue> {
+    match self {
+      RedisValue::String(s) => match s.parse::<i64>() {
+        Ok(i) => Ok(RedisValue::Integer(i)),
+        Err(_) => Err(RedisValue::String(s)),
+      },
+      RedisValue::Integer(i) => Ok(RedisValue::Integer(i)),
+      _ => Err(self),
+    }
+  }
+
+  /// Read the type of the value without any associated data.
+  pub fn kind(&self) -> RedisValueKind {
+    match *self {
+      RedisValue::Boolean(_) => RedisValueKind::Boolean,
+      RedisValue::Integer(_) => RedisValueKind::Integer,
+      RedisValue::Double(_) => RedisValueKind::Double,
+      RedisValue::String(_) => RedisValueKind::String,
+      RedisValue::Bytes(_) => RedisValueKind::Bytes,
+      RedisValue::Null => RedisValueKind::Null,
+      RedisValue::Queued => RedisValueKind::Queued,
+      RedisValue::Map(_) => RedisValueKind::Map,
+      RedisValue::Array(_) => RedisValueKind::Array,
+    }
+  }
+
+  /// Check if the value is null.
+  pub fn is_null(&self) -> bool {
+    matches!(*self, RedisValue::Null)
+  }
+
+  /// Check if the value is an integer.
+  pub fn is_integer(&self) -> bool {
+    matches!(self, RedisValue::Integer(_))
+  }
+
+  /// Check if the value is a string.
+  pub fn is_string(&self) -> bool {
+    matches!(*self, RedisValue::String(_))
+  }
+
+  /// Check if the value is an array of bytes.
+  pub fn is_bytes(&self) -> bool {
+    matches!(*self, RedisValue::Bytes(_))
+  }
+
+  /// Whether the value is a boolean value or can be parsed as a boolean value.
+  #[allow(clippy::match_like_matches_macro)]
+  pub fn is_boolean(&self) -> bool {
+    match *self {
+      RedisValue::Boolean(_) => true,
+      RedisValue::Integer(0 | 1) => true,
+      RedisValue::Integer(_) => false,
+      RedisValue::String(ref s) => match s.as_bytes() {
+        b"true" | b"false" | b"t" | b"f" | b"TRUE" | b"FALSE" | b"T" | b"F" | b"1" | b"0" => true,
+        _ => false,
+      },
+      _ => false,
+    }
+  }
+
+  /// Whether the inner value is a double or can be parsed as a double.
+  pub fn is_double(&self) -> bool {
+    match *self {
+      RedisValue::Double(_) => true,
+      RedisValue::String(ref s) => utils::redis_string_to_f64(s).is_ok(),
+      _ => false,
+    }
+  }
+
+  /// Check if the value is a `QUEUED` response.
+  pub fn is_queued(&self) -> bool {
+    matches!(*self, RedisValue::Queued)
+  }
+
+  /// Whether the value is an array or map.
+  pub fn is_aggregate_type(&self) -> bool {
+    matches!(*self, RedisValue::Array(_) | RedisValue::Map(_))
+  }
+
+  /// Whether the value is a `RedisMap`.
+  ///
+  /// See [is_maybe_map](Self::is_maybe_map) for a function that also checks for arrays that likely represent a map in
+  /// RESP2 mode.
+  pub fn is_map(&self) -> bool {
+    matches!(*self, RedisValue::Map(_))
+  }
+
+  /// Whether the value is a `RedisMap` or an array with an even number of elements where each even-numbered
+  /// element is not an aggregate type.
+  ///
+  /// RESP2 and RESP3 encode maps differently, and this function can be used to duck-type maps across protocol
+  /// versions.
+  pub fn is_maybe_map(&self) -> bool {
+    match *self {
+      RedisValue::Map(_) => true,
+      RedisValue::Array(ref arr) => utils::is_maybe_array_map(arr),
+      _ => false,
+    }
+  }
+
+  /// Whether the value is an array.
+  pub fn is_array(&self) -> bool {
+    matches!(*self, RedisValue::Array(_))
+  }
+
+  /// Read and return the inner value as a `u64`, if possible.
+  pub fn as_u64(&self) -> Option<u64> {
+    match self {
+      RedisValue::Integer(ref i) => {
+        if *i >= 0 {
+          Some(*i as u64)
+        } else {
+          None
+        }
+      },
+      RedisValue::String(ref s) => s.parse::<u64>().ok(),
+      RedisValue::Array(ref inner) => {
+        if inner.len() == 1 {
+          inner.first().and_then(|v| v.as_u64())
+        } else {
+          None
+        }
+      },
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Some(0),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => None,
+      _ => None,
+    }
+  }
+
+  ///  Read and return the inner value as a `i64`, if possible.
+  pub fn as_i64(&self) -> Option<i64> {
+    match self {
+      RedisValue::Integer(ref i) => Some(*i),
+      RedisValue::String(ref s) => s.parse::<i64>().ok(),
+      RedisValue::Array(ref inner) => {
+        if inner.len() == 1 {
+          inner.first().and_then(|v| v.as_i64())
+        } else {
+          None
+        }
+      },
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Some(0),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => None,
+      _ => None,
+    }
+  }
+
+  ///  Read and return the inner value as a `usize`, if possible.
+  pub fn as_usize(&self) -> Option<usize> {
+    match self {
+      RedisValue::Integer(i) => {
+        if *i >= 0 {
+          Some(*i as usize)
+        } else {
+          None
+        }
+      },
+      RedisValue::String(ref s) => s.parse::<usize>().ok(),
+      RedisValue::Array(ref inner) => {
+        if inner.len() == 1 {
+          inner.first().and_then(|v| v.as_usize())
+        } else {
+          None
+        }
+      },
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Some(0),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => None,
+      _ => None,
+    }
+  }
+
+  ///  Read and return the inner value as a `f64`, if possible.
+  pub fn as_f64(&self) -> Option<f64> {
+    match self {
+      RedisValue::Double(ref f) => Some(*f),
+      RedisValue::String(ref s) => utils::redis_string_to_f64(s).ok(),
+      RedisValue::Integer(ref i) => Some(*i as f64),
+      RedisValue::Array(ref inner) => {
+        if inner.len() == 1 {
+          inner.first().and_then(|v| v.as_f64())
+        } else {
+          None
+        }
+      },
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Some(0.0),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => None,
+      _ => None,
+    }
+  }
+
+  /// Read and return the inner `String` if the value is a string or scalar value.
+  pub fn into_string(self) -> Option<String> {
+    match self {
+      RedisValue::Boolean(b) => Some(b.to_string()),
+      RedisValue::Double(f) => Some(f.to_string()),
+      RedisValue::String(s) => Some(s.to_string()),
+      RedisValue::Bytes(b) => String::from_utf8(b.to_vec()).ok(),
+      RedisValue::Integer(i) => Some(i.to_string()),
+      RedisValue::Queued => Some(QUEUED.to_owned()),
+      RedisValue::Array(mut inner) => {
+        if inner.len() == 1 {
+          inner.pop().and_then(|v| v.into_string())
+        } else {
+          None
+        }
+      },
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Some(String::new()),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => None,
+      _ => None,
+    }
+  }
+
+  /// Read and return the inner data as a `Str` from the `bytes` crate.
+  pub fn into_bytes_str(self) -> Option<Str> {
+    match self {
+      RedisValue::Boolean(b) => match b {
+        true => Some(TRUE_STR.clone()),
+        false => Some(FALSE_STR.clone()),
+      },
+      RedisValue::Double(f) => Some(f.to_string().into()),
+      RedisValue::String(s) => Some(s),
+      RedisValue::Bytes(b) => Str::from_inner(b).ok(),
+      RedisValue::Integer(i) => Some(i.to_string().into()),
+      RedisValue::Queued => Some(utils::static_str(QUEUED)),
+      RedisValue::Array(mut inner) => {
+        if inner.len() == 1 {
+          inner.pop().and_then(|v| v.into_bytes_str())
+        } else {
+          None
+        }
+      },
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Some(Str::new()),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => None,
+      _ => None,
+    }
+  }
+
+  /// Read the inner value as a `Str`.
+  pub fn as_bytes_str(&self) -> Option<Str> {
+    match self {
+      RedisValue::Boolean(ref b) => match *b {
+        true => Some(TRUE_STR.clone()),
+        false => Some(FALSE_STR.clone()),
+      },
+      RedisValue::Double(ref f) => Some(f.to_string().into()),
+      RedisValue::String(ref s) => Some(s.clone()),
+      RedisValue::Bytes(ref b) => Str::from_inner(b.clone()).ok(),
+      RedisValue::Integer(ref i) => Some(i.to_string().into()),
+      RedisValue::Queued => Some(utils::static_str(QUEUED)),
+      RedisValue::Array(ref inner) => {
+        if inner.len() == 1 {
+          inner[0].as_bytes_str()
+        } else {
+          None
+        }
+      },
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Some(Str::new()),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => None,
+      _ => None,
+    }
+  }
+
+  /// Read and return the inner `String` if the value is a string or scalar value.
+  ///
+  /// Note: this will cast integers and doubles to strings.
+  pub fn as_string(&self) -> Option<String> {
+    match self {
+      RedisValue::Boolean(ref b) => Some(b.to_string()),
+      RedisValue::Double(ref f) => Some(f.to_string()),
+      RedisValue::String(ref s) => Some(s.to_string()),
+      RedisValue::Bytes(ref b) => str::from_utf8(b).ok().map(|s| s.to_owned()),
+      RedisValue::Integer(ref i) => Some(i.to_string()),
+      RedisValue::Queued => Some(QUEUED.to_owned()),
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Some(String::new()),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => None,
+      _ => None,
+    }
+  }
+
+  /// Read the inner value as a string slice.
+  ///
+  /// Null is returned as `"nil"` and scalar values are cast to a string.
+  pub fn as_str(&self) -> Option<Cow<str>> {
+    let s: Cow<str> = match *self {
+      RedisValue::Double(ref f) => Cow::Owned(f.to_string()),
+      RedisValue::Boolean(ref b) => Cow::Owned(b.to_string()),
+      RedisValue::String(ref s) => Cow::Borrowed(s.deref()),
+      RedisValue::Integer(ref i) => Cow::Owned(i.to_string()),
+      RedisValue::Queued => Cow::Borrowed(QUEUED),
+      RedisValue::Bytes(ref b) => return str::from_utf8(b).ok().map(Cow::Borrowed),
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Cow::Borrowed(""),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => return None,
+      _ => return None,
+    };
+
+    Some(s)
+  }
+
+  /// Read the inner value as a string, using `String::from_utf8_lossy` on byte slices.
+  pub fn as_str_lossy(&self) -> Option<Cow<str>> {
+    let s: Cow<str> = match *self {
+      RedisValue::Boolean(ref b) => Cow::Owned(b.to_string()),
+      RedisValue::Double(ref f) => Cow::Owned(f.to_string()),
+      RedisValue::String(ref s) => Cow::Borrowed(s.deref()),
+      RedisValue::Integer(ref i) => Cow::Owned(i.to_string()),
+      RedisValue::Queued => Cow::Borrowed(QUEUED),
+      RedisValue::Bytes(ref b) => String::from_utf8_lossy(b),
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Cow::Borrowed(""),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => return None,
+      _ => return None,
+    };
+
+    Some(s)
+  }
+
+  /// Read the inner value as an array of bytes, if possible.
+  pub fn as_bytes(&self) -> Option<&[u8]> {
+    match *self {
+      RedisValue::String(ref s) => Some(s.as_bytes()),
+      RedisValue::Bytes(ref b) => Some(b),
+      RedisValue::Queued => Some(QUEUED.as_bytes()),
+      _ => None,
+    }
+  }
+
+  /// Attempt to convert the value to a `bool`.
+  pub fn as_bool(&self) -> Option<bool> {
+    match *self {
+      RedisValue::Boolean(b) => Some(b),
+      RedisValue::Integer(ref i) => match *i {
+        0 => Some(false),
+        1 => Some(true),
+        _ => None,
+      },
+      RedisValue::String(ref s) => match s.as_bytes() {
+        b"true" | b"TRUE" | b"t" | b"T" | b"1" => Some(true),
+        b"false" | b"FALSE" | b"f" | b"F" | b"0" => Some(false),
+        _ => None,
+      },
+      RedisValue::Array(ref inner) => {
+        if inner.len() == 1 {
+          inner.first().and_then(|v| v.as_bool())
+        } else {
+          None
+        }
+      },
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Some(false),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => None,
+      _ => None,
+    }
+  }
+
+  /// Attempt to convert this value to a Redis map if it's an array with an even number of elements.
+  pub fn into_map(self) -> Result<RedisMap, RedisError> {
+    match self {
+      RedisValue::Map(map) => Ok(map),
+      RedisValue::Array(mut values) => {
+        if values.len() % 2 != 0 {
+          return Err(RedisError::new(
+            RedisErrorKind::Unknown,
+            "Expected an even number of elements.",
+          ));
+        }
+        let mut inner = HashMap::with_capacity(values.len() / 2);
+        while values.len() >= 2 {
+          let value = values.pop().unwrap();
+          let key: RedisKey = values.pop().unwrap().try_into()?;
+
+          inner.insert(key, value);
+        }
+
+        Ok(RedisMap { inner })
+      },
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Ok(RedisMap::new()),
+      _ => Err(RedisError::new(RedisErrorKind::Unknown, "Could not convert to map.")),
+    }
+  }
+
+  pub(crate) fn into_multiple_values(self) -> Vec<RedisValue> {
+    match self {
+      RedisValue::Array(values) => values,
+      RedisValue::Map(map) => map
+        .inner()
+        .into_iter()
+        .flat_map(|(k, v)| [RedisValue::Bytes(k.into_bytes()), v])
+        .collect(),
+      RedisValue::Null => Vec::new(),
+      _ => vec![self],
+    }
+  }
+
+  /// Convert the array value to a set, if possible.
+  pub fn into_set(self) -> Result<HashSet<RedisValue>, RedisError> {
+    match self {
+      RedisValue::Array(values) => Ok(values.into_iter().collect()),
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Ok(HashSet::new()),
+      _ => Err(RedisError::new_parse("Could not convert to set.")),
+    }
+  }
+
+  /// Convert a `RedisValue` to `Vec<(RedisValue, f64)>`, if possible.
+  pub fn into_zset_result(self) -> Result<Vec<(RedisValue, f64)>, RedisError> {
+    protocol_utils::value_to_zset_result(self)
+  }
+
+  /// Convert this value to an array if it's an array or map.
+  ///
+  /// If the value is not an array or map this returns a single-element array containing the original value.
+  pub fn into_array(self) -> Vec<RedisValue> {
+    match self {
+      RedisValue::Array(values) => values,
+      RedisValue::Map(map) => {
+        let mut out = Vec::with_capacity(map.len() * 2);
+        for (key, value) in map.inner().into_iter() {
+          out.extend([key.into(), value]);
+        }
+        out
+      },
+      _ => vec![self],
+    }
+  }
+
+  /// Convert the value to an array of bytes, if possible.
+  pub fn into_owned_bytes(self) -> Option<Vec<u8>> {
+    let v = match self {
+      RedisValue::String(s) => s.to_string().into_bytes(),
+      RedisValue::Bytes(b) => b.to_vec(),
+      RedisValue::Queued => QUEUED.as_bytes().to_vec(),
+      RedisValue::Array(mut inner) => {
+        if inner.len() == 1 {
+          return inner.pop().and_then(|v| v.into_owned_bytes());
+        } else {
+          return None;
+        }
+      },
+      RedisValue::Integer(i) => i.to_string().into_bytes(),
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Vec::new(),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => return None,
+      _ => return None,
+    };
+
+    Some(v)
+  }
+
+  /// Convert the value into a `Bytes` view.
+  pub fn into_bytes(self) -> Option<Bytes> {
+    let v = match self {
+      RedisValue::String(s) => s.inner().clone(),
+      RedisValue::Bytes(b) => b,
+      RedisValue::Queued => Bytes::from_static(QUEUED.as_bytes()),
+      RedisValue::Array(mut inner) => {
+        if inner.len() == 1 {
+          return inner.pop().and_then(|v| v.into_bytes());
+        } else {
+          return None;
+        }
+      },
+      RedisValue::Integer(i) => i.to_string().into(),
+      #[cfg(feature = "default-nil-types")]
+      RedisValue::Null => Bytes::new(),
+      #[cfg(not(feature = "default-nil-types"))]
+      RedisValue::Null => return None,
+      _ => return None,
+    };
+
+    Some(v)
+  }
+
+  /// Return the length of the inner array if the value is an array.
+  pub fn array_len(&self) -> Option<usize> {
+    match self {
+      RedisValue::Array(ref a) => Some(a.len()),
+      _ => None,
+    }
+  }
+
+  /// Whether the value is an array with one element.
+  pub(crate) fn is_single_element_vec(&self) -> bool {
+    if let RedisValue::Array(ref d) = self {
+      d.len() == 1
+    } else {
+      false
+    }
+  }
+
+  /// Pop the first value in the inner array or return the original value.
+  ///
+  /// This uses unwrap. Use [is_single_element_vec] first.
+  pub(crate) fn pop_or_take(self) -> Self {
+    if let RedisValue::Array(mut values) = self {
+      values.pop().unwrap()
+    } else {
+      self
+    }
+  }
+
+  /// Flatten adjacent nested arrays to the provided depth.
+  ///
+  /// See the [XREAD](crate::interfaces::StreamsInterface::xread) documentation for an example of when this might be
+  /// useful.
+  pub fn flatten_array_values(self, depth: usize) -> Self {
+    utils::flatten_nested_array_values(self, depth)
+  }
+
+  /// A utility function to convert the response from `XREAD` or `XREADGROUP` into a type with a less verbose type
+  /// declaration.
+  ///
+  /// This function supports responses in both RESP2 and RESP3 formats.
+  ///
+  /// See the [XREAD](crate::interfaces::StreamsInterface::xread) (or `XREADGROUP`) documentation for more
+  /// information.
+  #[cfg(feature = "i-streams")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))]
+  pub fn into_xread_response<K1, I, K2, V>(self) -> Result<XReadResponse<K1, I, K2, V>, RedisError>
+  where
+    K1: FromRedisKey + Hash + Eq,
+    K2: FromRedisKey + Hash + Eq,
+    I: FromRedis,
+    V: FromRedis,
+  {
+    self.flatten_array_values(2).convert()
+  }
+
+  /// A utility function to convert the response from `XCLAIM`, etc into a type with a less verbose type declaration.
+  ///
+  /// This function supports responses in both RESP2 and RESP3 formats.
+  #[cfg(feature = "i-streams")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))]
+  pub fn into_xread_value<I, K, V>(self) -> Result<Vec<XReadValue<I, K, V>>, RedisError>
+  where
+    K: FromRedisKey + Hash + Eq,
+    I: FromRedis,
+    V: FromRedis,
+  {
+    self.flatten_array_values(1).convert()
+  }
+
+  /// A utility function to convert the response from `XAUTOCLAIM` into a type with a less verbose type declaration.
+  ///
+  /// This function supports responses in both RESP2 and RESP3 formats.
+  ///
+  /// Note: the new (as of Redis 7.x) return value containing message PIDs that were deleted from the PEL are dropped.
+  /// Callers should use `xautoclaim` instead if this data is needed.
+  #[cfg(feature = "i-streams")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))]
+  pub fn into_xautoclaim_values<I, K, V>(self) -> Result<(String, Vec<XReadValue<I, K, V>>), RedisError>
+  where
+    K: FromRedisKey + Hash + Eq,
+    I: FromRedis,
+    V: FromRedis,
+  {
+    if let RedisValue::Array(mut values) = self {
+      if values.len() == 3 {
+        // convert the redis 7.x response format to the v6 format
+        trace!("Removing the third message PID elements from XAUTOCLAIM response.");
+        values.pop();
+      }
+
+      // unwrap checked above
+      let entries = values.pop().unwrap();
+      let cursor: String = values.pop().unwrap().convert()?;
+
+      Ok((cursor, entries.flatten_array_values(1).convert()?))
+    } else {
+      Err(RedisError::new_parse("Expected array response."))
+    }
+  }
+
+  /// Parse the value as the response from `FUNCTION LIST`, including only functions with the provided library `name`.
+  #[cfg(feature = "i-scripts")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+  pub fn as_functions(&self, name: &str) -> Result<Vec<Function>, RedisError> {
+    utils::value_to_functions(self, name)
+  }
+
+  /// Convert the value into a `GeoPosition`, if possible.
+  ///
+  /// Null values are returned as `None` to work more easily with the result of the `GEOPOS` command.
+  #[cfg(feature = "i-geo")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))]
+  pub fn as_geo_position(&self) -> Result<Option<GeoPosition>, RedisError> {
+    if self.is_null() {
+      Ok(None)
+    } else {
+      GeoPosition::try_from(self.clone()).map(Some)
+    }
+  }
+
+  /// Parse the value as the response to any of the relevant GEO commands that return an array of
+  /// [GeoRadiusInfo](crate::types::GeoRadiusInfo) values, such as `GEOSEARCH`, GEORADIUS`, etc.
+  #[cfg(feature = "i-geo")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))]
+  pub fn into_geo_radius_result(
+    self,
+    withcoord: bool,
+    withdist: bool,
+    withhash: bool,
+  ) -> Result<Vec<GeoRadiusInfo>, RedisError> {
+    match self {
+      RedisValue::Array(data) => data
+        .into_iter()
+        .map(|value| GeoRadiusInfo::from_redis_value(value, withcoord, withdist, withhash))
+        .collect(),
+      RedisValue::Null => Ok(Vec::new()),
+      _ => Err(RedisError::new(RedisErrorKind::Parse, "Expected array.")),
+    }
+  }
+
+  /// Replace this value with `RedisValue::Null`, returning the original value.
+  pub fn take(&mut self) -> RedisValue {
+    mem::replace(self, RedisValue::Null)
+  }
+
+  /// Attempt to convert this value to any value that implements the [FromRedis](crate::types::FromRedis) trait.
+  pub fn convert<R>(self) -> Result<R, RedisError>
+  where
+    R: FromRedis,
+  {
+    R::from_value(self)
+  }
+
+  /// Whether the value can be hashed.
+  ///
+  /// Some use cases require using `RedisValue` types as keys in a `HashMap`, etc. Trying to do so with an aggregate
+  /// type can panic, and this function can be used to more gracefully handle this situation.
+  pub fn can_hash(&self) -> bool {
+    matches!(
+      self.kind(),
+      RedisValueKind::String
+        | RedisValueKind::Boolean
+        | RedisValueKind::Double
+        | RedisValueKind::Integer
+        | RedisValueKind::Bytes
+        | RedisValueKind::Null
+        | RedisValueKind::Array
+        | RedisValueKind::Queued
+    )
+  }
+
+  /// Convert the value to JSON.
+  #[cfg(feature = "serde-json")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "serde-json")))]
+  pub fn into_json(self) -> Result<Value, RedisError> {
+    Value::from_value(self)
+  }
+}
+
+impl Hash for RedisValue {
+  fn hash<H: Hasher>(&self, state: &mut H) {
+    // used to prevent collisions between different types
+    let prefix = match self.kind() {
+      RedisValueKind::Boolean => b'B',
+      RedisValueKind::Double => b'd',
+      RedisValueKind::Integer => b'i',
+      RedisValueKind::String => b's',
+      RedisValueKind::Null => b'n',
+      RedisValueKind::Queued => b'q',
+      RedisValueKind::Array => b'a',
+      RedisValueKind::Map => b'm',
+      RedisValueKind::Bytes => b'b',
+    };
+    prefix.hash(state);
+
+    match *self {
+      RedisValue::Boolean(b) => b.hash(state),
+      RedisValue::Double(f) => f.to_be_bytes().hash(state),
+      RedisValue::Integer(d) => d.hash(state),
+      RedisValue::String(ref s) => s.hash(state),
+      RedisValue::Bytes(ref b) => b.hash(state),
+      RedisValue::Null => NULL.hash(state),
+      RedisValue::Queued => QUEUED.hash(state),
+      RedisValue::Array(ref arr) => {
+        for value in arr.iter() {
+          value.hash(state);
+        }
+      },
+      _ => panic!("Cannot hash aggregate value."),
+    }
+  }
+}
+
+impl From<u8> for RedisValue {
+  fn from(d: u8) -> Self {
+    RedisValue::Integer(d as i64)
+  }
+}
+
+impl From<u16> for RedisValue {
+  fn from(d: u16) -> Self {
+    RedisValue::Integer(d as i64)
+  }
+}
+
+impl From<u32> for RedisValue {
+  fn from(d: u32) -> Self {
+    RedisValue::Integer(d as i64)
+  }
+}
+
+impl From<i8> for RedisValue {
+  fn from(d: i8) -> Self {
+    RedisValue::Integer(d as i64)
+  }
+}
+
+impl From<i16> for RedisValue {
+  fn from(d: i16) -> Self {
+    RedisValue::Integer(d as i64)
+  }
+}
+
+impl From<i32> for RedisValue {
+  fn from(d: i32) -> Self {
+    RedisValue::Integer(d as i64)
+  }
+}
+
+impl From<i64> for RedisValue {
+  fn from(d: i64) -> Self {
+    RedisValue::Integer(d)
+  }
+}
+
+impl From<f32> for RedisValue {
+  fn from(f: f32) -> Self {
+    RedisValue::Double(f as f64)
+  }
+}
+
+impl From<f64> for RedisValue {
+  fn from(f: f64) -> Self {
+    RedisValue::Double(f)
+  }
+}
+
+impl TryFrom<u64> for RedisValue {
+  type Error = RedisError;
+
+  fn try_from(d: u64) -> Result<Self, Self::Error> {
+    if d >= (i64::MAX as u64) {
+      return Err(RedisError::new(RedisErrorKind::Unknown, "Unsigned integer too large."));
+    }
+
+    Ok((d as i64).into())
+  }
+}
+
+impl TryFrom<u128> for RedisValue {
+  type Error = RedisError;
+
+  fn try_from(d: u128) -> Result<Self, Self::Error> {
+    if d >= (i64::MAX as u128) {
+      return Err(RedisError::new(RedisErrorKind::Unknown, "Unsigned integer too large."));
+    }
+
+    Ok((d as i64).into())
+  }
+}
+
+impl TryFrom<i128> for RedisValue {
+  type Error = RedisError;
+
+  fn try_from(d: i128) -> Result<Self, Self::Error> {
+    if d >= (i64::MAX as i128) {
+      return Err(RedisError::new(RedisErrorKind::Unknown, "Signed integer too large."));
+    }
+
+    Ok((d as i64).into())
+  }
+}
+
+impl TryFrom<usize> for RedisValue {
+  type Error = RedisError;
+
+  fn try_from(d: usize) -> Result<Self, Self::Error> {
+    if d >= (i64::MAX as usize) {
+      return Err(RedisError::new(RedisErrorKind::Unknown, "Unsigned integer too large."));
+    }
+
+    Ok((d as i64).into())
+  }
+}
+
+impl From<Str> for RedisValue {
+  fn from(s: Str) -> Self {
+    RedisValue::String(s)
+  }
+}
+
+impl From<Bytes> for RedisValue {
+  fn from(b: Bytes) -> Self {
+    RedisValue::Bytes(b)
+  }
+}
+
+impl From<Box<[u8]>> for RedisValue {
+  fn from(b: Box<[u8]>) -> Self {
+    RedisValue::Bytes(b.into())
+  }
+}
+
+impl From<String> for RedisValue {
+  fn from(d: String) -> Self {
+    RedisValue::String(Str::from(d))
+  }
+}
+
+impl<'a> From<&'a String> for RedisValue {
+  fn from(d: &'a String) -> Self {
+    RedisValue::String(Str::from(d))
+  }
+}
+
+impl<'a> From<&'a str> for RedisValue {
+  fn from(d: &'a str) -> Self {
+    RedisValue::String(Str::from(d))
+  }
+}
+
+impl<'a> From<&'a [u8]> for RedisValue {
+  fn from(b: &'a [u8]) -> Self {
+    RedisValue::Bytes(Bytes::from(b.to_vec()))
+  }
+}
+
+impl From<bool> for RedisValue {
+  fn from(d: bool) -> Self {
+    RedisValue::Boolean(d)
+  }
+}
+
+impl<T> TryFrom<Option<T>> for RedisValue
+where
+  T: TryInto<RedisValue>,
+  T::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(d: Option<T>) -> Result<Self, Self::Error> {
+    match d {
+      Some(i) => to!(i),
+      None => Ok(RedisValue::Null),
+    }
+  }
+}
+
+impl<'a, T, const N: usize> TryFrom<&'a [T; N]> for RedisValue
+where
+  T: TryInto<RedisValue> + Clone,
+  T::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(value: &'a [T; N]) -> Result<Self, Self::Error> {
+    let values = value
+      .iter()
+      .map(|v| v.clone().try_into().map_err(|e| e.into()))
+      .collect::<Result<Vec<RedisValue>, RedisError>>()?;
+
+    Ok(RedisValue::Array(values))
+  }
+}
+
+impl<T, const N: usize> TryFrom<[T; N]> for RedisValue
+where
+  T: TryInto<RedisValue> + Clone,
+  T::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(value: [T; N]) -> Result<Self, Self::Error> {
+    let values = value
+      .into_iter()
+      .map(|v| v.try_into().map_err(|e| e.into()))
+      .collect::<Result<Vec<RedisValue>, RedisError>>()?;
+
+    Ok(RedisValue::Array(values))
+  }
+}
+
+impl<T> TryFrom<Vec<T>> for RedisValue
+where
+  T: TryInto<RedisValue>,
+  T::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(value: Vec<T>) -> Result<Self, Self::Error> {
+    let values = value
+      .into_iter()
+      .map(|v| v.try_into().map_err(|e| e.into()))
+      .collect::<Result<Vec<RedisValue>, RedisError>>()?;
+
+    Ok(RedisValue::Array(values))
+  }
+}
+
+impl<T> TryFrom<VecDeque<T>> for RedisValue
+where
+  T: TryInto<RedisValue>,
+  T::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(value: VecDeque<T>) -> Result<Self, Self::Error> {
+    let values = value
+      .into_iter()
+      .map(|v| v.try_into().map_err(|e| e.into()))
+      .collect::<Result<Vec<RedisValue>, RedisError>>()?;
+
+    Ok(RedisValue::Array(values))
+  }
+}
+
+impl<V> FromIterator<V> for RedisValue
+where
+  V: Into<RedisValue>,
+{
+  fn from_iter<I: IntoIterator<Item = V>>(iter: I) -> Self {
+    RedisValue::Array(iter.into_iter().map(|v| v.into()).collect())
+  }
+}
+
+impl<K, V> TryFrom<HashMap<K, V>> for RedisValue
+where
+  K: TryInto<RedisKey>,
+  K::Error: Into<RedisError>,
+  V: TryInto<RedisValue>,
+  V::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(d: HashMap<K, V>) -> Result<Self, Self::Error> {
+    Ok(RedisValue::Map(RedisMap {
+      inner: utils::into_redis_map(d.into_iter())?,
+    }))
+  }
+}
+
+impl<K, V> TryFrom<BTreeMap<K, V>> for RedisValue
+where
+  K: TryInto<RedisKey>,
+  K::Error: Into<RedisError>,
+  V: TryInto<RedisValue>,
+  V::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(d: BTreeMap<K, V>) -> Result<Self, Self::Error> {
+    Ok(RedisValue::Map(RedisMap {
+      inner: utils::into_redis_map(d.into_iter())?,
+    }))
+  }
+}
+
+impl From<RedisKey> for RedisValue {
+  fn from(d: RedisKey) -> Self {
+    RedisValue::Bytes(d.key)
+  }
+}
+
+impl From<RedisMap> for RedisValue {
+  fn from(m: RedisMap) -> Self {
+    RedisValue::Map(m)
+  }
+}
+
+impl From<()> for RedisValue {
+  fn from(_: ()) -> Self {
+    RedisValue::Null
+  }
+}
+
+impl TryFrom<Resp3Frame> for RedisValue {
+  type Error = RedisError;
+
+  fn try_from(value: Resp3Frame) -> Result<Self, Self::Error> {
+    protocol_utils::frame_to_results(value)
+  }
+}
+
+#[cfg(test)]
+mod tests {
+  use super::*;
+
+  #[test]
+  fn redis_map_from_iter() {
+    let map = [("hello", "world")].into_iter().collect::<RedisMap>();
+    assert_eq!(map.inner[&RedisKey::from("hello")], RedisValue::from("world"));
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/types/builder.rs.html b/doc/src/fred/types/builder.rs.html new file mode 100644 index 00000000..99245d14 --- /dev/null +++ b/doc/src/fred/types/builder.rs.html @@ -0,0 +1,611 @@ +builder.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+
use crate::{
+  clients::{RedisClient, RedisPool},
+  error::{RedisError, RedisErrorKind},
+  prelude::ReconnectPolicy,
+  types::{ConnectionConfig, PerformanceConfig, RedisConfig, ServerConfig},
+};
+
+#[cfg(not(feature = "glommio"))]
+use crate::clients::ExclusivePool;
+#[cfg(feature = "subscriber-client")]
+use crate::clients::SubscriberClient;
+#[cfg(feature = "sentinel-client")]
+use crate::{clients::SentinelClient, types::SentinelConfig};
+
+/// A client and pool builder interface.
+///
+/// ```rust
+/// # use std::time::Duration;
+/// # use redis_protocol::resp3::types::RespVersion;
+/// # use fred::prelude::*;
+/// fn example() -> Result<(), RedisError> {
+///   // use default values
+///   let client = Builder::default_centralized().build()?;
+///
+///   // or initialize from a URL or config
+///   let config = RedisConfig::from_url("redis://localhost:6379/1")?;
+///   let mut builder = Builder::from_config(config);
+///   // or modify values in place (creating defaults if needed)
+///   builder
+///     .with_performance_config(|config| {
+///       config.auto_pipeline = true;
+///     })
+///     .with_config(|config| {
+///       config.version = RespVersion::RESP3;
+///       config.fail_fast = true;
+///     })
+///     .with_connection_config(|config| {
+///       config.tcp = TcpConfig {
+///         nodelay: Some(true),
+///         ..Default::default()
+///       };
+///       config.internal_command_timeout = Duration::from_secs(10);
+///     });
+///   // or overwrite configuration structs in place
+///   builder.set_policy(ReconnectPolicy::new_exponential(0, 100, 30_000, 2));
+///   builder.set_performance_config(PerformanceConfig::default());
+///
+///   // reuse the builder as needed to create any kind of client
+///   let client = builder.build()?;
+///   let pool = builder.build_pool(3)?;
+///   let subscriber = builder.build_subscriber_client()?;
+///
+///   // ...
+///
+///   Ok(())
+/// }
+/// ```
+#[derive(Clone, Debug)]
+pub struct Builder {
+  config:      Option<RedisConfig>,
+  performance: PerformanceConfig,
+  connection:  ConnectionConfig,
+  policy:      Option<ReconnectPolicy>,
+  #[cfg(feature = "sentinel-client")]
+  sentinel:    Option<SentinelConfig>,
+}
+
+impl Default for Builder {
+  fn default() -> Self {
+    Builder {
+      config:                                       None,
+      performance:                                  PerformanceConfig::default(),
+      connection:                                   ConnectionConfig::default(),
+      policy:                                       None,
+      #[cfg(feature = "sentinel-client")]
+      sentinel:                                     None,
+    }
+  }
+}
+
+impl Builder {
+  /// Create a new builder instance with default config values for a centralized deployment.
+  pub fn default_centralized() -> Self {
+    Builder {
+      config: Some(RedisConfig {
+        server: ServerConfig::default_centralized(),
+        ..Default::default()
+      }),
+      ..Default::default()
+    }
+  }
+
+  /// Create a new builder instance with default config values for a clustered deployment.
+  pub fn default_clustered() -> Self {
+    Builder {
+      config: Some(RedisConfig {
+        server: ServerConfig::default_clustered(),
+        ..Default::default()
+      }),
+      ..Default::default()
+    }
+  }
+
+  /// Create a new builder instance from the provided client config.
+  pub fn from_config(config: RedisConfig) -> Self {
+    Builder {
+      config: Some(config),
+      ..Default::default()
+    }
+  }
+
+  /// Read the client config.
+  pub fn get_config(&self) -> Option<&RedisConfig> {
+    self.config.as_ref()
+  }
+
+  /// Read the reconnection policy.
+  pub fn get_policy(&self) -> Option<&ReconnectPolicy> {
+    self.policy.as_ref()
+  }
+
+  /// Read the performance config.
+  pub fn get_performance_config(&self) -> &PerformanceConfig {
+    &self.performance
+  }
+
+  /// Read the connection config.
+  pub fn get_connection_config(&self) -> &ConnectionConfig {
+    &self.connection
+  }
+
+  /// Read the sentinel client config.
+  #[cfg(feature = "sentinel-client")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))]
+  pub fn get_sentinel_config(&self) -> Option<&RedisConfig> {
+    self.config.as_ref()
+  }
+
+  /// Overwrite the client config on the builder.
+  pub fn set_config(&mut self, config: RedisConfig) -> &mut Self {
+    self.config = Some(config);
+    self
+  }
+
+  /// Overwrite the reconnection policy on the builder.
+  pub fn set_policy(&mut self, policy: ReconnectPolicy) -> &mut Self {
+    self.policy = Some(policy);
+    self
+  }
+
+  /// Overwrite the performance config on the builder.
+  pub fn set_performance_config(&mut self, config: PerformanceConfig) -> &mut Self {
+    self.performance = config;
+    self
+  }
+
+  /// Overwrite the connection config on the builder.
+  pub fn set_connection_config(&mut self, config: ConnectionConfig) -> &mut Self {
+    self.connection = config;
+    self
+  }
+
+  /// Overwrite the sentinel config on the builder.
+  #[cfg(feature = "sentinel-client")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))]
+  pub fn set_sentinel_config(&mut self, config: SentinelConfig) -> &mut Self {
+    self.sentinel = Some(config);
+    self
+  }
+
+  /// Modify the client config in place, creating a new one with default centralized values first if needed.
+  pub fn with_config<F>(&mut self, func: F) -> &mut Self
+  where
+    F: FnOnce(&mut RedisConfig),
+  {
+    if let Some(config) = self.config.as_mut() {
+      func(config);
+    } else {
+      let mut config = RedisConfig::default();
+      func(&mut config);
+      self.config = Some(config);
+    }
+
+    self
+  }
+
+  /// Modify the performance config in place, creating a new one with default values first if needed.
+  pub fn with_performance_config<F>(&mut self, func: F) -> &mut Self
+  where
+    F: FnOnce(&mut PerformanceConfig),
+  {
+    func(&mut self.performance);
+    self
+  }
+
+  /// Modify the connection config in place, creating a new one with default values first if needed.
+  pub fn with_connection_config<F>(&mut self, func: F) -> &mut Self
+  where
+    F: FnOnce(&mut ConnectionConfig),
+  {
+    func(&mut self.connection);
+    self
+  }
+
+  /// Modify the sentinel config in place, creating a new one with default values first if needed.
+  #[cfg(feature = "sentinel-client")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))]
+  pub fn with_sentinel_config<F>(&mut self, func: F) -> &mut Self
+  where
+    F: FnOnce(&mut SentinelConfig),
+  {
+    if let Some(config) = self.sentinel.as_mut() {
+      func(config);
+    } else {
+      let mut config = SentinelConfig::default();
+      func(&mut config);
+      self.sentinel = Some(config);
+    }
+
+    self
+  }
+
+  /// Create a new client.
+  pub fn build(&self) -> Result<RedisClient, RedisError> {
+    if let Some(config) = self.config.as_ref() {
+      Ok(RedisClient::new(
+        config.clone(),
+        Some(self.performance.clone()),
+        Some(self.connection.clone()),
+        self.policy.clone(),
+      ))
+    } else {
+      Err(RedisError::new(RedisErrorKind::Config, "Missing client configuration."))
+    }
+  }
+
+  /// Create a new client pool.
+  pub fn build_pool(&self, size: usize) -> Result<RedisPool, RedisError> {
+    if let Some(config) = self.config.as_ref() {
+      RedisPool::new(
+        config.clone(),
+        Some(self.performance.clone()),
+        Some(self.connection.clone()),
+        self.policy.clone(),
+        size,
+      )
+    } else {
+      Err(RedisError::new(RedisErrorKind::Config, "Missing client configuration."))
+    }
+  }
+
+  /// Create a new exclusive client pool.
+  #[cfg(not(feature = "glommio"))]
+  pub fn build_exclusive_pool(&self, size: usize) -> Result<ExclusivePool, RedisError> {
+    if let Some(config) = self.config.as_ref() {
+      ExclusivePool::new(
+        config.clone(),
+        Some(self.performance.clone()),
+        Some(self.connection.clone()),
+        self.policy.clone(),
+        size,
+      )
+    } else {
+      Err(RedisError::new(RedisErrorKind::Config, "Missing client configuration."))
+    }
+  }
+
+  /// Create a new subscriber client.
+  #[cfg(feature = "subscriber-client")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "subscriber-client")))]
+  pub fn build_subscriber_client(&self) -> Result<SubscriberClient, RedisError> {
+    if let Some(config) = self.config.as_ref() {
+      Ok(SubscriberClient::new(
+        config.clone(),
+        Some(self.performance.clone()),
+        Some(self.connection.clone()),
+        self.policy.clone(),
+      ))
+    } else {
+      Err(RedisError::new(RedisErrorKind::Config, "Missing client configuration."))
+    }
+  }
+
+  /// Create a new sentinel client.
+  ///
+  /// This is only necessary if callers need to communicate directly with sentinel nodes. Use a
+  /// `ServerConfig::Sentinel` to interact with Redis servers behind a sentinel layer.
+  #[cfg(feature = "sentinel-client")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))]
+  pub fn build_sentinel_client(&self) -> Result<SentinelClient, RedisError> {
+    if let Some(config) = self.sentinel.as_ref() {
+      Ok(SentinelClient::new(
+        config.clone(),
+        Some(self.performance.clone()),
+        Some(self.connection.clone()),
+        self.policy.clone(),
+      ))
+    } else {
+      Err(RedisError::new(
+        RedisErrorKind::Config,
+        "Missing sentinel client configuration.",
+      ))
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/types/client.rs.html b/doc/src/fred/types/client.rs.html new file mode 100644 index 00000000..ac7b55db --- /dev/null +++ b/doc/src/fred/types/client.rs.html @@ -0,0 +1,401 @@ +client.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+
use crate::utils;
+use bytes_utils::Str;
+
+#[cfg(feature = "i-tracking")]
+use crate::{
+  error::{RedisError, RedisErrorKind},
+  types::{Message, RedisKey, RedisValue, Server},
+};
+
+/// The type of clients to close.
+///
+/// <https://redis.io/commands/client-kill>
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClientKillType {
+  Normal,
+  Master,
+  Replica,
+  Pubsub,
+}
+
+impl ClientKillType {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      ClientKillType::Normal => "normal",
+      ClientKillType::Master => "master",
+      ClientKillType::Replica => "replica",
+      ClientKillType::Pubsub => "pubsub",
+    })
+  }
+}
+
+/// Filters provided to the CLIENT KILL command.
+///
+/// <https://redis.io/commands/client-kill>
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClientKillFilter {
+  ID(String),
+  Type(ClientKillType),
+  User(String),
+  Addr(String),
+  LAddr(String),
+  SkipMe(bool),
+}
+
+impl ClientKillFilter {
+  pub(crate) fn to_str(&self) -> (Str, Str) {
+    let (prefix, value) = match *self {
+      ClientKillFilter::ID(ref id) => ("ID", id.into()),
+      ClientKillFilter::Type(ref kind) => ("TYPE", kind.to_str()),
+      ClientKillFilter::User(ref user) => ("USER", user.into()),
+      ClientKillFilter::Addr(ref addr) => ("ADDR", addr.into()),
+      ClientKillFilter::LAddr(ref addr) => ("LADDR", addr.into()),
+      ClientKillFilter::SkipMe(ref b) => ("SKIPME", match *b {
+        true => utils::static_str("yes"),
+        false => utils::static_str("no"),
+      }),
+    };
+
+    (utils::static_str(prefix), value)
+  }
+}
+
+/// Filters for the CLIENT PAUSE command.
+///
+/// <https://redis.io/commands/client-pause>
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClientPauseKind {
+  Write,
+  All,
+}
+
+impl ClientPauseKind {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      ClientPauseKind::Write => "WRITE",
+      ClientPauseKind::All => "ALL",
+    })
+  }
+}
+
+/// Arguments for the CLIENT REPLY command.
+///
+/// <https://redis.io/commands/client-reply>
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClientReplyFlag {
+  On,
+  Off,
+  Skip,
+}
+
+impl ClientReplyFlag {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      ClientReplyFlag::On => "ON",
+      ClientReplyFlag::Off => "OFF",
+      ClientReplyFlag::Skip => "SKIP",
+    })
+  }
+}
+
+/// An `ON|OFF` flag used with client tracking commands.
+#[cfg(feature = "i-tracking")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum Toggle {
+  On,
+  Off,
+}
+
+#[cfg(feature = "i-tracking")]
+impl Toggle {
+  pub(crate) fn to_str(&self) -> &'static str {
+    match self {
+      Toggle::On => "ON",
+      Toggle::Off => "OFF",
+    }
+  }
+
+  pub(crate) fn from_str(s: &str) -> Option<Self> {
+    Some(match s {
+      "ON" | "on" => Toggle::On,
+      "OFF" | "off" => Toggle::Off,
+      _ => return None,
+    })
+  }
+}
+
+#[cfg(feature = "i-tracking")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+impl TryFrom<&str> for Toggle {
+  type Error = RedisError;
+
+  fn try_from(value: &str) -> Result<Self, Self::Error> {
+    Toggle::from_str(value).ok_or(RedisError::new(RedisErrorKind::Parse, "Invalid toggle value."))
+  }
+}
+
+#[cfg(feature = "i-tracking")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+impl TryFrom<String> for Toggle {
+  type Error = RedisError;
+
+  fn try_from(value: String) -> Result<Self, Self::Error> {
+    Toggle::from_str(&value).ok_or(RedisError::new(RedisErrorKind::Parse, "Invalid toggle value."))
+  }
+}
+
+#[cfg(feature = "i-tracking")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+impl TryFrom<&String> for Toggle {
+  type Error = RedisError;
+
+  fn try_from(value: &String) -> Result<Self, Self::Error> {
+    Toggle::from_str(value).ok_or(RedisError::new(RedisErrorKind::Parse, "Invalid toggle value."))
+  }
+}
+
+#[cfg(feature = "i-tracking")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+impl From<bool> for Toggle {
+  fn from(value: bool) -> Self {
+    if value {
+      Toggle::On
+    } else {
+      Toggle::Off
+    }
+  }
+}
+
+/// A [client tracking](https://redis.io/docs/manual/client-side-caching/) invalidation message from the provided server.
+#[cfg(feature = "i-tracking")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Invalidation {
+  pub keys:   Vec<RedisKey>,
+  pub server: Server,
+}
+
+#[cfg(feature = "i-tracking")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+impl Invalidation {
+  pub(crate) fn from_message(message: Message, server: &Server) -> Option<Invalidation> {
+    Some(Invalidation {
+      keys:   match message.value {
+        RedisValue::Array(values) => values.into_iter().filter_map(|v| v.try_into().ok()).collect(),
+        RedisValue::String(s) => vec![s.into()],
+        RedisValue::Bytes(b) => vec![b.into()],
+        RedisValue::Double(f) => vec![f.into()],
+        RedisValue::Integer(i) => vec![i.into()],
+        RedisValue::Boolean(b) => vec![b.into()],
+        RedisValue::Null => vec![],
+        _ => {
+          trace!("Dropping invalid invalidation message.");
+          return None;
+        },
+      },
+      server: server.clone(),
+    })
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/types/cluster.rs.html b/doc/src/fred/types/cluster.rs.html new file mode 100644 index 00000000..ccdaa595 --- /dev/null +++ b/doc/src/fred/types/cluster.rs.html @@ -0,0 +1,307 @@ +cluster.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+
pub use crate::protocol::types::{ClusterRouting, SlotRange};
+use crate::{
+  error::{RedisError, RedisErrorKind},
+  types::RedisValue,
+  utils,
+};
+use bytes_utils::Str;
+
+macro_rules! parse_or_zero(
+  ($data:ident, $t:ty) => {
+    $data.parse::<$t>().ok().unwrap_or(0)
+  }
+);
+
+fn parse_cluster_info_line(info: &mut ClusterInfo, line: &str) -> Result<(), RedisError> {
+  let parts: Vec<&str> = line.split(':').collect();
+  if parts.len() != 2 {
+    return Err(RedisError::new(RedisErrorKind::Protocol, "Expected key:value pair."));
+  }
+  let (field, val) = (parts[0], parts[1]);
+
+  match field {
+    "cluster_state" => match val {
+      "ok" => info.cluster_state = ClusterState::Ok,
+      "fail" => info.cluster_state = ClusterState::Fail,
+      _ => return Err(RedisError::new(RedisErrorKind::Protocol, "Invalid cluster state.")),
+    },
+    "cluster_slots_assigned" => info.cluster_slots_assigned = parse_or_zero!(val, u16),
+    "cluster_slots_ok" => info.cluster_slots_ok = parse_or_zero!(val, u16),
+    "cluster_slots_pfail" => info.cluster_slots_pfail = parse_or_zero!(val, u16),
+    "cluster_slots_fail" => info.cluster_slots_fail = parse_or_zero!(val, u16),
+    "cluster_known_nodes" => info.cluster_known_nodes = parse_or_zero!(val, u16),
+    "cluster_size" => info.cluster_size = parse_or_zero!(val, u32),
+    "cluster_current_epoch" => info.cluster_current_epoch = parse_or_zero!(val, u64),
+    "cluster_my_epoch" => info.cluster_my_epoch = parse_or_zero!(val, u64),
+    "cluster_stats_messages_sent" => info.cluster_stats_messages_sent = parse_or_zero!(val, u64),
+    "cluster_stats_messages_received" => info.cluster_stats_messages_received = parse_or_zero!(val, u64),
+    _ => {
+      warn!("Invalid cluster info field: {}", line);
+    },
+  };
+
+  Ok(())
+}
+
+/// The state of the cluster from the `CLUSTER INFO` command.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClusterState {
+  Ok,
+  Fail,
+}
+
+impl Default for ClusterState {
+  fn default() -> Self {
+    ClusterState::Ok
+  }
+}
+
+/// A parsed response from the `CLUSTER INFO` command.
+///
+/// <https://redis.io/commands/cluster-info>
+#[derive(Clone, Debug, Eq, PartialEq, Default)]
+pub struct ClusterInfo {
+  pub cluster_state:                   ClusterState,
+  pub cluster_slots_assigned:          u16,
+  pub cluster_slots_ok:                u16,
+  pub cluster_slots_pfail:             u16,
+  pub cluster_slots_fail:              u16,
+  pub cluster_known_nodes:             u16,
+  pub cluster_size:                    u32,
+  pub cluster_current_epoch:           u64,
+  pub cluster_my_epoch:                u64,
+  pub cluster_stats_messages_sent:     u64,
+  pub cluster_stats_messages_received: u64,
+}
+
+impl TryFrom<RedisValue> for ClusterInfo {
+  type Error = RedisError;
+
+  fn try_from(value: RedisValue) -> Result<Self, Self::Error> {
+    if let Some(data) = value.as_bytes_str() {
+      let mut out = ClusterInfo::default();
+
+      for line in data.lines() {
+        let trimmed = line.trim();
+        if !trimmed.is_empty() {
+          parse_cluster_info_line(&mut out, trimmed)?;
+        }
+      }
+      Ok(out)
+    } else {
+      Err(RedisError::new(RedisErrorKind::Protocol, "Expected string response."))
+    }
+  }
+}
+
+/// Options for the CLUSTER FAILOVER command.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClusterFailoverFlag {
+  Force,
+  Takeover,
+}
+
+impl ClusterFailoverFlag {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      ClusterFailoverFlag::Force => "FORCE",
+      ClusterFailoverFlag::Takeover => "TAKEOVER",
+    })
+  }
+}
+
+/// Flags for the CLUSTER RESET command.
+///
+/// <https://redis.io/commands/cluster-reset>
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClusterResetFlag {
+  Hard,
+  Soft,
+}
+
+impl ClusterResetFlag {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      ClusterResetFlag::Hard => "HARD",
+      ClusterResetFlag::Soft => "SOFT",
+    })
+  }
+}
+
+/// Flags for the CLUSTER SETSLOT command.
+///
+/// <https://redis.io/commands/cluster-setslot>
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClusterSetSlotState {
+  Importing,
+  Migrating,
+  Stable,
+  Node(String),
+}
+
+impl ClusterSetSlotState {
+  pub(crate) fn to_str(&self) -> (Str, Option<Str>) {
+    let (prefix, value) = match *self {
+      ClusterSetSlotState::Importing => ("IMPORTING", None),
+      ClusterSetSlotState::Migrating => ("MIGRATING", None),
+      ClusterSetSlotState::Stable => ("STABLE", None),
+      ClusterSetSlotState::Node(ref n) => ("NODE", Some(n.into())),
+    };
+
+    (utils::static_str(prefix), value)
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/types/config.rs.html b/doc/src/fred/types/config.rs.html new file mode 100644 index 00000000..c9e7adb6 --- /dev/null +++ b/doc/src/fred/types/config.rs.html @@ -0,0 +1,3515 @@ +config.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
+1265
+1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+1274
+1275
+1276
+1277
+1278
+1279
+1280
+1281
+1282
+1283
+1284
+1285
+1286
+1287
+1288
+1289
+1290
+1291
+1292
+1293
+1294
+1295
+1296
+1297
+1298
+1299
+1300
+1301
+1302
+1303
+1304
+1305
+1306
+1307
+1308
+1309
+1310
+1311
+1312
+1313
+1314
+1315
+1316
+1317
+1318
+1319
+1320
+1321
+1322
+1323
+1324
+1325
+1326
+1327
+1328
+1329
+1330
+1331
+1332
+1333
+1334
+1335
+1336
+1337
+1338
+1339
+1340
+1341
+1342
+1343
+1344
+1345
+1346
+1347
+1348
+1349
+1350
+1351
+1352
+1353
+1354
+1355
+1356
+1357
+1358
+1359
+1360
+1361
+1362
+1363
+1364
+1365
+1366
+1367
+1368
+1369
+1370
+1371
+1372
+1373
+1374
+1375
+1376
+1377
+1378
+1379
+1380
+1381
+1382
+1383
+1384
+1385
+1386
+1387
+1388
+1389
+1390
+1391
+1392
+1393
+1394
+1395
+1396
+1397
+1398
+1399
+1400
+1401
+1402
+1403
+1404
+1405
+1406
+1407
+1408
+1409
+1410
+1411
+1412
+1413
+1414
+1415
+1416
+1417
+1418
+1419
+1420
+1421
+1422
+1423
+1424
+1425
+1426
+1427
+1428
+1429
+1430
+1431
+1432
+1433
+1434
+1435
+1436
+1437
+1438
+1439
+1440
+1441
+1442
+1443
+1444
+1445
+1446
+1447
+1448
+1449
+1450
+1451
+1452
+1453
+1454
+1455
+1456
+1457
+1458
+1459
+1460
+1461
+1462
+1463
+1464
+1465
+1466
+1467
+1468
+1469
+1470
+1471
+1472
+1473
+1474
+1475
+1476
+1477
+1478
+1479
+1480
+1481
+1482
+1483
+1484
+1485
+1486
+1487
+1488
+1489
+1490
+1491
+1492
+1493
+1494
+1495
+1496
+1497
+1498
+1499
+1500
+1501
+1502
+1503
+1504
+1505
+1506
+1507
+1508
+1509
+1510
+1511
+1512
+1513
+1514
+1515
+1516
+1517
+1518
+1519
+1520
+1521
+1522
+1523
+1524
+1525
+1526
+1527
+1528
+1529
+1530
+1531
+1532
+1533
+1534
+1535
+1536
+1537
+1538
+1539
+1540
+1541
+1542
+1543
+1544
+1545
+1546
+1547
+1548
+1549
+1550
+1551
+1552
+1553
+1554
+1555
+1556
+1557
+1558
+1559
+1560
+1561
+1562
+1563
+1564
+1565
+1566
+1567
+1568
+1569
+1570
+1571
+1572
+1573
+1574
+1575
+1576
+1577
+1578
+1579
+1580
+1581
+1582
+1583
+1584
+1585
+1586
+1587
+1588
+1589
+1590
+1591
+1592
+1593
+1594
+1595
+1596
+1597
+1598
+1599
+1600
+1601
+1602
+1603
+1604
+1605
+1606
+1607
+1608
+1609
+1610
+1611
+1612
+1613
+1614
+1615
+1616
+1617
+1618
+1619
+1620
+1621
+1622
+1623
+1624
+1625
+1626
+1627
+1628
+1629
+1630
+1631
+1632
+1633
+1634
+1635
+1636
+1637
+1638
+1639
+1640
+1641
+1642
+1643
+1644
+1645
+1646
+1647
+1648
+1649
+1650
+1651
+1652
+1653
+1654
+1655
+1656
+1657
+1658
+1659
+1660
+1661
+1662
+1663
+1664
+1665
+1666
+1667
+1668
+1669
+1670
+1671
+1672
+1673
+1674
+1675
+1676
+1677
+1678
+1679
+1680
+1681
+1682
+1683
+1684
+1685
+1686
+1687
+1688
+1689
+1690
+1691
+1692
+1693
+1694
+1695
+1696
+1697
+1698
+1699
+1700
+1701
+1702
+1703
+1704
+1705
+1706
+1707
+1708
+1709
+1710
+1711
+1712
+1713
+1714
+1715
+1716
+1717
+1718
+1719
+1720
+1721
+1722
+1723
+1724
+1725
+1726
+1727
+1728
+1729
+1730
+1731
+1732
+1733
+1734
+1735
+1736
+1737
+1738
+1739
+1740
+1741
+1742
+1743
+1744
+1745
+1746
+1747
+1748
+1749
+1750
+1751
+1752
+1753
+1754
+1755
+1756
+1757
+
pub use crate::protocol::types::Server;
+use crate::{error::RedisError, protocol::command::RedisCommand, types::RespVersion, utils};
+use socket2::TcpKeepalive;
+use std::{cmp, time::Duration};
+use url::Url;
+
+use crate::error::RedisErrorKind;
+#[cfg(feature = "mocks")]
+use crate::mocks::Mocks;
+#[cfg(feature = "unix-sockets")]
+use std::path::PathBuf;
+#[cfg(feature = "mocks")]
+use std::sync::Arc;
+
+#[cfg(any(
+  feature = "enable-rustls",
+  feature = "enable-native-tls",
+  feature = "enable-rustls-ring"
+))]
+#[cfg_attr(
+  docsrs,
+  doc(cfg(any(
+    feature = "enable-rustls",
+    feature = "enable-native-tls",
+    feature = "enable-rustls-ring"
+  )))
+)]
+pub use crate::protocol::tls::{HostMapping, TlsConfig, TlsConnector, TlsHostMapping};
+
+#[cfg(feature = "replicas")]
+#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+pub use crate::router::replicas::{ReplicaConfig, ReplicaFilter};
+use crate::types::ClusterHash;
+
+/// The default amount of jitter when waiting to reconnect.
+pub const DEFAULT_JITTER_MS: u32 = 100;
+
+/// Special errors that can trigger reconnection logic, which can also retry the failing command if possible.
+///
+/// `MOVED`, `ASK`, and `NOAUTH` errors are handled separately by the client.
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[cfg(feature = "custom-reconnect-errors")]
+#[cfg_attr(docsrs, doc(cfg(feature = "custom-reconnect-errors")))]
+pub enum ReconnectError {
+  /// The CLUSTERDOWN prefix.
+  ClusterDown,
+  /// The LOADING prefix.
+  Loading,
+  /// The MASTERDOWN prefix.
+  MasterDown,
+  /// The READONLY prefix, which can happen if a primary node is switched to a replica without any connection
+  /// interruption.
+  ReadOnly,
+  /// The MISCONF prefix.
+  Misconf,
+  /// The BUSY prefix.
+  Busy,
+  /// The NOREPLICAS prefix.
+  NoReplicas,
+  /// A case-sensitive prefix on an error message.
+  ///
+  /// See [the source](https://github.com/redis/redis/blob/fe37e4fc874a92dcf61b3b0de899ec6f674d2442/src/server.c#L1845) for examples.
+  Custom(&'static str),
+}
+
+#[cfg(feature = "custom-reconnect-errors")]
+impl ReconnectError {
+  pub(crate) fn to_str(&self) -> &'static str {
+    use ReconnectError::*;
+
+    match self {
+      ClusterDown => "CLUSTERDOWN",
+      Loading => "LOADING",
+      MasterDown => "MASTERDOWN",
+      ReadOnly => "READONLY",
+      Misconf => "MISCONF",
+      Busy => "BUSY",
+      NoReplicas => "NOREPLICAS",
+      Custom(prefix) => prefix,
+    }
+  }
+}
+
+/// The type of reconnection policy to use. This will apply to every connection used by the client.
+///
+/// Use a `max_attempts` value of `0` to retry forever.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ReconnectPolicy {
+  /// Wait a constant amount of time between reconnect attempts, in ms.
+  Constant {
+    attempts:     u32,
+    max_attempts: u32,
+    delay:        u32,
+    jitter:       u32,
+  },
+  /// Backoff reconnection attempts linearly, adding `delay` each time.
+  Linear {
+    attempts:     u32,
+    max_attempts: u32,
+    max_delay:    u32,
+    delay:        u32,
+    jitter:       u32,
+  },
+  /// Backoff reconnection attempts exponentially, multiplying the last delay by `mult` each time.
+  Exponential {
+    attempts:     u32,
+    max_attempts: u32,
+    min_delay:    u32,
+    max_delay:    u32,
+    mult:         u32,
+    jitter:       u32,
+  },
+}
+
+impl Default for ReconnectPolicy {
+  fn default() -> Self {
+    ReconnectPolicy::Constant {
+      attempts:     0,
+      max_attempts: 0,
+      delay:        1000,
+      jitter:       DEFAULT_JITTER_MS,
+    }
+  }
+}
+
+impl ReconnectPolicy {
+  /// Create a new reconnect policy with a constant backoff.
+  pub fn new_constant(max_attempts: u32, delay: u32) -> ReconnectPolicy {
+    ReconnectPolicy::Constant {
+      max_attempts,
+      delay,
+      attempts: 0,
+      jitter: DEFAULT_JITTER_MS,
+    }
+  }
+
+  /// Create a new reconnect policy with a linear backoff.
+  pub fn new_linear(max_attempts: u32, max_delay: u32, delay: u32) -> ReconnectPolicy {
+    ReconnectPolicy::Linear {
+      max_attempts,
+      max_delay,
+      delay,
+      attempts: 0,
+      jitter: DEFAULT_JITTER_MS,
+    }
+  }
+
+  /// Create a new reconnect policy with an exponential backoff.
+  pub fn new_exponential(max_attempts: u32, min_delay: u32, max_delay: u32, mult: u32) -> ReconnectPolicy {
+    ReconnectPolicy::Exponential {
+      max_delay,
+      max_attempts,
+      min_delay,
+      mult,
+      attempts: 0,
+      jitter: DEFAULT_JITTER_MS,
+    }
+  }
+
+  /// Set the amount of jitter to add to each reconnect delay.
+  ///
+  /// Default: 50 ms
+  pub fn set_jitter(&mut self, jitter_ms: u32) {
+    match self {
+      ReconnectPolicy::Constant { ref mut jitter, .. } => {
+        *jitter = jitter_ms;
+      },
+      ReconnectPolicy::Linear { ref mut jitter, .. } => {
+        *jitter = jitter_ms;
+      },
+      ReconnectPolicy::Exponential { ref mut jitter, .. } => {
+        *jitter = jitter_ms;
+      },
+    }
+  }
+
+  /// Reset the number of reconnection attempts.
+  pub(crate) fn reset_attempts(&mut self) {
+    match *self {
+      ReconnectPolicy::Constant { ref mut attempts, .. } => {
+        *attempts = 0;
+      },
+      ReconnectPolicy::Linear { ref mut attempts, .. } => {
+        *attempts = 0;
+      },
+      ReconnectPolicy::Exponential { ref mut attempts, .. } => {
+        *attempts = 0;
+      },
+    }
+  }
+
+  /// Read the number of reconnection attempts.
+  pub fn attempts(&self) -> u32 {
+    match *self {
+      ReconnectPolicy::Constant { ref attempts, .. } => *attempts,
+      ReconnectPolicy::Linear { ref attempts, .. } => *attempts,
+      ReconnectPolicy::Exponential { ref attempts, .. } => *attempts,
+    }
+  }
+
+  /// Whether the client should initiate a reconnect.
+  pub(crate) fn should_reconnect(&self) -> bool {
+    match *self {
+      ReconnectPolicy::Constant {
+        ref attempts,
+        ref max_attempts,
+        ..
+      } => *max_attempts == 0 || *attempts < *max_attempts,
+      ReconnectPolicy::Linear {
+        ref attempts,
+        ref max_attempts,
+        ..
+      } => *max_attempts == 0 || *attempts < *max_attempts,
+      ReconnectPolicy::Exponential {
+        ref attempts,
+        ref max_attempts,
+        ..
+      } => *max_attempts == 0 || *attempts < *max_attempts,
+    }
+  }
+
+  /// Calculate the next delay, incrementing `attempts` in the process.
+  pub fn next_delay(&mut self) -> Option<u64> {
+    match *self {
+      ReconnectPolicy::Constant {
+        ref mut attempts,
+        delay,
+        max_attempts,
+        jitter,
+      } => {
+        *attempts = match utils::incr_with_max(*attempts, max_attempts) {
+          Some(a) => a,
+          None => return None,
+        };
+
+        Some(utils::add_jitter(delay as u64, jitter))
+      },
+      ReconnectPolicy::Linear {
+        ref mut attempts,
+        max_delay,
+        max_attempts,
+        delay,
+        jitter,
+      } => {
+        *attempts = match utils::incr_with_max(*attempts, max_attempts) {
+          Some(a) => a,
+          None => return None,
+        };
+        let delay = (delay as u64).saturating_mul(*attempts as u64);
+
+        Some(cmp::min(max_delay as u64, utils::add_jitter(delay, jitter)))
+      },
+      ReconnectPolicy::Exponential {
+        ref mut attempts,
+        min_delay,
+        max_delay,
+        max_attempts,
+        mult,
+        jitter,
+      } => {
+        *attempts = match utils::incr_with_max(*attempts, max_attempts) {
+          Some(a) => a,
+          None => return None,
+        };
+        let delay = (mult as u64)
+          .saturating_pow(*attempts - 1)
+          .saturating_mul(min_delay as u64);
+
+        Some(cmp::min(max_delay as u64, utils::add_jitter(delay, jitter)))
+      },
+    }
+  }
+}
+
+/// Describes how the client should respond when a command is sent while the client is in a blocked state from a
+/// blocking command.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum Blocking {
+  /// Wait to send the command until the blocked command finishes. (Default)
+  Block,
+  /// Return an error to the caller.
+  Error,
+  /// Interrupt the blocked command by automatically sending `CLIENT UNBLOCK` for the blocked connection.
+  Interrupt,
+}
+
+impl Default for Blocking {
+  fn default() -> Self {
+    Blocking::Block
+  }
+}
+
+/// Backpressure policies to apply when the max number of in-flight commands is reached on a connection.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum BackpressurePolicy {
+  /// Sleep for some amount of time before sending the next command.
+  Sleep {
+    /// Disable the backpressure scaling logic used to calculate the `sleep` duration when throttling commands.
+    ///
+    /// If `true` the client will always wait a constant amount of time defined by `min_sleep_duration_ms` when
+    /// throttling commands. Otherwise the sleep duration will scale based on the number of in-flight commands.
+    ///
+    /// Default: `false`
+    disable_backpressure_scaling: bool,
+    /// The minimum amount of time to wait when applying backpressure to a command.
+    ///
+    /// If `0` then no backpressure will be applied, but backpressure errors will not be surfaced to callers unless
+    /// `disable_auto_backpressure` is `true`.
+    ///
+    /// Default: 10 ms
+    min_sleep_duration:           Duration,
+  },
+  /// Wait for all in-flight commands to finish before sending the next command.
+  Drain,
+}
+
+impl Default for BackpressurePolicy {
+  fn default() -> Self {
+    BackpressurePolicy::Drain
+  }
+}
+
+impl BackpressurePolicy {
+  /// Create a new `Sleep` policy with the legacy default values.
+  pub fn default_sleep() -> Self {
+    BackpressurePolicy::Sleep {
+      disable_backpressure_scaling: false,
+      min_sleep_duration:           Duration::from_millis(10),
+    }
+  }
+}
+
+/// Configuration options for backpressure features in the client.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct BackpressureConfig {
+  /// Whether to disable the automatic backpressure features when pipelining is enabled.
+  ///
+  /// If `true` then `RedisErrorKind::Backpressure` errors may be surfaced to callers. Callers can set this to `true`
+  /// and `max_in_flight_commands` to `0` to effectively disable the backpressure logic.
+  ///
+  /// Default: `false`
+  pub disable_auto_backpressure: bool,
+  /// The maximum number of in-flight commands (per connection) before backpressure will be applied.
+  ///
+  /// Default: 10_000
+  pub max_in_flight_commands:    u64,
+  /// The backpressure policy to apply when the max number of in-flight commands is reached.
+  ///
+  /// Default: [Drain](crate::types::BackpressurePolicy::Drain).
+  pub policy:                    BackpressurePolicy,
+}
+
+impl Default for BackpressureConfig {
+  fn default() -> Self {
+    BackpressureConfig {
+      disable_auto_backpressure: false,
+      max_in_flight_commands:    10_000,
+      policy:                    BackpressurePolicy::default(),
+    }
+  }
+}
+
+/// TCP configuration options.
+#[derive(Clone, Debug, Default)]
+pub struct TcpConfig {
+  /// Set the [TCP_NODELAY](https://docs.rs/tokio/latest/tokio/net/struct.TcpStream.html#method.set_nodelay) value.
+  pub nodelay:   Option<bool>,
+  /// Set the [SO_LINGER](https://docs.rs/tokio/latest/tokio/net/struct.TcpStream.html#method.set_linger) value.
+  pub linger:    Option<Duration>,
+  /// Set the [IP_TTL](https://docs.rs/tokio/latest/tokio/net/struct.TcpStream.html#method.set_ttl) value.
+  pub ttl:       Option<u32>,
+  /// Set the [TCP keepalive values](https://docs.rs/socket2/latest/socket2/struct.Socket.html#method.set_tcp_keepalive).
+  pub keepalive: Option<TcpKeepalive>,
+}
+
+impl PartialEq for TcpConfig {
+  fn eq(&self, other: &Self) -> bool {
+    self.nodelay == other.nodelay && self.linger == other.linger && self.ttl == other.ttl
+  }
+}
+
+impl Eq for TcpConfig {}
+
+/// Configuration options used to detect potentially unresponsive connections.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct UnresponsiveConfig {
+  /// If provided, the amount of time a frame can wait without a response before the associated connection is
+  /// considered unresponsive.
+  ///
+  /// If a connection is considered unresponsive it will be forcefully closed and the client will reconnect based on
+  /// the [ReconnectPolicy](crate::types::ReconnectPolicy). This heuristic can be useful in environments where
+  /// connections may close or change in subtle or unexpected ways.
+  ///
+  /// Unlike the [timeout](crate::types::Options) and [default_command_timeout](crate::types::PerformanceConfig)
+  /// interfaces, any in-flight commands waiting on a response when the connection is closed this way will be
+  /// retried based on the associated [ReconnectPolicy](crate::types::ReconnectPolicy) and
+  /// [Options](crate::types::Options).
+  ///
+  /// Default: `None`
+  pub max_timeout: Option<Duration>,
+  /// The frequency at which the client checks for unresponsive connections.
+  ///
+  /// This value should usually be less than half of `max_timeout` and always more than 1 ms.
+  ///
+  /// Default: 2 sec
+  pub interval:    Duration,
+}
+
+impl Default for UnresponsiveConfig {
+  fn default() -> Self {
+    UnresponsiveConfig {
+      max_timeout: None,
+      interval:    Duration::from_secs(2),
+    }
+  }
+}
+
+/// A policy that determines how clustered clients initially connect to and discover other cluster nodes.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClusterDiscoveryPolicy {
+  /// Always use the endpoint(s) provided in the client's [ServerConfig](ServerConfig).
+  ///
+  /// This is generally recommended with managed services, Kubernetes, or other systems that provide client routing
+  /// or cluster discovery interfaces.
+  ///
+  /// Default.
+  ConfigEndpoint,
+  /// Try connecting to nodes specified in both the client's [ServerConfig](ServerConfig) and the most recently
+  /// cached routing table.
+  UseCache,
+}
+
+impl Default for ClusterDiscoveryPolicy {
+  fn default() -> Self {
+    ClusterDiscoveryPolicy::ConfigEndpoint
+  }
+}
+
+/// Configuration options related to the creation or management of TCP connection.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct ConnectionConfig {
+  /// The timeout to apply when attempting to create a new TCP connection.
+  ///
+  /// This also includes the TLS handshake if using any of the TLS features.
+  ///
+  /// Default: 10 sec
+  pub connection_timeout:           Duration,
+  /// The timeout to apply when sending internal commands such as `AUTH`, `SELECT`, `CLUSTER SLOTS`, `READONLY`, etc.
+  ///
+  /// Default: 10 sec
+  pub internal_command_timeout:     Duration,
+  /// The amount of time to wait after a `MOVED` error is received before the client will update the cached cluster
+  /// state.
+  ///
+  /// Default: `0`
+  pub cluster_cache_update_delay:   Duration,
+  /// The maximum number of times the client will attempt to send a command.
+  ///
+  /// This value be incremented whenever the connection closes while the command is in-flight.
+  ///
+  /// Default: `3`
+  pub max_command_attempts:         u32,
+  /// The maximum number of times the client will attempt to follow a `MOVED` or `ASK` redirection per command.
+  ///
+  /// Default: `5`
+  pub max_redirections:             u32,
+  /// Unresponsive connection configuration options.
+  pub unresponsive:                 UnresponsiveConfig,
+  /// An unexpected `NOAUTH` error is treated the same as a general connection failure, causing the client to
+  /// reconnect based on the [ReconnectPolicy](crate::types::ReconnectPolicy). This is [recommended](https://github.com/StackExchange/StackExchange.Redis/issues/1273#issuecomment-651823824) if callers are using ElastiCache.
+  ///
+  /// Default: `false`
+  pub reconnect_on_auth_error:      bool,
+  /// Automatically send `CLIENT SETNAME` on each connection associated with a client instance.
+  ///
+  /// Default: `false`
+  pub auto_client_setname:          bool,
+  /// Limit the size of the internal in-memory command queue.
+  ///
+  /// Commands that exceed this limit will receive a `RedisErrorKind::Backpressure` error.
+  ///
+  /// See [command_queue_len](crate::interfaces::MetricsInterface::command_queue_len) for more information.
+  ///
+  /// Default: `0` (unlimited)
+  pub max_command_buffer_len:       usize,
+  /// Disable the `CLUSTER INFO` health check when initializing cluster connections.
+  ///
+  /// Default: `false`
+  pub disable_cluster_health_check: bool,
+  /// Configuration options for replica nodes.
+  ///
+  /// Default: `None`
+  #[cfg(feature = "replicas")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
+  pub replica:                      ReplicaConfig,
+  /// TCP connection options.
+  pub tcp:                          TcpConfig,
+  /// Errors that should trigger reconnection logic.
+  #[cfg(feature = "custom-reconnect-errors")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "custom-reconnect-errors")))]
+  pub reconnect_errors:             Vec<ReconnectError>,
+
+  /// The task queue onto which routing tasks will be spawned.
+  #[cfg(feature = "glommio")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "glommio")))]
+  pub router_task_queue: Option<glommio::TaskQueueHandle>,
+
+  /// The task queue onto which connection reader tasks will be spawned.
+  #[cfg(feature = "glommio")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "glommio")))]
+  pub connection_task_queue: Option<glommio::TaskQueueHandle>,
+}
+
+impl Default for ConnectionConfig {
+  fn default() -> Self {
+    ConnectionConfig {
+      connection_timeout: Duration::from_millis(10_000),
+      internal_command_timeout: Duration::from_millis(10_000),
+      max_redirections: 5,
+      max_command_attempts: 3,
+      max_command_buffer_len: 0,
+      auto_client_setname: false,
+      cluster_cache_update_delay: Duration::from_millis(0),
+      reconnect_on_auth_error: false,
+      disable_cluster_health_check: false,
+      tcp: TcpConfig::default(),
+      unresponsive: UnresponsiveConfig::default(),
+      #[cfg(feature = "replicas")]
+      replica: ReplicaConfig::default(),
+      #[cfg(feature = "custom-reconnect-errors")]
+      reconnect_errors: vec![
+        ReconnectError::ClusterDown,
+        ReconnectError::Loading,
+        ReconnectError::ReadOnly,
+      ],
+      #[cfg(feature = "glommio")]
+      router_task_queue: None,
+      #[cfg(feature = "glommio")]
+      connection_task_queue: None,
+    }
+  }
+}
+
+/// Configuration options that can affect the performance of the client.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct PerformanceConfig {
+  /// Whether the client should automatically pipeline commands across tasks when possible.
+  ///
+  /// The [Pipeline](crate::clients::Pipeline) interface can be used to pipeline commands within one task,
+  /// whereas this flag can automatically pipeline commands across tasks.
+  ///
+  /// Default: `true`
+  pub auto_pipeline:              bool,
+  /// Configuration options for backpressure features in the client.
+  pub backpressure:               BackpressureConfig,
+  /// An optional timeout to apply to all commands.
+  ///
+  /// If `0` this will disable any timeout being applied to commands. Callers can also set timeouts on individual
+  /// commands via the [with_options](crate::interfaces::ClientLike::with_options) interface.
+  ///
+  /// Default: `0`
+  pub default_command_timeout:    Duration,
+  /// The maximum number of frames that will be fed to a socket before flushing.
+  ///
+  /// Note: in some circumstances the client with always flush the socket (`QUIT`, `EXEC`, etc).
+  ///
+  /// Default: 200
+  pub max_feed_count:             u64,
+  /// The default capacity used when creating [broadcast channels](https://docs.rs/tokio/latest/tokio/sync/broadcast/fn.channel.html) in the [EventInterface](crate::interfaces::EventInterface).
+  ///
+  /// Default: 32
+  pub broadcast_channel_capacity: usize,
+  /// The minimum size, in bytes, of frames that should be encoded or decoded with a blocking task.
+  ///
+  /// See [block_in_place](https://docs.rs/tokio/latest/tokio/task/fn.block_in_place.html) for more information.
+  ///
+  /// Default: 50_000_000
+  #[cfg(feature = "blocking-encoding")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "blocking-encoding")))]
+  pub blocking_encode_threshold:  usize,
+}
+
+impl Default for PerformanceConfig {
+  fn default() -> Self {
+    PerformanceConfig {
+      auto_pipeline: true,
+      backpressure: BackpressureConfig::default(),
+      default_command_timeout: Duration::from_millis(0),
+      max_feed_count: 200,
+      broadcast_channel_capacity: 32,
+      #[cfg(feature = "blocking-encoding")]
+      blocking_encode_threshold: 50_000_000,
+    }
+  }
+}
+
+/// Configuration options for a `RedisClient`.
+#[derive(Clone, Debug)]
+pub struct RedisConfig {
+  /// Whether the client should return an error if it cannot connect to the server the first time when being
+  /// initialized. If `false` the client will run the reconnect logic if it cannot connect to the server the first
+  /// time, but if `true` the client will return initial connection errors to the caller immediately.
+  ///
+  /// Normally the reconnection logic only applies to connections that close unexpectedly, but this flag can apply
+  /// the same logic to the first connection as it is being created.
+  ///
+  /// Callers should use caution setting this to `false` since it can make debugging configuration issues more
+  /// difficult.
+  ///
+  /// Default: `true`
+  pub fail_fast: bool,
+  /// The default behavior of the client when a command is sent while the connection is blocked on a blocking
+  /// command.
+  ///
+  /// Setting this to anything other than `Blocking::Block` incurs a small performance penalty.
+  ///
+  /// Default: `Blocking::Block`
+  pub blocking:  Blocking,
+  /// An optional ACL username for the client to use when authenticating. If ACL rules are not configured this should
+  /// be `None`.
+  ///
+  /// Default: `None`
+  pub username:  Option<String>,
+  /// An optional password for the client to use when authenticating.
+  ///
+  /// Default: `None`
+  pub password:  Option<String>,
+  /// Connection configuration for the server(s).
+  ///
+  /// Default: `Centralized(localhost, 6379)`
+  pub server:    ServerConfig,
+  /// The protocol version to use when communicating with the server(s).
+  ///
+  /// If RESP3 is specified the client will automatically use `HELLO` when authenticating. **This requires Redis
+  /// >=6.0.0.** If the `HELLO` command fails this will prevent the client from connecting. Callers should set this
+  /// to RESP2 and use `HELLO` manually to fall back to RESP2 if needed.
+  ///
+  /// Note: upgrading an existing codebase from RESP2 to RESP3 may require changing certain type signatures. RESP3
+  /// has a slightly different type system than RESP2.
+  ///
+  /// Default: `RESP2`
+  pub version:   RespVersion,
+  /// An optional database number that the client will automatically `SELECT` after connecting or reconnecting.
+  ///
+  /// It is recommended that callers use this field instead of putting a `select()` call inside the `on_reconnect`
+  /// block, if possible. Commands that were in-flight when the connection closed will retry before anything inside
+  /// the `on_reconnect` block.
+  ///
+  /// Default: `None`
+  pub database:  Option<u8>,
+  /// TLS configuration options.
+  ///
+  /// Default: `None`
+  #[cfg(any(
+    feature = "enable-native-tls",
+    feature = "enable-rustls",
+    feature = "enable-rustls-ring"
+  ))]
+  #[cfg_attr(
+    docsrs,
+    doc(cfg(any(
+      feature = "enable-native-tls",
+      feature = "enable-rustls",
+      feature = "enable-rustls-ring"
+    )))
+  )]
+  pub tls:       Option<TlsConfig>,
+  /// Tracing configuration options.
+  #[cfg(feature = "partial-tracing")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))]
+  pub tracing:   TracingConfig,
+  /// An optional [mocking layer](crate::mocks) to intercept and process commands.
+  ///
+  /// Default: `None`
+  #[cfg(feature = "mocks")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "mocks")))]
+  pub mocks:     Option<Arc<dyn Mocks>>,
+}
+
+impl PartialEq for RedisConfig {
+  fn eq(&self, other: &Self) -> bool {
+    self.server == other.server
+      && self.database == other.database
+      && self.fail_fast == other.fail_fast
+      && self.version == other.version
+      && self.username == other.username
+      && self.password == other.password
+      && self.blocking == other.blocking
+  }
+}
+
+impl Eq for RedisConfig {}
+
+impl Default for RedisConfig {
+  fn default() -> Self {
+    RedisConfig {
+      fail_fast:                                   true,
+      blocking:                                    Blocking::default(),
+      username:                                    None,
+      password:                                    None,
+      server:                                      ServerConfig::default(),
+      version:                                     RespVersion::RESP2,
+      database:                                    None,
+      #[cfg(any(
+        feature = "enable-native-tls",
+        feature = "enable-rustls",
+        feature = "enable-rustls-ring"
+      ))]
+      tls:                                         None,
+      #[cfg(feature = "partial-tracing")]
+      tracing:                                     TracingConfig::default(),
+      #[cfg(feature = "mocks")]
+      mocks:                                       None,
+    }
+  }
+}
+
+#[cfg_attr(docsrs, allow(rustdoc::broken_intra_doc_links))]
+impl RedisConfig {
+  /// Whether the client uses TLS.
+  #[cfg(any(
+    feature = "enable-native-tls",
+    feature = "enable-rustls",
+    feature = "enable-rustls-ring"
+  ))]
+  pub fn uses_tls(&self) -> bool {
+    self.tls.is_some()
+  }
+
+  /// Whether the client uses TLS.
+  #[cfg(not(any(
+    feature = "enable-native-tls",
+    feature = "enable-rustls",
+    feature = "enable-rustls-ring"
+  )))]
+  pub fn uses_tls(&self) -> bool {
+    false
+  }
+
+  /// Whether the client uses a `native-tls` connector.
+  #[cfg(feature = "enable-native-tls")]
+  pub fn uses_native_tls(&self) -> bool {
+    self
+      .tls
+      .as_ref()
+      .map(|config| matches!(config.connector, TlsConnector::Native(_)))
+      .unwrap_or(false)
+  }
+
+  /// Whether the client uses a `native-tls` connector.
+  #[cfg(not(feature = "enable-native-tls"))]
+  pub fn uses_native_tls(&self) -> bool {
+    false
+  }
+
+  /// Whether the client uses a `rustls` connector.
+  #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))]
+  pub fn uses_rustls(&self) -> bool {
+    self
+      .tls
+      .as_ref()
+      .map(|config| matches!(config.connector, TlsConnector::Rustls(_)))
+      .unwrap_or(false)
+  }
+
+  /// Whether the client uses a `rustls` connector.
+  #[cfg(not(any(feature = "enable-rustls", feature = "enable-rustls-ring")))]
+  pub fn uses_rustls(&self) -> bool {
+    false
+  }
+
+  /// Parse a URL string into a `RedisConfig`.
+  ///
+  /// # URL Syntax
+  ///
+  /// **Centralized**
+  ///
+  /// ```text
+  /// redis|rediss :// [[username:]password@] host [:port][/database]
+  /// ```
+  ///
+  /// **Clustered**
+  ///
+  /// ```text
+  /// redis|rediss[-cluster] :// [[username:]password@] host [:port][?[node=host1:port1][&node=host2:port2][&node=hostN:portN]]
+  /// ```
+  ///
+  /// **Sentinel**
+  ///
+  /// ```text
+  /// redis|rediss[-sentinel] :// [[username1:]password1@] host [:port][/database][?[node=host1:port1][&node=host2:port2][&node=hostN:portN]
+  ///                             [&sentinelServiceName=myservice][&sentinelUsername=username2][&sentinelPassword=password2]]
+  /// ```
+  ///
+  /// **Unix Socket**
+  ///
+  /// ```text
+  /// redis+unix:// [[username:]password@] /path/to/redis.sock
+  /// ```
+  ///
+  /// # Schemes
+  ///
+  /// This function will use the URL scheme to determine which server type the caller is using. Valid schemes include:
+  ///
+  /// * `redis` - TCP connected to a centralized server.
+  /// * `rediss` - TLS connected to a centralized server.
+  /// * `redis-cluster` - TCP connected to a cluster.
+  /// * `rediss-cluster` - TLS connected to a cluster.
+  /// * `redis-sentinel` - TCP connected to a centralized server behind a sentinel layer.
+  /// * `rediss-sentinel` - TLS connected to a centralized server behind a sentinel layer.
+  /// * `redis+unix` - Unix domain socket followed by a path.
+  ///
+  /// **The `rediss` scheme prefix requires one of the TLS feature flags.**
+  ///
+  /// # Query Parameters
+  ///
+  /// In some cases it's necessary to specify multiple node hostname/port tuples (with a cluster or sentinel layer for
+  /// example). The following query parameters may also be used in their respective contexts:
+  ///
+  /// * `node` - Specify another node in the topology. In a cluster this would refer to any other known cluster node.
+  ///   In the context of a Redis sentinel layer this refers to a known **sentinel** node. Multiple `node` parameters
+  ///   may be used in a URL.
+  /// * `sentinelServiceName` - Specify the name of the sentinel service. This is required when using the
+  ///   `redis-sentinel` scheme.
+  /// * `sentinelUsername` - Specify the username to use when connecting to a **sentinel** node. This requires the
+  ///   `sentinel-auth` feature and allows the caller to use different credentials for sentinel nodes vs the actual
+  ///   Redis server. The `username` part of the URL immediately following the scheme will refer to the username used
+  ///   when connecting to the backing Redis server.
+  /// * `sentinelPassword` - Specify the password to use when connecting to a **sentinel** node. This requires the
+  ///   `sentinel-auth` feature and allows the caller to use different credentials for sentinel nodes vs the actual
+  ///   Redis server. The `password` part of the URL immediately following the scheme will refer to the password used
+  ///   when connecting to the backing Redis server.
+  ///
+  /// See the [from_url_centralized](Self::from_url_centralized), [from_url_clustered](Self::from_url_clustered),
+  /// [from_url_sentinel](Self::from_url_sentinel), and [from_url_unix](Self::from_url_unix) for more information. Or
+  /// see the [RedisConfig](Self) unit tests for examples.
+  pub fn from_url(url: &str) -> Result<RedisConfig, RedisError> {
+    let parsed_url = Url::parse(url)?;
+    if utils::url_is_clustered(&parsed_url) {
+      RedisConfig::from_url_clustered(url)
+    } else if utils::url_is_sentinel(&parsed_url) {
+      RedisConfig::from_url_sentinel(url)
+    } else if utils::url_is_unix_socket(&parsed_url) {
+      #[cfg(feature = "unix-sockets")]
+      return RedisConfig::from_url_unix(url);
+      #[allow(unreachable_code)]
+      Err(RedisError::new(RedisErrorKind::Config, "Missing unix-socket feature."))
+    } else {
+      RedisConfig::from_url_centralized(url)
+    }
+  }
+
+  /// Create a centralized `RedisConfig` struct from a URL.
+  ///
+  /// ```text
+  /// redis://username:password@foo.com:6379/1
+  /// rediss://username:password@foo.com:6379/1
+  /// redis://foo.com:6379/1
+  /// redis://foo.com
+  /// // ... etc
+  /// ```
+  ///
+  /// This function is very similar to [from_url](Self::from_url), but it adds a layer of validation for configuration
+  /// parameters that are only relevant to a centralized server.
+  ///
+  /// For example:
+  ///
+  /// * A database can be defined in the `path` section.
+  /// * The `port` field is optional in this context. If it is not specified then `6379` will be used.
+  /// * Any `node` or sentinel query parameters will be ignored.
+  pub fn from_url_centralized(url: &str) -> Result<RedisConfig, RedisError> {
+    let (url, host, port, _tls) = utils::parse_url(url, Some(6379))?;
+    let server = ServerConfig::new_centralized(host, port);
+    let database = utils::parse_url_db(&url)?;
+    let (username, password) = utils::parse_url_credentials(&url)?;
+
+    Ok(RedisConfig {
+      server,
+      username,
+      password,
+      database,
+      #[cfg(any(
+        feature = "enable-native-tls",
+        feature = "enable-rustls",
+        feature = "enable-rustls-ring"
+      ))]
+      tls: utils::tls_config_from_url(_tls)?,
+      ..RedisConfig::default()
+    })
+  }
+
+  /// Create a clustered `RedisConfig` struct from a URL.
+  ///
+  /// ```text
+  /// redis-cluster://username:password@foo.com:30001?node=bar.com:30002&node=baz.com:30003
+  /// rediss-cluster://username:password@foo.com:30001?node=bar.com:30002&node=baz.com:30003
+  /// rediss://foo.com:30001?node=bar.com:30002&node=baz.com:30003
+  /// redis://foo.com:30001
+  /// // ... etc
+  /// ```
+  ///
+  /// This function is very similar to [from_url](Self::from_url), but it adds a layer of validation for configuration
+  /// parameters that are only relevant to a clustered deployment.
+  ///
+  /// For example:
+  ///
+  /// * The `-cluster` suffix in the scheme is optional when using this function directly.
+  /// * Any database defined in the `path` section will be ignored.
+  /// * The `port` field is required in this context alongside any hostname.
+  /// * Any `node` query parameters will be used to find other known cluster nodes.
+  /// * Any sentinel query parameters will be ignored.
+  pub fn from_url_clustered(url: &str) -> Result<RedisConfig, RedisError> {
+    let (url, host, port, _tls) = utils::parse_url(url, Some(6379))?;
+    let mut cluster_nodes = utils::parse_url_other_nodes(&url)?;
+    cluster_nodes.push(Server::new(host, port));
+    let server = ServerConfig::Clustered {
+      hosts:  cluster_nodes,
+      policy: ClusterDiscoveryPolicy::default(),
+    };
+    let (username, password) = utils::parse_url_credentials(&url)?;
+
+    Ok(RedisConfig {
+      server,
+      username,
+      password,
+      #[cfg(any(
+        feature = "enable-native-tls",
+        feature = "enable-rustls",
+        feature = "enable-rustls-ring"
+      ))]
+      tls: utils::tls_config_from_url(_tls)?,
+      ..RedisConfig::default()
+    })
+  }
+
+  /// Create a sentinel `RedisConfig` struct from a URL.
+  ///
+  /// ```text
+  /// redis-sentinel://username:password@foo.com:6379/1?sentinelServiceName=fakename&node=foo.com:30001&node=bar.com:30002
+  /// rediss-sentinel://username:password@foo.com:6379/0?sentinelServiceName=fakename&node=foo.com:30001&node=bar.com:30002
+  /// redis://foo.com:6379?sentinelServiceName=fakename
+  /// rediss://foo.com:6379/1?sentinelServiceName=fakename
+  /// // ... etc
+  /// ```
+  ///
+  /// This function is very similar to [from_url](Self::from_url), but it adds a layer of validation for configuration
+  /// parameters that are only relevant to a sentinel deployment.
+  ///
+  /// For example:
+  ///
+  /// * The `-sentinel` suffix in the scheme is optional when using this function directly.
+  /// * A database can be defined in the `path` section.
+  /// * The `port` field is optional following the first hostname (`26379` will be used if undefined), but required
+  ///   within any `node` query parameters.
+  /// * Any `node` query parameters will be used to find other known sentinel nodes.
+  /// * The `sentinelServiceName` query parameter is required.
+  /// * Depending on the cargo features used other sentinel query parameters may be used.
+  ///
+  /// This particular function is more complex than the others when the `sentinel-auth` feature is used. For example,
+  /// to declare a config that uses different credentials for the sentinel nodes vs the backing Redis servers:
+  ///
+  /// ```text
+  /// redis-sentinel://username1:password1@foo.com:26379/1?sentinelServiceName=fakename&sentinelUsername=username2&sentinelPassword=password2&node=bar.com:26379&node=baz.com:26380
+  /// ```
+  ///
+  /// The above example will use `("username1", "password1")` when authenticating to the backing Redis servers, and
+  /// `("username2", "password2")` when initially connecting to the sentinel nodes. Additionally, all 3 addresses
+  /// (`foo.com:26379`, `bar.com:26379`, `baz.com:26380`) specify known **sentinel** nodes.
+  pub fn from_url_sentinel(url: &str) -> Result<RedisConfig, RedisError> {
+    let (url, host, port, _tls) = utils::parse_url(url, Some(26379))?;
+    let mut other_nodes = utils::parse_url_other_nodes(&url)?;
+    other_nodes.push(Server::new(host, port));
+    let service_name = utils::parse_url_sentinel_service_name(&url)?;
+    let (username, password) = utils::parse_url_credentials(&url)?;
+    let database = utils::parse_url_db(&url)?;
+    let server = ServerConfig::Sentinel {
+      hosts: other_nodes,
+      service_name,
+      #[cfg(feature = "sentinel-auth")]
+      username: utils::parse_url_sentinel_username(&url),
+      #[cfg(feature = "sentinel-auth")]
+      password: utils::parse_url_sentinel_password(&url),
+    };
+
+    Ok(RedisConfig {
+      server,
+      username,
+      password,
+      database,
+      #[cfg(any(
+        feature = "enable-native-tls",
+        feature = "enable-rustls",
+        feature = "enable-rustls-ring"
+      ))]
+      tls: utils::tls_config_from_url(_tls)?,
+      ..RedisConfig::default()
+    })
+  }
+
+  /// Create a `RedisConfig` from a URL that connects via a Unix domain socket.
+  ///
+  /// ```text
+  /// redis+unix:///path/to/redis.sock
+  /// redis+unix://username:password@nonemptyhost/path/to/redis.sock
+  /// ```
+  ///
+  /// **Important**
+  ///
+  /// * In the other URL parsing functions the path section indicates the database that the client should `SELECT`
+  ///   after connecting. However, Unix sockets are also specified by a path rather than a hostname:port, which
+  ///   creates some ambiguity in this case. Callers should manually set the database field on the returned
+  ///   `RedisConfig` if needed.
+  /// * If credentials are provided the caller must also specify a hostname in order to pass to the [URL
+  ///   validation](Url::parse) process. This function will ignore the value, but some non-empty string must be
+  ///   provided.
+  #[cfg(feature = "unix-sockets")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "unix-sockets")))]
+  pub fn from_url_unix(url: &str) -> Result<RedisConfig, RedisError> {
+    let (url, path) = utils::parse_unix_url(url)?;
+    let (username, password) = utils::parse_url_credentials(&url)?;
+
+    Ok(RedisConfig {
+      server: ServerConfig::Unix { path },
+      username,
+      password,
+      ..Default::default()
+    })
+  }
+}
+
+/// Connection configuration for the Redis server.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ServerConfig {
+  Centralized {
+    /// The `Server` identifier.
+    server: Server,
+  },
+  Clustered {
+    /// The known cluster node `Server` identifiers.
+    ///
+    /// Only one node in the cluster needs to be provided here, the rest will be discovered via the `CLUSTER SLOTS`
+    /// command.
+    hosts:  Vec<Server>,
+    /// The cluster discovery policy to use when connecting or following redirections.
+    policy: ClusterDiscoveryPolicy,
+  },
+  #[cfg(feature = "unix-sockets")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "unix-sockets")))]
+  Unix {
+    /// The path to the Unix socket.
+    ///
+    /// Any associated [Server](crate::types::Server) identifiers will use this value as the `host`.
+    path: PathBuf,
+  },
+  Sentinel {
+    /// An array of `Server` identifiers for each known sentinel instance.
+    hosts:        Vec<Server>,
+    /// The service name for primary/main instances.
+    service_name: String,
+
+    /// An optional ACL username for the client to use when authenticating.
+    #[cfg(feature = "sentinel-auth")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "sentinel-auth")))]
+    username: Option<String>,
+    /// An optional password for the client to use when authenticating.
+    #[cfg(feature = "sentinel-auth")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "sentinel-auth")))]
+    password: Option<String>,
+  },
+}
+
+impl Default for ServerConfig {
+  fn default() -> Self {
+    ServerConfig::default_centralized()
+  }
+}
+
+impl ServerConfig {
+  /// Create a new centralized config with the provided host and port.
+  pub fn new_centralized<S>(host: S, port: u16) -> ServerConfig
+  where
+    S: Into<String>,
+  {
+    ServerConfig::Centralized {
+      server: Server::new(host.into(), port),
+    }
+  }
+
+  /// Create a new clustered config with the provided set of hosts and ports.
+  ///
+  /// Only one valid host in the cluster needs to be provided here. The client will use `CLUSTER NODES` to discover
+  /// the other nodes.
+  pub fn new_clustered<S>(mut hosts: Vec<(S, u16)>) -> ServerConfig
+  where
+    S: Into<String>,
+  {
+    ServerConfig::Clustered {
+      hosts:  hosts.drain(..).map(|(s, p)| Server::new(s.into(), p)).collect(),
+      policy: ClusterDiscoveryPolicy::default(),
+    }
+  }
+
+  /// Create a new sentinel config with the provided set of hosts and the name of the service.
+  ///
+  /// This library will connect using the details from the [Redis documentation](https://redis.io/topics/sentinel-clients).
+  pub fn new_sentinel<H, N>(hosts: Vec<(H, u16)>, service_name: N) -> ServerConfig
+  where
+    H: Into<String>,
+    N: Into<String>,
+  {
+    ServerConfig::Sentinel {
+      hosts:                                      hosts.into_iter().map(|(h, p)| Server::new(h.into(), p)).collect(),
+      service_name:                               service_name.into(),
+      #[cfg(feature = "sentinel-auth")]
+      username:                                   None,
+      #[cfg(feature = "sentinel-auth")]
+      password:                                   None,
+    }
+  }
+
+  /// Create a new server config for a connected Unix socket.
+  #[cfg(feature = "unix-sockets")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "unix-sockets")))]
+  pub fn new_unix_socket<P>(path: P) -> ServerConfig
+  where
+    P: Into<PathBuf>,
+  {
+    ServerConfig::Unix { path: path.into() }
+  }
+
+  /// Create a centralized config with default settings for a local deployment.
+  pub fn default_centralized() -> ServerConfig {
+    ServerConfig::Centralized {
+      server: Server::new("127.0.0.1", 6379),
+    }
+  }
+
+  /// Create a clustered config with the same defaults as specified in the `create-cluster` script provided by Redis.
+  pub fn default_clustered() -> ServerConfig {
+    ServerConfig::Clustered {
+      hosts:  vec![
+        Server::new("127.0.0.1", 30001),
+        Server::new("127.0.0.1", 30002),
+        Server::new("127.0.0.1", 30003),
+      ],
+      policy: ClusterDiscoveryPolicy::default(),
+    }
+  }
+
+  /// Whether the config uses a clustered deployment.
+  pub fn is_clustered(&self) -> bool {
+    matches!(*self, ServerConfig::Clustered { .. })
+  }
+
+  /// Whether the config is for a centralized server behind a sentinel node(s).
+  pub fn is_sentinel(&self) -> bool {
+    matches!(*self, ServerConfig::Sentinel { .. })
+  }
+
+  /// Whether the config is for a centralized server.
+  pub fn is_centralized(&self) -> bool {
+    matches!(*self, ServerConfig::Centralized { .. })
+  }
+
+  /// Whether the config uses a Unix socket.
+  pub fn is_unix_socket(&self) -> bool {
+    match *self {
+      #[cfg(feature = "unix-sockets")]
+      ServerConfig::Unix { .. } => true,
+      _ => false,
+    }
+  }
+
+  /// Read the server hosts or sentinel hosts if using the sentinel interface.
+  pub fn hosts(&self) -> Vec<Server> {
+    match *self {
+      ServerConfig::Centralized { ref server } => vec![server.clone()],
+      ServerConfig::Clustered { ref hosts, .. } => hosts.to_vec(),
+      ServerConfig::Sentinel { ref hosts, .. } => hosts.to_vec(),
+      #[cfg(feature = "unix-sockets")]
+      ServerConfig::Unix { ref path } => vec![Server::new(utils::path_to_string(path), 0)],
+    }
+  }
+
+  /// Set the [ClusterDiscoveryPolicy], if possible.
+  pub fn set_cluster_discovery_policy(&mut self, new_policy: ClusterDiscoveryPolicy) -> Result<(), RedisError> {
+    if let ServerConfig::Clustered { ref mut policy, .. } = self {
+      *policy = new_policy;
+      Ok(())
+    } else {
+      Err(RedisError::new(RedisErrorKind::Config, "Expected clustered config."))
+    }
+  }
+}
+
+/// Configuration options for tracing.
+#[cfg(feature = "partial-tracing")]
+#[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))]
+#[derive(Clone, Debug)]
+pub struct TracingConfig {
+  /// Whether to enable tracing for this client.
+  ///
+  /// Default: `false`
+  pub enabled: bool,
+
+  /// Set the `tracing::Level` of spans under `partial-tracing` feature.
+  ///
+  /// Default: `INFO`
+  pub default_tracing_level: tracing::Level,
+
+  /// Set the `tracing::Level` of spans under `full-tracing` feature.
+  ///
+  /// Default: `DEBUG`
+  #[cfg(feature = "full-tracing")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "full-tracing")))]
+  pub full_tracing_level: tracing::Level,
+}
+
+#[cfg(feature = "partial-tracing")]
+#[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))]
+impl TracingConfig {
+  pub fn new(enabled: bool) -> Self {
+    Self {
+      enabled,
+      ..Self::default()
+    }
+  }
+}
+
+#[cfg(feature = "partial-tracing")]
+#[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))]
+impl Default for TracingConfig {
+  fn default() -> Self {
+    Self {
+      enabled:                                             false,
+      default_tracing_level:                               tracing::Level::INFO,
+      #[cfg(feature = "full-tracing")]
+      full_tracing_level:                                  tracing::Level::DEBUG,
+    }
+  }
+}
+
+/// Configuration options for sentinel clients.
+#[derive(Clone, Debug)]
+#[cfg(feature = "sentinel-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))]
+pub struct SentinelConfig {
+  /// The hostname for the sentinel node.
+  ///
+  /// Default: `127.0.0.1`
+  pub host:     String,
+  /// The port on which the sentinel node is listening.
+  ///
+  /// Default: `26379`
+  pub port:     u16,
+  /// An optional ACL username for the client to use when authenticating. If ACL rules are not configured this should
+  /// be `None`.
+  ///
+  /// Default: `None`
+  pub username: Option<String>,
+  /// An optional password for the client to use when authenticating.
+  ///
+  /// Default: `None`
+  pub password: Option<String>,
+  /// TLS configuration fields. If `None` the connection will not use TLS.
+  ///
+  /// See the `tls` examples on Github for more information.
+  ///
+  /// Default: `None`
+  #[cfg(any(
+    feature = "enable-native-tls",
+    feature = "enable-rustls",
+    feature = "enable-rustls-ring"
+  ))]
+  #[cfg_attr(
+    docsrs,
+    doc(cfg(any(
+      feature = "enable-native-tls",
+      feature = "enable-rustls",
+      feature = "enable-rustls-ring"
+    )))
+  )]
+  pub tls:      Option<TlsConfig>,
+  /// Whether to enable tracing for this client.
+  ///
+  /// Default: `false`
+  #[cfg(feature = "partial-tracing")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))]
+  pub tracing:  TracingConfig,
+}
+
+#[cfg(feature = "sentinel-client")]
+impl Default for SentinelConfig {
+  fn default() -> Self {
+    SentinelConfig {
+      host:                                        "127.0.0.1".into(),
+      port:                                        26379,
+      username:                                    None,
+      password:                                    None,
+      #[cfg(any(
+        feature = "enable-native-tls",
+        feature = "enable-rustls",
+        feature = "enable-rustls-ring"
+      ))]
+      tls:                                         None,
+      #[cfg(feature = "partial-tracing")]
+      tracing:                                     TracingConfig::default(),
+    }
+  }
+}
+
+#[doc(hidden)]
+#[cfg(feature = "sentinel-client")]
+impl From<SentinelConfig> for RedisConfig {
+  fn from(config: SentinelConfig) -> Self {
+    RedisConfig {
+      server:                                      ServerConfig::Centralized {
+        server: Server::new(config.host, config.port),
+      },
+      fail_fast:                                   true,
+      database:                                    None,
+      blocking:                                    Blocking::Block,
+      username:                                    config.username,
+      password:                                    config.password,
+      version:                                     RespVersion::RESP2,
+      #[cfg(any(
+        feature = "enable-native-tls",
+        feature = "enable-rustls",
+        feature = "enable-rustls-ring"
+      ))]
+      tls:                                         config.tls,
+      #[cfg(feature = "partial-tracing")]
+      tracing:                                     config.tracing,
+      #[cfg(feature = "mocks")]
+      mocks:                                       None,
+    }
+  }
+}
+
+/// Options to configure or overwrite for individual commands.
+///
+/// Fields left as `None` will use the value from the corresponding client or global config option.
+///
+/// ```rust
+/// # use fred::prelude::*;
+/// async fn example() -> Result<(), RedisError> {
+///   let options = Options {
+///     max_attempts: Some(10),
+///     max_redirections: Some(2),
+///     ..Default::default()
+///   };
+///
+///   let client = RedisClient::default();
+///   client.init().await?;
+///   let _: () = client.with_options(&options).get("foo").await?;
+///
+///   Ok(())
+/// }
+/// ```
+///
+/// See [WithOptions](crate::clients::WithOptions) for more information.
+#[derive(Clone, Debug, Eq, PartialEq, Default)]
+pub struct Options {
+  /// Set the max number of write attempts for a command.
+  pub max_attempts:     Option<u32>,
+  /// Set the max number of cluster redirections to follow for a command.
+  pub max_redirections: Option<u32>,
+  /// Set the timeout duration for a command.
+  ///
+  /// This interface is more<sup>*</sup> cancellation-safe than a simple [timeout](https://docs.rs/tokio/latest/tokio/time/fn.timeout.html) call.
+  ///
+  /// <sup>*</sup> But it's not perfect. There's no reliable mechanism to cancel a command once it has been written
+  /// to the connection.
+  pub timeout:          Option<Duration>,
+  /// The cluster node that should receive the command.
+  ///
+  /// The caller will receive a `RedisErrorKind::Cluster` error if the provided server does not exist.
+  ///
+  /// The client will still follow redirection errors via this interface. Callers may not notice this, but incorrect
+  /// server arguments here could result in unnecessary calls to refresh the cached cluster routing table.
+  pub cluster_node:     Option<Server>,
+  /// The cluster hashing policy to use, if applicable.
+  ///
+  /// If `cluster_node` is also provided it will take precedence over this value.
+  pub cluster_hash:     Option<ClusterHash>,
+  /// Whether to skip backpressure checks for a command.
+  pub no_backpressure:  bool,
+  /// Whether the command should fail quickly if the connection is not healthy or available for writes. This always
+  /// takes precedence over `max_attempts` if `true`.
+  ///
+  /// This can be useful for caching use cases where it's preferable to fail fast with a fallback query to another
+  /// storage layer rather than wait for a reconnection delay.
+  ///
+  /// Default: `false`
+  pub fail_fast:        bool,
+  /// Whether to send `CLIENT CACHING yes|no` before the command.
+  #[cfg(feature = "i-tracking")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))]
+  pub caching:          Option<bool>,
+}
+
+impl Options {
+  /// Set the non-null values from `other` onto `self`.
+  pub fn extend(&mut self, other: &Self) -> &mut Self {
+    if let Some(val) = other.max_attempts {
+      self.max_attempts = Some(val);
+    }
+    if let Some(val) = other.max_redirections {
+      self.max_redirections = Some(val);
+    }
+    if let Some(val) = other.timeout {
+      self.timeout = Some(val);
+    }
+    if let Some(ref val) = other.cluster_node {
+      self.cluster_node = Some(val.clone());
+    }
+    if let Some(ref cluster_hash) = other.cluster_hash {
+      self.cluster_hash = Some(cluster_hash.clone());
+    }
+    self.no_backpressure |= other.no_backpressure;
+    self.fail_fast |= other.fail_fast;
+
+    #[cfg(feature = "i-tracking")]
+    if let Some(val) = other.caching {
+      self.caching = Some(val);
+    }
+
+    self
+  }
+
+  /// Create options from a command
+  #[cfg(feature = "transactions")]
+  pub(crate) fn from_command(cmd: &RedisCommand) -> Self {
+    Options {
+      max_attempts:                           Some(cmd.attempts_remaining),
+      max_redirections:                       Some(cmd.redirections_remaining),
+      timeout:                                cmd.timeout_dur,
+      no_backpressure:                        cmd.skip_backpressure,
+      cluster_node:                           cmd.cluster_node.clone(),
+      cluster_hash:                           Some(cmd.hasher.clone()),
+      fail_fast:                              cmd.fail_fast,
+      #[cfg(feature = "i-tracking")]
+      caching:                                cmd.caching,
+    }
+  }
+
+  /// Overwrite the configuration options on the provided command.
+  pub(crate) fn apply(&self, command: &mut RedisCommand) {
+    command.skip_backpressure = self.no_backpressure;
+    command.timeout_dur = self.timeout;
+    command.cluster_node = self.cluster_node.clone();
+    command.fail_fast = self.fail_fast;
+
+    #[cfg(feature = "i-tracking")]
+    {
+      command.caching = self.caching;
+    }
+
+    if let Some(attempts) = self.max_attempts {
+      command.attempts_remaining = attempts;
+    }
+    if let Some(redirections) = self.max_redirections {
+      command.redirections_remaining = redirections;
+    }
+    if let Some(ref cluster_hash) = self.cluster_hash {
+      command.hasher = cluster_hash.clone();
+    }
+  }
+}
+
+#[cfg(test)]
+mod tests {
+  #[cfg(feature = "sentinel-auth")]
+  use crate::types::Server;
+  #[allow(unused_imports)]
+  use crate::{prelude::ServerConfig, types::RedisConfig, utils};
+
+  #[test]
+  fn should_parse_centralized_url() {
+    let url = "redis://username:password@foo.com:6379/1";
+    let expected = RedisConfig {
+      server: ServerConfig::new_centralized("foo.com", 6379),
+      database: Some(1),
+      username: Some("username".into()),
+      password: Some("password".into()),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_centralized(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_centralized_url_without_port() {
+    let url = "redis://foo.com";
+    let expected = RedisConfig {
+      server: ServerConfig::new_centralized("foo.com", 6379),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_centralized(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_centralized_url_without_creds() {
+    let url = "redis://foo.com:6379/1";
+    let expected = RedisConfig {
+      server: ServerConfig::new_centralized("foo.com", 6379),
+      database: Some(1),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_centralized(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_centralized_url_without_db() {
+    let url = "redis://username:password@foo.com:6379";
+    let expected = RedisConfig {
+      server: ServerConfig::new_centralized("foo.com", 6379),
+      username: Some("username".into()),
+      password: Some("password".into()),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_centralized(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  #[cfg(feature = "enable-native-tls")]
+  fn should_parse_centralized_url_with_tls() {
+    let url = "rediss://username:password@foo.com:6379/1";
+    let expected = RedisConfig {
+      server: ServerConfig::new_centralized("foo.com", 6379),
+      database: Some(1),
+      username: Some("username".into()),
+      password: Some("password".into()),
+      tls: utils::tls_config_from_url(true).unwrap(),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_centralized(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_clustered_url() {
+    let url = "redis-cluster://username:password@foo.com:30000";
+    let expected = RedisConfig {
+      server: ServerConfig::new_clustered(vec![("foo.com", 30000)]),
+      username: Some("username".into()),
+      password: Some("password".into()),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_clustered(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_clustered_url_without_port() {
+    let url = "redis-cluster://foo.com";
+    let expected = RedisConfig {
+      server: ServerConfig::new_clustered(vec![("foo.com", 6379)]),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_clustered(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_clustered_url_without_creds() {
+    let url = "redis-cluster://foo.com:30000";
+    let expected = RedisConfig {
+      server: ServerConfig::new_clustered(vec![("foo.com", 30000)]),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_clustered(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_clustered_url_with_other_nodes() {
+    let url = "redis-cluster://username:password@foo.com:30000?node=bar.com:30001&node=baz.com:30002";
+    let expected = RedisConfig {
+      // need to be careful with the array ordering here
+      server: ServerConfig::new_clustered(vec![("bar.com", 30001), ("baz.com", 30002), ("foo.com", 30000)]),
+      username: Some("username".into()),
+      password: Some("password".into()),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_clustered(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  #[cfg(feature = "enable-native-tls")]
+  fn should_parse_clustered_url_with_tls() {
+    let url = "rediss-cluster://username:password@foo.com:30000";
+    let expected = RedisConfig {
+      server: ServerConfig::new_clustered(vec![("foo.com", 30000)]),
+      username: Some("username".into()),
+      password: Some("password".into()),
+      tls: utils::tls_config_from_url(true).unwrap(),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_clustered(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_sentinel_url() {
+    let url = "redis-sentinel://username:password@foo.com:26379/1?sentinelServiceName=fakename";
+    let expected = RedisConfig {
+      server: ServerConfig::new_sentinel(vec![("foo.com", 26379)], "fakename"),
+      username: Some("username".into()),
+      password: Some("password".into()),
+      database: Some(1),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_sentinel(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  fn should_parse_sentinel_url_with_other_nodes() {
+    let url = "redis-sentinel://username:password@foo.com:26379/1?sentinelServiceName=fakename&node=bar.com:26380&\
+               node=baz.com:26381";
+    let expected = RedisConfig {
+      // also need to be careful with array ordering here
+      server: ServerConfig::new_sentinel(
+        vec![("bar.com", 26380), ("baz.com", 26381), ("foo.com", 26379)],
+        "fakename",
+      ),
+      username: Some("username".into()),
+      password: Some("password".into()),
+      database: Some(1),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_sentinel(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  #[cfg(feature = "unix-sockets")]
+  fn should_parse_unix_socket_url_no_auth() {
+    let url = "redis+unix:///path/to/redis.sock";
+    let expected = RedisConfig {
+      server: ServerConfig::Unix {
+        path: "/path/to/redis.sock".into(),
+      },
+      username: None,
+      password: None,
+      ..Default::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_unix(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  #[cfg(feature = "unix-sockets")]
+  fn should_parse_unix_socket_url_with_auth() {
+    let url = "redis+unix://username:password@foo/path/to/redis.sock";
+    let expected = RedisConfig {
+      server: ServerConfig::Unix {
+        path: "/path/to/redis.sock".into(),
+      },
+      username: Some("username".into()),
+      password: Some("password".into()),
+      ..Default::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_unix(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  #[cfg(feature = "enable-native-tls")]
+  fn should_parse_sentinel_url_with_tls() {
+    let url = "rediss-sentinel://username:password@foo.com:26379/1?sentinelServiceName=fakename";
+    let expected = RedisConfig {
+      server: ServerConfig::new_sentinel(vec![("foo.com", 26379)], "fakename"),
+      username: Some("username".into()),
+      password: Some("password".into()),
+      database: Some(1),
+      tls: utils::tls_config_from_url(true).unwrap(),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_sentinel(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+
+  #[test]
+  #[cfg(feature = "sentinel-auth")]
+  fn should_parse_sentinel_url_with_sentinel_auth() {
+    let url = "redis-sentinel://username1:password1@foo.com:26379/1?sentinelServiceName=fakename&\
+               sentinelUsername=username2&sentinelPassword=password2";
+    let expected = RedisConfig {
+      server: ServerConfig::Sentinel {
+        hosts:        vec![Server::new("foo.com", 26379)],
+        service_name: "fakename".into(),
+        username:     Some("username2".into()),
+        password:     Some("password2".into()),
+      },
+      username: Some("username1".into()),
+      password: Some("password1".into()),
+      database: Some(1),
+      ..RedisConfig::default()
+    };
+
+    let actual = RedisConfig::from_url(url).unwrap();
+    assert_eq!(actual, expected);
+    let actual = RedisConfig::from_url_sentinel(url).unwrap();
+    assert_eq!(actual, expected);
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/types/from_tuple.rs.html b/doc/src/fred/types/from_tuple.rs.html new file mode 100644 index 00000000..379d4f04 --- /dev/null +++ b/doc/src/fred/types/from_tuple.rs.html @@ -0,0 +1,73 @@ +from_tuple.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+
use crate::types::{MultipleKeys, RedisKey, RedisValue};
+
+macro_rules! tuple2val {
+    ($($id:tt $ty:ident);+) => {
+impl<$($ty: Into<RedisValue>),+ > From<($($ty),+)> for RedisValue {
+    fn from(value: ($($ty),+)) -> Self {
+        RedisValue::Array(vec![$(value.$id.into()),+])
+    }
+}
+
+impl<$($ty: Into<RedisKey>),+ > From<($($ty),+)> for MultipleKeys {
+    fn from(value: ($($ty),+)) -> Self {
+        Self{keys:vec![$(value.$id.into()),+]}
+    }
+}
+    };
+}
+
+tuple2val!(0 A0; 1 A1);
+tuple2val!(0 A0; 1 A1; 2 A2);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10; 11 A11);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10; 11 A11; 12 A12);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10; 11 A11; 12 A12; 13 A13);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10; 11 A11; 12 A12; 13 A13; 14 A14);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10; 11 A11; 12 A12; 13 A13; 14 A14; 15
+A15);
+tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10; 11 A11; 12 A12; 13 A13; 14
+A14; 15 A15; 16 A16);
+
\ No newline at end of file diff --git a/doc/src/fred/types/geo.rs.html b/doc/src/fred/types/geo.rs.html new file mode 100644 index 00000000..ea6b8993 --- /dev/null +++ b/doc/src/fred/types/geo.rs.html @@ -0,0 +1,445 @@ +geo.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+
use crate::{error::RedisError, protocol::utils as protocol_utils, types::RedisValue, utils};
+use bytes_utils::Str;
+use std::{
+  collections::VecDeque,
+  convert::{TryFrom, TryInto},
+};
+
+/// A struct describing the longitude and latitude coordinates of a GEO command.
+#[derive(Clone, Debug)]
+pub struct GeoPosition {
+  pub longitude: f64,
+  pub latitude:  f64,
+}
+
+impl PartialEq for GeoPosition {
+  fn eq(&self, other: &Self) -> bool {
+    utils::f64_eq(self.longitude, other.longitude) && utils::f64_eq(self.latitude, other.latitude)
+  }
+}
+
+impl Eq for GeoPosition {}
+
+impl From<(f64, f64)> for GeoPosition {
+  fn from(d: (f64, f64)) -> Self {
+    GeoPosition {
+      longitude: d.0,
+      latitude:  d.1,
+    }
+  }
+}
+
+impl TryFrom<RedisValue> for GeoPosition {
+  type Error = RedisError;
+
+  fn try_from(value: RedisValue) -> Result<Self, Self::Error> {
+    let (longitude, latitude): (f64, f64) = value.convert()?;
+    Ok(GeoPosition { longitude, latitude })
+  }
+}
+
+/// Units for the GEO DIST command.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum GeoUnit {
+  Meters,
+  Kilometers,
+  Miles,
+  Feet,
+}
+
+impl GeoUnit {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      GeoUnit::Meters => "m",
+      GeoUnit::Kilometers => "km",
+      GeoUnit::Feet => "ft",
+      GeoUnit::Miles => "mi",
+    })
+  }
+}
+
+/// A struct describing the value inside a GEO data structure.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct GeoValue {
+  pub coordinates: GeoPosition,
+  pub member:      RedisValue,
+}
+
+impl<T> TryFrom<(f64, f64, T)> for GeoValue
+where
+  T: TryInto<RedisValue>,
+  T::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(v: (f64, f64, T)) -> Result<Self, Self::Error> {
+    Ok(GeoValue {
+      coordinates: GeoPosition {
+        longitude: v.0,
+        latitude:  v.1,
+      },
+      member:      utils::try_into(v.2)?,
+    })
+  }
+}
+
+/// A convenience struct for commands that take one or more GEO values.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct MultipleGeoValues {
+  inner: Vec<GeoValue>,
+}
+
+impl MultipleGeoValues {
+  pub fn len(&self) -> usize {
+    self.inner.len()
+  }
+
+  pub fn inner(self) -> Vec<GeoValue> {
+    self.inner
+  }
+}
+
+impl From<GeoValue> for MultipleGeoValues {
+  fn from(d: GeoValue) -> Self {
+    MultipleGeoValues { inner: vec![d] }
+  }
+}
+
+impl From<Vec<GeoValue>> for MultipleGeoValues {
+  fn from(d: Vec<GeoValue>) -> Self {
+    MultipleGeoValues { inner: d }
+  }
+}
+
+impl From<VecDeque<GeoValue>> for MultipleGeoValues {
+  fn from(d: VecDeque<GeoValue>) -> Self {
+    MultipleGeoValues {
+      inner: d.into_iter().collect(),
+    }
+  }
+}
+
+/// A typed struct representing the full output of the GEORADIUS (or similar) command.
+#[derive(Clone, Debug)]
+pub struct GeoRadiusInfo {
+  pub member:   RedisValue,
+  pub position: Option<GeoPosition>,
+  pub distance: Option<f64>,
+  pub hash:     Option<i64>,
+}
+
+impl Default for GeoRadiusInfo {
+  fn default() -> Self {
+    GeoRadiusInfo {
+      member:   RedisValue::Null,
+      position: None,
+      distance: None,
+      hash:     None,
+    }
+  }
+}
+
+impl PartialEq for GeoRadiusInfo {
+  fn eq(&self, other: &Self) -> bool {
+    self.member == other.member
+      && self.position == other.position
+      && self.hash == other.hash
+      && utils::f64_opt_eq(&self.distance, &other.distance)
+  }
+}
+
+impl Eq for GeoRadiusInfo {}
+
+impl GeoRadiusInfo {
+  /// Parse the value with context from the calling command.
+  pub fn from_redis_value(
+    value: RedisValue,
+    withcoord: bool,
+    withdist: bool,
+    withhash: bool,
+  ) -> Result<Self, RedisError> {
+    if let RedisValue::Array(mut data) = value {
+      let mut out = GeoRadiusInfo::default();
+      data.reverse();
+
+      if withcoord && withdist && withhash {
+        // 4 elements: member, dist, hash, position
+        protocol_utils::assert_array_len(&data, 4)?;
+
+        out.member = data.pop().unwrap();
+        out.distance = data.pop().unwrap().convert()?;
+        out.hash = data.pop().unwrap().convert()?;
+        out.position = data.pop().unwrap().convert()?;
+      } else if withcoord && withdist {
+        // 3 elements: member, dist, position
+        protocol_utils::assert_array_len(&data, 3)?;
+
+        out.member = data.pop().unwrap();
+        out.distance = data.pop().unwrap().convert()?;
+        out.position = data.pop().unwrap().convert()?;
+      } else if withcoord && withhash {
+        // 3 elements: member, hash, position
+        protocol_utils::assert_array_len(&data, 3)?;
+
+        out.member = data.pop().unwrap();
+        out.hash = data.pop().unwrap().convert()?;
+        out.position = data.pop().unwrap().convert()?;
+      } else if withdist && withhash {
+        // 3 elements: member, dist, hash
+        protocol_utils::assert_array_len(&data, 3)?;
+
+        out.member = data.pop().unwrap();
+        out.distance = data.pop().unwrap().convert()?;
+        out.hash = data.pop().unwrap().convert()?;
+      } else if withcoord {
+        // 2 elements: member, position
+        protocol_utils::assert_array_len(&data, 2)?;
+
+        out.member = data.pop().unwrap();
+        out.position = data.pop().unwrap().convert()?;
+      } else if withdist {
+        // 2 elements: member, dist
+        protocol_utils::assert_array_len(&data, 2)?;
+
+        out.member = data.pop().unwrap();
+        out.distance = data.pop().unwrap().convert()?;
+      } else if withhash {
+        // 2 elements: member, hash
+        protocol_utils::assert_array_len(&data, 2)?;
+
+        out.member = data.pop().unwrap();
+        out.hash = data.pop().unwrap().convert()?;
+      }
+
+      Ok(out)
+    } else {
+      Ok(GeoRadiusInfo {
+        member: value,
+        ..Default::default()
+      })
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/types/lists.rs.html b/doc/src/fred/types/lists.rs.html new file mode 100644 index 00000000..4e4a3aca --- /dev/null +++ b/doc/src/fred/types/lists.rs.html @@ -0,0 +1,73 @@ +lists.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+
use crate::utils;
+use bytes_utils::Str;
+
+/// The direction to move elements in a *LMOVE command.
+///
+/// <https://redis.io/commands/blmove>
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum LMoveDirection {
+  Left,
+  Right,
+}
+
+impl LMoveDirection {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      LMoveDirection::Left => "LEFT",
+      LMoveDirection::Right => "RIGHT",
+    })
+  }
+}
+
+/// Location flag for the `LINSERT` command.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ListLocation {
+  Before,
+  After,
+}
+
+impl ListLocation {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      ListLocation::Before => "BEFORE",
+      ListLocation::After => "AFTER",
+    })
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/types/misc.rs.html b/doc/src/fred/types/misc.rs.html new file mode 100644 index 00000000..03db81d4 --- /dev/null +++ b/doc/src/fred/types/misc.rs.html @@ -0,0 +1,1369 @@ +misc.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+
pub use crate::protocol::{
+  hashers::ClusterHash,
+  types::{Message, MessageKind},
+};
+use crate::{
+  error::{RedisError, RedisErrorKind},
+  types::{RedisKey, RedisValue, Server},
+  utils,
+};
+use bytes_utils::Str;
+use std::{convert::TryFrom, fmt, time::Duration};
+
+#[cfg(feature = "i-memory")]
+use crate::utils::convert_or_default;
+#[cfg(feature = "i-memory")]
+use std::collections::HashMap;
+
+/// Arguments passed to the SHUTDOWN command.
+///
+/// <https://redis.io/commands/shutdown>
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ShutdownFlags {
+  Save,
+  NoSave,
+}
+
+impl ShutdownFlags {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      ShutdownFlags::Save => "SAVE",
+      ShutdownFlags::NoSave => "NOSAVE",
+    })
+  }
+}
+
+/// An event on the publish-subscribe interface describing a keyspace notification.
+///
+/// <https://redis.io/topics/notifications>
+#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct KeyspaceEvent {
+  pub db:        u8,
+  pub operation: String,
+  pub key:       RedisKey,
+}
+
+/// Aggregate options for the [zinterstore](https://redis.io/commands/zinterstore) (and related) commands.
+pub enum AggregateOptions {
+  Sum,
+  Min,
+  Max,
+}
+
+impl AggregateOptions {
+  #[cfg(feature = "i-sorted-sets")]
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      AggregateOptions::Sum => "SUM",
+      AggregateOptions::Min => "MIN",
+      AggregateOptions::Max => "MAX",
+    })
+  }
+}
+
+/// Options for the [info](https://redis.io/commands/info) command.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum InfoKind {
+  Default,
+  All,
+  Keyspace,
+  Cluster,
+  CommandStats,
+  Cpu,
+  Replication,
+  Stats,
+  Persistence,
+  Memory,
+  Clients,
+  Server,
+}
+
+impl InfoKind {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      InfoKind::Default => "default",
+      InfoKind::All => "all",
+      InfoKind::Keyspace => "keyspace",
+      InfoKind::Cluster => "cluster",
+      InfoKind::CommandStats => "commandstats",
+      InfoKind::Cpu => "cpu",
+      InfoKind::Replication => "replication",
+      InfoKind::Stats => "stats",
+      InfoKind::Persistence => "persistence",
+      InfoKind::Memory => "memory",
+      InfoKind::Clients => "clients",
+      InfoKind::Server => "server",
+    })
+  }
+}
+
+/// Configuration for custom redis commands, primarily used for interacting with third party modules or extensions.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct CustomCommand {
+  /// The command name, sent directly to the server.
+  pub cmd:          Str,
+  /// The cluster hashing policy to use, if any.
+  ///
+  /// Cluster clients will use the default policy if not provided.
+  pub cluster_hash: ClusterHash,
+  /// Whether the command should block the connection while waiting on a response.
+  pub blocking:     bool,
+}
+
+impl CustomCommand {
+  /// Create a new custom command.
+  ///
+  /// See the [custom](crate::interfaces::ClientLike::custom) command for more information.
+  pub fn new<C, H>(cmd: C, cluster_hash: H, blocking: bool) -> Self
+  where
+    C: Into<Str>,
+    H: Into<ClusterHash>,
+  {
+    CustomCommand {
+      cmd: cmd.into(),
+      cluster_hash: cluster_hash.into(),
+      blocking,
+    }
+  }
+
+  /// Create a new custom command specified by a `&'static str`.
+  pub fn new_static<H>(cmd: &'static str, cluster_hash: H, blocking: bool) -> Self
+  where
+    H: Into<ClusterHash>,
+  {
+    CustomCommand {
+      cmd: utils::static_str(cmd),
+      cluster_hash: cluster_hash.into(),
+      blocking,
+    }
+  }
+}
+
+/// An enum describing the possible ways in which a Redis cluster can change state.
+///
+/// See [on_cluster_change](crate::interfaces::EventInterface::on_cluster_change) for more information.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClusterStateChange {
+  /// A node was added to the cluster.
+  ///
+  /// This implies that hash slots were also probably rebalanced.
+  Add(Server),
+  /// A node was removed from the cluster.
+  ///
+  /// This implies that hash slots were also probably rebalanced.
+  Remove(Server),
+  /// Hash slots were rebalanced across the cluster and/or local routing state was updated.
+  Rebalance,
+}
+
+/// Options for the [set](https://redis.io/commands/set) command.
+///
+/// <https://redis.io/commands/set>
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum SetOptions {
+  NX,
+  XX,
+}
+
+impl SetOptions {
+  #[allow(dead_code)]
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      SetOptions::NX => "NX",
+      SetOptions::XX => "XX",
+    })
+  }
+}
+
+/// Options for certain expiration commands (`PEXPIRE`, etc).
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum ExpireOptions {
+  NX,
+  XX,
+  GT,
+  LT,
+}
+
+impl ExpireOptions {
+  #[allow(dead_code)]
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      ExpireOptions::NX => "NX",
+      ExpireOptions::XX => "XX",
+      ExpireOptions::GT => "GT",
+      ExpireOptions::LT => "LT",
+    })
+  }
+}
+
+/// Expiration options for the [set](https://redis.io/commands/set) command.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum Expiration {
+  /// Expiration in seconds.
+  EX(i64),
+  /// Expiration in milliseconds.
+  PX(i64),
+  /// Expiration time, in seconds.
+  EXAT(i64),
+  /// Expiration time, in milliseconds.
+  PXAT(i64),
+  /// Do not reset the TTL.
+  KEEPTTL,
+}
+
+impl Expiration {
+  #[allow(dead_code)]
+  pub(crate) fn into_args(self) -> (Str, Option<i64>) {
+    let (prefix, value) = match self {
+      Expiration::EX(i) => ("EX", Some(i)),
+      Expiration::PX(i) => ("PX", Some(i)),
+      Expiration::EXAT(i) => ("EXAT", Some(i)),
+      Expiration::PXAT(i) => ("PXAT", Some(i)),
+      Expiration::KEEPTTL => ("KEEPTTL", None),
+    };
+
+    (utils::static_str(prefix), value)
+  }
+}
+
+/// The state of the underlying connection to the Redis server.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClientState {
+  Disconnected,
+  Disconnecting,
+  Connected,
+  Connecting,
+}
+
+impl ClientState {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      ClientState::Connecting => "Connecting",
+      ClientState::Connected => "Connected",
+      ClientState::Disconnecting => "Disconnecting",
+      ClientState::Disconnected => "Disconnected",
+    })
+  }
+}
+
+impl fmt::Display for ClientState {
+  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    write!(f, "{}", self.to_str())
+  }
+}
+
+/// The parsed result of the MEMORY STATS command for a specific database.
+///
+/// <https://redis.io/commands/memory-stats>
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+pub struct DatabaseMemoryStats {
+  pub overhead_hashtable_main:         u64,
+  pub overhead_hashtable_expires:      u64,
+  pub overhead_hashtable_slot_to_keys: u64,
+}
+
+#[cfg(feature = "i-memory")]
+impl Default for DatabaseMemoryStats {
+  fn default() -> Self {
+    DatabaseMemoryStats {
+      overhead_hashtable_expires:      0,
+      overhead_hashtable_main:         0,
+      overhead_hashtable_slot_to_keys: 0,
+    }
+  }
+}
+
+#[cfg(feature = "i-memory")]
+fn parse_database_memory_stat(stats: &mut DatabaseMemoryStats, key: &str, value: RedisValue) {
+  match key {
+    "overhead.hashtable.main" => stats.overhead_hashtable_main = convert_or_default(value),
+    "overhead.hashtable.expires" => stats.overhead_hashtable_expires = convert_or_default(value),
+    "overhead.hashtable.slot-to-keys" => stats.overhead_hashtable_slot_to_keys = convert_or_default(value),
+    _ => {},
+  };
+}
+
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+impl TryFrom<RedisValue> for DatabaseMemoryStats {
+  type Error = RedisError;
+
+  fn try_from(value: RedisValue) -> Result<Self, Self::Error> {
+    let values: HashMap<Str, RedisValue> = value.convert()?;
+    let mut out = DatabaseMemoryStats::default();
+
+    for (key, value) in values.into_iter() {
+      parse_database_memory_stat(&mut out, &key, value);
+    }
+    Ok(out)
+  }
+}
+
+/// The parsed result of the MEMORY STATS command.
+///
+/// <https://redis.io/commands/memory-stats>
+#[derive(Clone, Debug)]
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+pub struct MemoryStats {
+  pub peak_allocated:                u64,
+  pub total_allocated:               u64,
+  pub startup_allocated:             u64,
+  pub replication_backlog:           u64,
+  pub clients_slaves:                u64,
+  pub clients_normal:                u64,
+  pub aof_buffer:                    u64,
+  pub lua_caches:                    u64,
+  pub overhead_total:                u64,
+  pub keys_count:                    u64,
+  pub keys_bytes_per_key:            u64,
+  pub dataset_bytes:                 u64,
+  pub dataset_percentage:            f64,
+  pub peak_percentage:               f64,
+  pub fragmentation:                 f64,
+  pub fragmentation_bytes:           u64,
+  pub rss_overhead_ratio:            f64,
+  pub rss_overhead_bytes:            u64,
+  pub allocator_allocated:           u64,
+  pub allocator_active:              u64,
+  pub allocator_resident:            u64,
+  pub allocator_fragmentation_ratio: f64,
+  pub allocator_fragmentation_bytes: u64,
+  pub allocator_rss_ratio:           f64,
+  pub allocator_rss_bytes:           u64,
+  pub db:                            HashMap<u16, DatabaseMemoryStats>,
+}
+
+#[cfg(feature = "i-memory")]
+impl Default for MemoryStats {
+  fn default() -> Self {
+    MemoryStats {
+      peak_allocated:                0,
+      total_allocated:               0,
+      startup_allocated:             0,
+      replication_backlog:           0,
+      clients_normal:                0,
+      clients_slaves:                0,
+      aof_buffer:                    0,
+      lua_caches:                    0,
+      overhead_total:                0,
+      keys_count:                    0,
+      keys_bytes_per_key:            0,
+      dataset_bytes:                 0,
+      dataset_percentage:            0.0,
+      peak_percentage:               0.0,
+      fragmentation:                 0.0,
+      fragmentation_bytes:           0,
+      rss_overhead_ratio:            0.0,
+      rss_overhead_bytes:            0,
+      allocator_allocated:           0,
+      allocator_active:              0,
+      allocator_resident:            0,
+      allocator_fragmentation_ratio: 0.0,
+      allocator_fragmentation_bytes: 0,
+      allocator_rss_bytes:           0,
+      allocator_rss_ratio:           0.0,
+      db:                            HashMap::new(),
+    }
+  }
+}
+#[cfg(feature = "i-memory")]
+impl PartialEq for MemoryStats {
+  fn eq(&self, other: &Self) -> bool {
+    self.peak_allocated == other.peak_allocated
+      && self.total_allocated == other.total_allocated
+      && self.startup_allocated == other.startup_allocated
+      && self.replication_backlog == other.replication_backlog
+      && self.clients_normal == other.clients_normal
+      && self.clients_slaves == other.clients_slaves
+      && self.aof_buffer == other.aof_buffer
+      && self.lua_caches == other.lua_caches
+      && self.overhead_total == other.overhead_total
+      && self.keys_count == other.keys_count
+      && self.keys_bytes_per_key == other.keys_bytes_per_key
+      && self.dataset_bytes == other.dataset_bytes
+      && utils::f64_eq(self.dataset_percentage, other.dataset_percentage)
+      && utils::f64_eq(self.peak_percentage, other.peak_percentage)
+      && utils::f64_eq(self.fragmentation, other.fragmentation)
+      && self.fragmentation_bytes == other.fragmentation_bytes
+      && utils::f64_eq(self.rss_overhead_ratio, other.rss_overhead_ratio)
+      && self.rss_overhead_bytes == other.rss_overhead_bytes
+      && self.allocator_allocated == other.allocator_allocated
+      && self.allocator_active == other.allocator_active
+      && self.allocator_resident == other.allocator_resident
+      && utils::f64_eq(self.allocator_fragmentation_ratio, other.allocator_fragmentation_ratio)
+      && self.allocator_fragmentation_bytes == other.allocator_fragmentation_bytes
+      && self.allocator_rss_bytes == other.allocator_rss_bytes
+      && utils::f64_eq(self.allocator_rss_ratio, other.allocator_rss_ratio)
+      && self.db == other.db
+  }
+}
+
+#[cfg(feature = "i-memory")]
+impl Eq for MemoryStats {}
+
+#[cfg(feature = "i-memory")]
+fn parse_memory_stat_field(stats: &mut MemoryStats, key: &str, value: RedisValue) {
+  match key {
+    "peak.allocated" => stats.peak_allocated = convert_or_default(value),
+    "total.allocated" => stats.total_allocated = convert_or_default(value),
+    "startup.allocated" => stats.startup_allocated = convert_or_default(value),
+    "replication.backlog" => stats.replication_backlog = convert_or_default(value),
+    "clients.slaves" => stats.clients_slaves = convert_or_default(value),
+    "clients.normal" => stats.clients_normal = convert_or_default(value),
+    "aof.buffer" => stats.aof_buffer = convert_or_default(value),
+    "lua.caches" => stats.lua_caches = convert_or_default(value),
+    "overhead.total" => stats.overhead_total = convert_or_default(value),
+    "keys.count" => stats.keys_count = convert_or_default(value),
+    "keys.bytes-per-key" => stats.keys_bytes_per_key = convert_or_default(value),
+    "dataset.bytes" => stats.dataset_bytes = convert_or_default(value),
+    "dataset.percentage" => stats.dataset_percentage = convert_or_default(value),
+    "peak.percentage" => stats.peak_percentage = convert_or_default(value),
+    "allocator.allocated" => stats.allocator_allocated = convert_or_default(value),
+    "allocator.active" => stats.allocator_active = convert_or_default(value),
+    "allocator.resident" => stats.allocator_resident = convert_or_default(value),
+    "allocator-fragmentation.ratio" => stats.allocator_fragmentation_ratio = convert_or_default(value),
+    "allocator-fragmentation.bytes" => stats.allocator_fragmentation_bytes = convert_or_default(value),
+    "allocator-rss.ratio" => stats.allocator_rss_ratio = convert_or_default(value),
+    "allocator-rss.bytes" => stats.allocator_rss_bytes = convert_or_default(value),
+    "rss-overhead.ratio" => stats.rss_overhead_ratio = convert_or_default(value),
+    "rss-overhead.bytes" => stats.rss_overhead_bytes = convert_or_default(value),
+    "fragmentation" => stats.fragmentation = convert_or_default(value),
+    "fragmentation.bytes" => stats.fragmentation_bytes = convert_or_default(value),
+    _ => {
+      if key.starts_with("db.") {
+        let db = match key.split('.').last().and_then(|v| v.parse::<u16>().ok()) {
+          Some(db) => db,
+          None => return,
+        };
+        let parsed: DatabaseMemoryStats = match value.convert().ok() {
+          Some(db) => db,
+          None => return,
+        };
+
+        stats.db.insert(db, parsed);
+      }
+    },
+  }
+}
+
+#[cfg(feature = "i-memory")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))]
+impl TryFrom<RedisValue> for MemoryStats {
+  type Error = RedisError;
+
+  fn try_from(value: RedisValue) -> Result<Self, Self::Error> {
+    let values: HashMap<Str, RedisValue> = value.convert()?;
+    let mut out = MemoryStats::default();
+
+    for (key, value) in values.into_iter() {
+      parse_memory_stat_field(&mut out, &key, value);
+    }
+    Ok(out)
+  }
+}
+
+/// The output of an entry in the slow queries log.
+///
+/// <https://redis.io/commands/slowlog#output-format>
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct SlowlogEntry {
+  pub id:        i64,
+  pub timestamp: i64,
+  pub duration:  Duration,
+  pub args:      Vec<RedisValue>,
+  pub ip:        Option<Str>,
+  pub name:      Option<Str>,
+}
+
+impl TryFrom<RedisValue> for SlowlogEntry {
+  type Error = RedisError;
+
+  fn try_from(value: RedisValue) -> Result<Self, Self::Error> {
+    if let RedisValue::Array(values) = value {
+      if values.len() < 4 {
+        return Err(RedisError::new(
+          RedisErrorKind::Protocol,
+          "Expected at least 4 response values.",
+        ));
+      }
+
+      let id = values[0]
+        .as_i64()
+        .ok_or(RedisError::new(RedisErrorKind::Protocol, "Expected integer ID."))?;
+      let timestamp = values[1]
+        .as_i64()
+        .ok_or(RedisError::new(RedisErrorKind::Protocol, "Expected integer timestamp."))?;
+      let duration = values[2]
+        .as_u64()
+        .map(Duration::from_micros)
+        .ok_or(RedisError::new(RedisErrorKind::Protocol, "Expected integer duration."))?;
+      let args = values[3].clone().into_multiple_values();
+
+      let (ip, name) = if values.len() == 6 {
+        let ip = values[4]
+          .as_bytes_str()
+          .ok_or(RedisError::new(RedisErrorKind::Protocol, "Expected IP address string."))?;
+        let name = values[5].as_bytes_str().ok_or(RedisError::new(
+          RedisErrorKind::Protocol,
+          "Expected client name string.",
+        ))?;
+
+        (Some(ip), Some(name))
+      } else {
+        (None, None)
+      };
+
+      Ok(SlowlogEntry {
+        id,
+        timestamp,
+        duration,
+        args,
+        ip,
+        name,
+      })
+    } else {
+      Err(RedisError::new_parse("Expected array."))
+    }
+  }
+}
+
+/// Flags for the SCRIPT DEBUG command.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ScriptDebugFlag {
+  Yes,
+  No,
+  Sync,
+}
+
+impl ScriptDebugFlag {
+  #[cfg(feature = "i-scripts")]
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      ScriptDebugFlag::Yes => "YES",
+      ScriptDebugFlag::No => "NO",
+      ScriptDebugFlag::Sync => "SYNC",
+    })
+  }
+}
+
+/// Arguments for the `SENTINEL SIMULATE-FAILURE` command.
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[cfg(feature = "sentinel-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))]
+pub enum SentinelFailureKind {
+  CrashAfterElection,
+  CrashAfterPromotion,
+  Help,
+}
+
+#[cfg(feature = "sentinel-client")]
+impl SentinelFailureKind {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match self {
+      SentinelFailureKind::CrashAfterElection => "crash-after-election",
+      SentinelFailureKind::CrashAfterPromotion => "crash-after-promotion",
+      SentinelFailureKind::Help => "help",
+    })
+  }
+}
+
+/// The sort order for redis commands that take or return a sorted list.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum SortOrder {
+  Asc,
+  Desc,
+}
+
+impl SortOrder {
+  #[allow(dead_code)]
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      SortOrder::Asc => "ASC",
+      SortOrder::Desc => "DESC",
+    })
+  }
+}
+
+/// The policy type for the [FUNCTION RESTORE](https://redis.io/commands/function-restore/) command.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum FnPolicy {
+  Flush,
+  Append,
+  Replace,
+}
+
+impl Default for FnPolicy {
+  fn default() -> Self {
+    FnPolicy::Append
+  }
+}
+
+impl FnPolicy {
+  #[cfg(feature = "i-scripts")]
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      FnPolicy::Flush => "FLUSH",
+      FnPolicy::Append => "APPEND",
+      FnPolicy::Replace => "REPLACE",
+    })
+  }
+
+  pub(crate) fn from_str(s: &str) -> Result<Self, RedisError> {
+    Ok(match s {
+      "flush" | "FLUSH" => FnPolicy::Flush,
+      "append" | "APPEND" => FnPolicy::Append,
+      "replace" | "REPLACE" => FnPolicy::Replace,
+      _ => {
+        return Err(RedisError::new(
+          RedisErrorKind::InvalidArgument,
+          "Invalid function restore policy.",
+        ))
+      },
+    })
+  }
+}
+
+// have to implement these for specific types to avoid conflicting with the core Into implementation
+impl TryFrom<&str> for FnPolicy {
+  type Error = RedisError;
+
+  fn try_from(value: &str) -> Result<Self, Self::Error> {
+    FnPolicy::from_str(value)
+  }
+}
+
+impl TryFrom<&String> for FnPolicy {
+  type Error = RedisError;
+
+  fn try_from(value: &String) -> Result<Self, Self::Error> {
+    FnPolicy::from_str(value.as_str())
+  }
+}
+
+impl TryFrom<String> for FnPolicy {
+  type Error = RedisError;
+
+  fn try_from(value: String) -> Result<Self, Self::Error> {
+    FnPolicy::from_str(value.as_str())
+  }
+}
+
+impl TryFrom<Str> for FnPolicy {
+  type Error = RedisError;
+
+  fn try_from(value: Str) -> Result<Self, Self::Error> {
+    FnPolicy::from_str(&value)
+  }
+}
+
+impl TryFrom<&Str> for FnPolicy {
+  type Error = RedisError;
+
+  fn try_from(value: &Str) -> Result<Self, Self::Error> {
+    FnPolicy::from_str(value)
+  }
+}
+
+/// Arguments to the CLIENT UNBLOCK command.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ClientUnblockFlag {
+  Timeout,
+  Error,
+}
+
+impl ClientUnblockFlag {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      ClientUnblockFlag::Timeout => "TIMEOUT",
+      ClientUnblockFlag::Error => "ERROR",
+    })
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/types/mod.rs.html b/doc/src/fred/types/mod.rs.html new file mode 100644 index 00000000..b7260fc6 --- /dev/null +++ b/doc/src/fred/types/mod.rs.html @@ -0,0 +1,165 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+
pub use crate::modules::response::{FromRedis, FromRedisKey};
+use crate::{error::RedisError, runtime::JoinHandle};
+pub use redis_protocol::resp3::types::{BytesFrame as Resp3Frame, RespVersion};
+
+mod args;
+mod builder;
+#[cfg(feature = "i-client")]
+mod client;
+#[cfg(feature = "i-cluster")]
+mod cluster;
+mod config;
+mod from_tuple;
+#[cfg(feature = "i-geo")]
+mod geo;
+#[cfg(feature = "i-lists")]
+mod lists;
+mod misc;
+mod multiple;
+#[cfg(feature = "i-redisearch")]
+mod redisearch;
+mod scan;
+#[cfg(feature = "i-scripts")]
+mod scripts;
+#[cfg(feature = "i-sorted-sets")]
+mod sorted_sets;
+#[cfg(feature = "i-streams")]
+mod streams;
+#[cfg(feature = "i-time-series")]
+mod timeseries;
+
+#[cfg(feature = "metrics")]
+#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
+pub use crate::modules::metrics::Stats;
+pub use args::*;
+pub use builder::*;
+#[cfg(feature = "i-client")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))]
+pub use client::*;
+#[cfg(feature = "i-cluster")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))]
+pub use cluster::*;
+pub use config::*;
+#[cfg(feature = "i-geo")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))]
+pub use geo::*;
+#[cfg(feature = "i-lists")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))]
+pub use lists::*;
+pub use misc::*;
+pub use multiple::*;
+#[cfg(feature = "i-redisearch")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))]
+pub use redisearch::*;
+pub use scan::*;
+#[cfg(feature = "i-scripts")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))]
+pub use scripts::*;
+pub use semver::Version;
+#[cfg(feature = "i-sorted-sets")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))]
+pub use sorted_sets::*;
+#[cfg(feature = "i-streams")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))]
+pub use streams::*;
+#[cfg(feature = "i-time-series")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+pub use timeseries::*;
+
+#[cfg(feature = "dns")]
+#[cfg_attr(docsrs, doc(cfg(feature = "dns")))]
+pub use crate::protocol::types::Resolve;
+
+pub(crate) static QUEUED: &str = "QUEUED";
+
+/// The ANY flag used on certain GEO commands.
+pub type Any = bool;
+/// The result from any of the `connect` functions showing the error that closed the connection, if any.
+pub type ConnectHandle = JoinHandle<Result<(), RedisError>>;
+/// A tuple of `(offset, count)` values for commands that allow paging through results.
+pub type Limit = (i64, i64);
+/// An argument type equivalent to "[LIMIT count]".
+pub type LimitCount = Option<i64>;
+
\ No newline at end of file diff --git a/doc/src/fred/types/multiple.rs.html b/doc/src/fred/types/multiple.rs.html new file mode 100644 index 00000000..66c24565 --- /dev/null +++ b/doc/src/fred/types/multiple.rs.html @@ -0,0 +1,301 @@ +multiple.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+
use crate::types::{RedisKey, RedisValue};
+use std::{collections::VecDeque, iter::FromIterator};
+
+/// Convenience struct for commands that take 1 or more keys.
+///
+/// **Note: this can also be used to represent an empty array of keys by passing `None` to any function that takes
+/// `Into<MultipleKeys>`.** This is mostly useful for `EVAL` and `EVALSHA`.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct MultipleKeys {
+  pub(crate) keys: Vec<RedisKey>,
+}
+
+impl MultipleKeys {
+  pub fn new() -> MultipleKeys {
+    MultipleKeys { keys: Vec::new() }
+  }
+
+  pub fn inner(self) -> Vec<RedisKey> {
+    self.keys
+  }
+
+  pub fn into_values(self) -> Vec<RedisValue> {
+    self.keys.into_iter().map(|k| k.into()).collect()
+  }
+
+  pub fn len(&self) -> usize {
+    self.keys.len()
+  }
+}
+
+impl From<Option<RedisKey>> for MultipleKeys {
+  fn from(key: Option<RedisKey>) -> Self {
+    let keys = if let Some(key) = key { vec![key] } else { vec![] };
+    MultipleKeys { keys }
+  }
+}
+
+impl<T> From<T> for MultipleKeys
+where
+  T: Into<RedisKey>,
+{
+  fn from(d: T) -> Self {
+    MultipleKeys { keys: vec![d.into()] }
+  }
+}
+
+impl<T> FromIterator<T> for MultipleKeys
+where
+  T: Into<RedisKey>,
+{
+  fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
+    MultipleKeys {
+      keys: iter.into_iter().map(|k| k.into()).collect(),
+    }
+  }
+}
+
+impl<'a, K, const N: usize> From<&'a [K; N]> for MultipleKeys
+where
+  K: Into<RedisKey> + Clone,
+{
+  fn from(value: &'a [K; N]) -> Self {
+    MultipleKeys {
+      keys: value.iter().map(|k| k.clone().into()).collect(),
+    }
+  }
+}
+
+impl<T> From<Vec<T>> for MultipleKeys
+where
+  T: Into<RedisKey>,
+{
+  fn from(d: Vec<T>) -> Self {
+    MultipleKeys {
+      keys: d.into_iter().map(|k| k.into()).collect(),
+    }
+  }
+}
+
+impl<T> From<VecDeque<T>> for MultipleKeys
+where
+  T: Into<RedisKey>,
+{
+  fn from(d: VecDeque<T>) -> Self {
+    MultipleKeys {
+      keys: d.into_iter().map(|k| k.into()).collect(),
+    }
+  }
+}
+
+impl From<()> for MultipleKeys {
+  fn from(_: ()) -> Self {
+    MultipleKeys { keys: Vec::new() }
+  }
+}
+
+/// Convenience interface for commands that take 1 or more strings.
+pub type MultipleStrings = MultipleKeys;
+
+/// Convenience interface for commands that take 1 or more values.
+pub type MultipleValues = RedisValue;
+
+/// A convenience struct for functions that take one or more hash slot values.
+pub struct MultipleHashSlots {
+  inner: Vec<u16>,
+}
+
+impl MultipleHashSlots {
+  pub fn inner(self) -> Vec<u16> {
+    self.inner
+  }
+
+  pub fn len(&self) -> usize {
+    self.inner.len()
+  }
+}
+
+impl From<u16> for MultipleHashSlots {
+  fn from(d: u16) -> Self {
+    MultipleHashSlots { inner: vec![d] }
+  }
+}
+
+impl From<Vec<u16>> for MultipleHashSlots {
+  fn from(d: Vec<u16>) -> Self {
+    MultipleHashSlots { inner: d }
+  }
+}
+
+impl<'a> From<&'a [u16]> for MultipleHashSlots {
+  fn from(d: &'a [u16]) -> Self {
+    MultipleHashSlots { inner: d.to_vec() }
+  }
+}
+
+impl From<VecDeque<u16>> for MultipleHashSlots {
+  fn from(d: VecDeque<u16>) -> Self {
+    MultipleHashSlots {
+      inner: d.into_iter().collect(),
+    }
+  }
+}
+
+impl FromIterator<u16> for MultipleHashSlots {
+  fn from_iter<I: IntoIterator<Item = u16>>(iter: I) -> Self {
+    MultipleHashSlots {
+      inner: iter.into_iter().collect(),
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/types/redisearch.rs.html b/doc/src/fred/types/redisearch.rs.html new file mode 100644 index 00000000..0082d150 --- /dev/null +++ b/doc/src/fred/types/redisearch.rs.html @@ -0,0 +1,1051 @@ +redisearch.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+
use crate::{
+  types::{GeoPosition, GeoUnit, Limit, RedisKey, RedisValue, SortOrder, ZRange},
+  utils,
+};
+use bytes::Bytes;
+use bytes_utils::Str;
+
+fn bool_args(b: bool) -> usize {
+  if b {
+    1
+  } else {
+    0
+  }
+}
+
+fn named_opt_args<T>(opt: &Option<T>) -> usize {
+  opt.as_ref().map(|_| 2).unwrap_or(0)
+}
+
+/// `GROUPBY` reducer functions.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ReducerFunc {
+  Count,
+  CountDistinct,
+  CountDistinctIsh,
+  Sum,
+  Min,
+  Max,
+  Avg,
+  StdDev,
+  Quantile,
+  ToList,
+  FirstValue,
+  RandomSample,
+  Custom(&'static str),
+}
+
+impl ReducerFunc {
+  pub(crate) fn to_str(&self) -> &'static str {
+    use ReducerFunc::*;
+
+    match self {
+      Count => "COUNT",
+      CountDistinct => "COUNT_DISTINCT",
+      CountDistinctIsh => "COUNT_DISTINCTISH",
+      Sum => "SUM",
+      Min => "MIN",
+      Max => "MAX",
+      Avg => "AVG",
+      StdDev => "STDDEV",
+      Quantile => "QUANTILE",
+      ToList => "TOLIST",
+      FirstValue => "FIRST_VALUE",
+      RandomSample => "RANDOM_SAMPLE",
+      Custom(v) => v,
+    }
+  }
+}
+
+/// `REDUCE` arguments in `FT.AGGREGATE`.
+///
+/// Equivalent to `function nargs arg [arg ...] [AS name]`
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct SearchReducer {
+  pub func: ReducerFunc,
+  pub args: Vec<Str>,
+  pub name: Option<Str>,
+}
+
+impl SearchReducer {
+  pub(crate) fn num_args(&self) -> usize {
+    3 + self.args.len() + named_opt_args(&self.name)
+  }
+}
+
+/// A search field with an optional property.
+///
+/// Typically equivalent to `identifier [AS property]` in `FT.AGGREGATE`, `FT.SEARCH`, etc.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct SearchField {
+  pub identifier: Str,
+  pub property:   Option<Str>,
+}
+
+impl SearchField {
+  pub(crate) fn num_args(&self) -> usize {
+    1 + named_opt_args(&self.property)
+  }
+}
+
+/// Arguments to `LOAD` in `FT.AGGREGATE`.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum Load {
+  All,
+  Some(Vec<SearchField>),
+}
+
+/// Arguments for `WITHCURSOR` in `FT.AGGREGATE`.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct WithCursor {
+  pub count:    Option<u64>,
+  pub max_idle: Option<u64>,
+}
+
+/// Arguments for `PARAMS` in `FT.AGGREGATE`.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct SearchParameter {
+  pub name:  Str,
+  pub value: Str,
+}
+
+/// An aggregation operation used in `FT.AGGREGATE`.
+///
+/// <https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/aggregations/>
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum AggregateOperation {
+  Filter {
+    expression: Str,
+  },
+  GroupBy {
+    /// An empty array is equivalent to `GROUPBY 0`
+    fields:   Vec<Str>,
+    reducers: Vec<SearchReducer>,
+  },
+  Apply {
+    expression: Str,
+    name:       Str,
+  },
+  SortBy {
+    properties: Vec<(Str, SortOrder)>,
+    max:        Option<u64>,
+  },
+  Limit {
+    offset: u64,
+    num:    u64,
+  },
+}
+
+impl AggregateOperation {
+  pub(crate) fn num_args(&self) -> usize {
+    match self {
+      AggregateOperation::Filter { .. } => 2,
+      AggregateOperation::Limit { .. } => 3,
+      AggregateOperation::Apply { .. } => 4,
+      AggregateOperation::SortBy { max, properties, .. } => 2 + (properties.len() * 2) + named_opt_args(max),
+      AggregateOperation::GroupBy { fields, reducers } => {
+        2 + fields.len() + reducers.iter().fold(0, |m, r| m + r.num_args())
+      },
+    }
+  }
+}
+
+/// Arguments to the `FT.AGGREGATE` command.
+///
+/// <https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/aggregations/>
+#[derive(Clone, Debug, Default)]
+pub struct FtAggregateOptions {
+  pub verbatim: bool,
+  pub load:     Option<Load>,
+  pub timeout:  Option<i64>,
+  pub pipeline: Vec<AggregateOperation>,
+  pub cursor:   Option<WithCursor>,
+  pub params:   Vec<SearchParameter>,
+  pub dialect:  Option<i64>,
+}
+
+impl FtAggregateOptions {
+  pub(crate) fn num_args(&self) -> usize {
+    let mut count = 0;
+    count += bool_args(self.verbatim);
+    if let Some(ref load) = self.load {
+      count += 1
+        + match load {
+          Load::All => 1,
+          Load::Some(ref v) => 1 + v.iter().fold(0, |m, f| m + f.num_args()),
+        };
+    }
+    count += named_opt_args(&self.timeout);
+    count += self.pipeline.iter().fold(0, |m, op| m + op.num_args());
+    if let Some(ref cursor) = self.cursor {
+      count += 1 + named_opt_args(&cursor.count) + named_opt_args(&cursor.max_idle);
+    }
+    if !self.params.is_empty() {
+      count += 2 + self.params.len() * 2;
+    }
+    count += named_opt_args(&self.dialect);
+
+    count
+  }
+}
+
+/// Arguments for `FILTER` in `FT.SEARCH`.
+///
+/// Callers should use the `*Score*` variants on any provided [ZRange](crate::types::ZRange) values.
+#[derive(Clone, Debug)]
+pub struct SearchFilter {
+  pub attribute: Str,
+  pub min:       ZRange,
+  pub max:       ZRange,
+}
+
+/// Arguments for `GEOFILTER` in `FT.SEARCH`.
+#[derive(Clone, Debug)]
+pub struct SearchGeoFilter {
+  pub attribute: Str,
+  pub position:  GeoPosition,
+  pub radius:    RedisValue,
+  pub units:     GeoUnit,
+}
+
+/// Arguments used in `SUMMARIZE` values.
+///
+/// <https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/highlight/>
+#[derive(Clone, Debug, Eq, PartialEq, Default)]
+pub struct SearchSummarize {
+  pub fields:    Vec<Str>,
+  pub frags:     Option<u64>,
+  pub len:       Option<u64>,
+  pub separator: Option<Str>,
+}
+
+/// Arguments used in `HIGHLIGHT` values.
+///
+/// <https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/highlight/>
+#[derive(Clone, Debug, Eq, PartialEq, Default)]
+pub struct SearchHighlight {
+  pub fields: Vec<Str>,
+  pub tags:   Option<(Str, Str)>,
+}
+
+/// Arguments for `SORTBY` in `FT.SEARCH`.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct SearchSortBy {
+  pub attribute: Str,
+  pub order:     Option<SortOrder>,
+  pub withcount: bool,
+}
+
+/// Arguments to `FT.SEARCH`.
+#[derive(Clone, Debug, Default)]
+pub struct FtSearchOptions {
+  pub nocontent:    bool,
+  pub verbatim:     bool,
+  pub nostopwords:  bool,
+  pub withscores:   bool,
+  pub withpayloads: bool,
+  pub withsortkeys: bool,
+  pub filters:      Vec<SearchFilter>,
+  pub geofilters:   Vec<SearchGeoFilter>,
+  pub inkeys:       Vec<RedisKey>,
+  pub infields:     Vec<Str>,
+  pub r#return:     Vec<SearchField>,
+  pub summarize:    Option<SearchSummarize>,
+  pub highlight:    Option<SearchHighlight>,
+  pub slop:         Option<i64>,
+  pub timeout:      Option<i64>,
+  pub inorder:      bool,
+  pub language:     Option<Str>,
+  pub expander:     Option<Str>,
+  pub scorer:       Option<Str>,
+  pub explainscore: bool,
+  pub payload:      Option<Bytes>,
+  pub sortby:       Option<SearchSortBy>,
+  pub limit:        Option<Limit>,
+  pub params:       Vec<SearchParameter>,
+  pub dialect:      Option<i64>,
+}
+
+impl FtSearchOptions {
+  pub(crate) fn num_args(&self) -> usize {
+    let mut count = 0;
+    count += bool_args(self.nocontent);
+    count += bool_args(self.verbatim);
+    count += bool_args(self.nostopwords);
+    count += bool_args(self.withscores);
+    count += bool_args(self.withpayloads);
+    count += bool_args(self.withsortkeys);
+    count += self.filters.len() * 4;
+    count += self.geofilters.len() * 6;
+    if !self.inkeys.is_empty() {
+      count += 2 + self.inkeys.len();
+    }
+    if !self.infields.is_empty() {
+      count += 2 + self.infields.len();
+    }
+    if !self.r#return.is_empty() {
+      count += 2;
+      for val in self.r#return.iter() {
+        count += if val.property.is_some() { 3 } else { 1 };
+      }
+    }
+    if let Some(ref summarize) = self.summarize {
+      count += 1;
+      if !summarize.fields.is_empty() {
+        count += 2 + summarize.fields.len();
+      }
+      count += named_opt_args(&summarize.frags);
+      count += named_opt_args(&summarize.len);
+      count += named_opt_args(&summarize.separator);
+    }
+    if let Some(ref highlight) = self.highlight {
+      count += 1;
+      if !highlight.fields.is_empty() {
+        count += 2 + highlight.fields.len();
+      }
+      if highlight.tags.is_some() {
+        count += 3;
+      }
+    }
+    count += named_opt_args(&self.slop);
+    count += named_opt_args(&self.timeout);
+    count += bool_args(self.inorder);
+    count += named_opt_args(&self.language);
+    count += named_opt_args(&self.expander);
+    count += named_opt_args(&self.scorer);
+    count += bool_args(self.explainscore);
+    count += named_opt_args(&self.payload);
+    if let Some(ref sort) = self.sortby {
+      count += 2 + if sort.order.is_some() { 1 } else { 0 } + bool_args(sort.withcount)
+    }
+    if self.limit.is_some() {
+      count += 3;
+    }
+    if !self.params.is_empty() {
+      count += 2 + self.params.len() * 2;
+    }
+    count += named_opt_args(&self.dialect);
+    count
+  }
+}
+
+/// Index arguments for `FT.CREATE`.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum IndexKind {
+  Hash,
+  JSON,
+}
+
+impl IndexKind {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match self {
+      IndexKind::JSON => "JSON",
+      IndexKind::Hash => "HASH",
+    })
+  }
+}
+
+/// Arguments for `FT.CREATE`.
+#[derive(Clone, Debug, Default)]
+pub struct FtCreateOptions {
+  pub on:              Option<IndexKind>,
+  pub prefixes:        Vec<Str>,
+  pub filter:          Option<Str>,
+  pub language:        Option<Str>,
+  pub language_field:  Option<Str>,
+  pub score:           Option<f64>,
+  pub score_field:     Option<f64>,
+  pub payload_field:   Option<Str>,
+  pub maxtextfields:   bool,
+  pub temporary:       Option<u64>,
+  pub nooffsets:       bool,
+  pub nohl:            bool,
+  pub nofields:        bool,
+  pub nofreqs:         bool,
+  pub stopwords:       Vec<Str>,
+  pub skipinitialscan: bool,
+}
+
+impl FtCreateOptions {
+  pub(crate) fn num_args(&self) -> usize {
+    let mut count = 0;
+    count += named_opt_args(&self.on);
+    if !self.prefixes.is_empty() {
+      count += 2 + self.prefixes.len();
+    }
+    count += named_opt_args(&self.filter);
+    count += named_opt_args(&self.language);
+    count += named_opt_args(&self.language_field);
+    count += named_opt_args(&self.score);
+    count += named_opt_args(&self.score_field);
+    count += named_opt_args(&self.payload_field);
+    count += bool_args(self.maxtextfields);
+    count += named_opt_args(&self.temporary);
+    count += bool_args(self.nooffsets);
+    count += bool_args(self.nohl);
+    count += bool_args(self.nofields);
+    count += bool_args(self.nofreqs);
+    if !self.stopwords.is_empty() {
+      count += 2 + self.stopwords.len();
+    }
+    count += bool_args(self.skipinitialscan);
+
+    count
+  }
+}
+
+/// One of the available schema types used with `FT.CREATE` or `FT.ALTER`.
+#[derive(Clone, Debug)]
+pub enum SearchSchemaKind {
+  Text {
+    sortable:       bool,
+    unf:            bool,
+    nostem:         bool,
+    phonetic:       Option<Str>,
+    weight:         Option<i64>,
+    withsuffixtrie: bool,
+    noindex:        bool,
+  },
+  Tag {
+    sortable:       bool,
+    unf:            bool,
+    separator:      Option<char>,
+    casesensitive:  bool,
+    withsuffixtrie: bool,
+    noindex:        bool,
+  },
+  Numeric {
+    sortable: bool,
+    unf:      bool,
+    noindex:  bool,
+  },
+  Geo {
+    sortable: bool,
+    unf:      bool,
+    noindex:  bool,
+  },
+  Vector {
+    noindex: bool,
+  },
+  GeoShape {
+    noindex: bool,
+  },
+  Custom {
+    name:      Str,
+    arguments: Vec<RedisValue>,
+  },
+}
+
+impl SearchSchemaKind {
+  pub(crate) fn num_args(&self) -> usize {
+    match self {
+      SearchSchemaKind::Custom { arguments, .. } => 1 + arguments.len(),
+      SearchSchemaKind::GeoShape { noindex } | SearchSchemaKind::Vector { noindex } => 1 + bool_args(*noindex),
+      SearchSchemaKind::Geo { sortable, unf, noindex } | SearchSchemaKind::Numeric { sortable, unf, noindex } => {
+        1 + bool_args(*sortable) + bool_args(*unf) + bool_args(*noindex)
+      },
+      SearchSchemaKind::Tag {
+        sortable,
+        unf,
+        separator,
+        casesensitive,
+        withsuffixtrie,
+        noindex,
+      } => {
+        1 + bool_args(*sortable)
+          + bool_args(*unf)
+          + named_opt_args(separator)
+          + bool_args(*casesensitive)
+          + bool_args(*withsuffixtrie)
+          + bool_args(*noindex)
+      },
+      SearchSchemaKind::Text {
+        sortable,
+        unf,
+        nostem,
+        phonetic,
+        weight,
+        withsuffixtrie,
+        noindex,
+      } => {
+        1 + bool_args(*sortable)
+          + bool_args(*unf)
+          + bool_args(*nostem)
+          + named_opt_args(phonetic)
+          + named_opt_args(weight)
+          + bool_args(*withsuffixtrie)
+          + bool_args(*noindex)
+      },
+    }
+  }
+}
+
+/// Arguments for `SCHEMA` in `FT.CREATE`.
+#[derive(Clone, Debug)]
+pub struct SearchSchema {
+  pub field_name: Str,
+  pub alias:      Option<Str>,
+  pub kind:       SearchSchemaKind,
+}
+
+impl SearchSchema {
+  pub(crate) fn num_args(&self) -> usize {
+    2 + named_opt_args(&self.alias) + self.kind.num_args()
+  }
+}
+
+/// Arguments to `FT.ALTER`.
+#[derive(Clone, Debug)]
+pub struct FtAlterOptions {
+  pub skipinitialscan: bool,
+  pub attribute:       Str,
+  pub options:         SearchSchemaKind,
+}
+
+impl FtAlterOptions {
+  pub(crate) fn num_args(&self) -> usize {
+    3 + bool_args(self.skipinitialscan) + self.options.num_args()
+  }
+}
+
+/// Arguments to `TERMS` in `FT.SPELLCHECK`,
+#[derive(Clone, Debug)]
+pub enum SpellcheckTerms {
+  Include { dictionary: Str, terms: Vec<Str> },
+  Exclude { dictionary: Str, terms: Vec<Str> },
+}
+
+impl SpellcheckTerms {
+  pub(crate) fn num_args(&self) -> usize {
+    3 + match self {
+      SpellcheckTerms::Include { terms, .. } => terms.len(),
+      SpellcheckTerms::Exclude { terms, .. } => terms.len(),
+    }
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/types/scan.rs.html b/doc/src/fred/types/scan.rs.html new file mode 100644 index 00000000..ddb2c0ed --- /dev/null +++ b/doc/src/fred/types/scan.rs.html @@ -0,0 +1,521 @@ +scan.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+
use crate::{
+  clients::RedisClient,
+  error::RedisError,
+  interfaces,
+  modules::inner::RedisClientInner,
+  protocol::{
+    command::{RedisCommand, RedisCommandKind},
+    responders::ResponseKind,
+    types::{KeyScanInner, ValueScanInner},
+  },
+  runtime::RefCount,
+  types::{RedisKey, RedisMap, RedisValue},
+  utils,
+};
+use bytes_utils::Str;
+use std::borrow::Cow;
+
+/// The types of values supported by the [type](https://redis.io/commands/type) command.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ScanType {
+  Set,
+  String,
+  ZSet,
+  List,
+  Hash,
+  Stream,
+}
+
+impl ScanType {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      ScanType::Set => "set",
+      ScanType::String => "string",
+      ScanType::List => "list",
+      ScanType::ZSet => "zset",
+      ScanType::Hash => "hash",
+      ScanType::Stream => "stream",
+    })
+  }
+}
+
+/// An interface for interacting with the results of a scan operation.
+pub trait Scanner {
+  /// The type of results from the scan operation.
+  type Page;
+
+  /// Read the cursor returned from the last scan operation.
+  fn cursor(&self) -> Option<Cow<str>>;
+
+  /// Whether the scan call will continue returning results. If `false` this will be the last result set
+  /// returned on the stream.
+  ///
+  /// Calling `next` when this returns `false` will return `Ok(())`, so this does not need to be checked on each
+  /// result.
+  fn has_more(&self) -> bool;
+
+  /// Return a reference to the last page of results.
+  fn results(&self) -> &Option<Self::Page>;
+
+  /// Take ownership over the results of the SCAN operation. Calls to `results` or `take_results` will return `None`
+  /// afterwards.
+  fn take_results(&mut self) -> Option<Self::Page>;
+
+  /// A lightweight function to create a Redis client from the SCAN result.
+  ///
+  /// To continue scanning the caller should call `next` on this struct. Calling `scan` again on the client will
+  /// initiate a new SCAN call starting with a cursor of 0.
+  fn create_client(&self) -> RedisClient;
+
+  /// Move on to the next page of results from the SCAN operation. If no more results are available this may close the
+  /// stream.
+  ///
+  /// **This must be called to continue scanning the keyspace.** Results are not automatically scanned in the
+  /// background since this could cause the  buffer backing the stream to grow too large very quickly. This
+  /// interface provides a mechanism for throttling the throughput of the SCAN call. If this struct is dropped
+  /// without calling this function the stream will close without an error.
+  ///
+  /// If this function returns an error the scan call cannot continue as the client has been closed, or some other
+  /// fatal error has occurred. If this happens the error will appear in the stream from the original SCAN call.
+  fn next(self) -> Result<(), RedisError>;
+}
+
+/// The result of a SCAN operation.
+pub struct ScanResult {
+  pub(crate) results:      Option<Vec<RedisKey>>,
+  pub(crate) inner:        RefCount<RedisClientInner>,
+  pub(crate) scan_state:   KeyScanInner,
+  pub(crate) can_continue: bool,
+}
+
+impl Scanner for ScanResult {
+  type Page = Vec<RedisKey>;
+
+  fn cursor(&self) -> Option<Cow<str>> {
+    self.scan_state.args[self.scan_state.cursor_idx].as_str()
+  }
+
+  fn has_more(&self) -> bool {
+    self.can_continue
+  }
+
+  fn results(&self) -> &Option<Self::Page> {
+    &self.results
+  }
+
+  fn take_results(&mut self) -> Option<Self::Page> {
+    self.results.take()
+  }
+
+  fn create_client(&self) -> RedisClient {
+    RedisClient {
+      inner: self.inner.clone(),
+    }
+  }
+
+  fn next(self) -> Result<(), RedisError> {
+    if !self.can_continue {
+      return Ok(());
+    }
+
+    let cluster_node = self.scan_state.server.clone();
+    let response = ResponseKind::KeyScan(self.scan_state);
+    let mut cmd: RedisCommand = (RedisCommandKind::Scan, Vec::new(), response).into();
+    cmd.cluster_node = cluster_node;
+
+    interfaces::default_send_command(&self.inner, cmd)
+  }
+}
+
+/// The result of a HSCAN operation.
+pub struct HScanResult {
+  pub(crate) results:      Option<RedisMap>,
+  pub(crate) inner:        RefCount<RedisClientInner>,
+  pub(crate) scan_state:   ValueScanInner,
+  pub(crate) can_continue: bool,
+}
+
+impl Scanner for HScanResult {
+  type Page = RedisMap;
+
+  fn cursor(&self) -> Option<Cow<str>> {
+    self.scan_state.args[self.scan_state.cursor_idx].as_str()
+  }
+
+  fn has_more(&self) -> bool {
+    self.can_continue
+  }
+
+  fn results(&self) -> &Option<Self::Page> {
+    &self.results
+  }
+
+  fn take_results(&mut self) -> Option<Self::Page> {
+    self.results.take()
+  }
+
+  fn create_client(&self) -> RedisClient {
+    RedisClient {
+      inner: self.inner.clone(),
+    }
+  }
+
+  fn next(self) -> Result<(), RedisError> {
+    if !self.can_continue {
+      return Ok(());
+    }
+
+    let response = ResponseKind::ValueScan(self.scan_state);
+    let cmd: RedisCommand = (RedisCommandKind::Hscan, Vec::new(), response).into();
+    interfaces::default_send_command(&self.inner, cmd)
+  }
+}
+
+/// The result of a SSCAN operation.
+pub struct SScanResult {
+  pub(crate) results:      Option<Vec<RedisValue>>,
+  pub(crate) inner:        RefCount<RedisClientInner>,
+  pub(crate) scan_state:   ValueScanInner,
+  pub(crate) can_continue: bool,
+}
+
+impl Scanner for SScanResult {
+  type Page = Vec<RedisValue>;
+
+  fn cursor(&self) -> Option<Cow<str>> {
+    self.scan_state.args[self.scan_state.cursor_idx].as_str()
+  }
+
+  fn results(&self) -> &Option<Self::Page> {
+    &self.results
+  }
+
+  fn take_results(&mut self) -> Option<Self::Page> {
+    self.results.take()
+  }
+
+  fn has_more(&self) -> bool {
+    self.can_continue
+  }
+
+  fn create_client(&self) -> RedisClient {
+    RedisClient {
+      inner: self.inner.clone(),
+    }
+  }
+
+  fn next(self) -> Result<(), RedisError> {
+    if !self.can_continue {
+      return Ok(());
+    }
+
+    let response = ResponseKind::ValueScan(self.scan_state);
+    let cmd: RedisCommand = (RedisCommandKind::Sscan, Vec::new(), response).into();
+    interfaces::default_send_command(&self.inner, cmd)
+  }
+}
+
+/// The result of a ZSCAN operation.
+pub struct ZScanResult {
+  pub(crate) results:      Option<Vec<(RedisValue, f64)>>,
+  pub(crate) inner:        RefCount<RedisClientInner>,
+  pub(crate) scan_state:   ValueScanInner,
+  pub(crate) can_continue: bool,
+}
+
+impl Scanner for ZScanResult {
+  type Page = Vec<(RedisValue, f64)>;
+
+  fn cursor(&self) -> Option<Cow<str>> {
+    self.scan_state.args[self.scan_state.cursor_idx].as_str()
+  }
+
+  fn has_more(&self) -> bool {
+    self.can_continue
+  }
+
+  fn results(&self) -> &Option<Self::Page> {
+    &self.results
+  }
+
+  fn take_results(&mut self) -> Option<Self::Page> {
+    self.results.take()
+  }
+
+  fn create_client(&self) -> RedisClient {
+    RedisClient {
+      inner: self.inner.clone(),
+    }
+  }
+
+  fn next(self) -> Result<(), RedisError> {
+    if !self.can_continue {
+      return Ok(());
+    }
+
+    let response = ResponseKind::ValueScan(self.scan_state);
+    let cmd: RedisCommand = (RedisCommandKind::Zscan, Vec::new(), response).into();
+    interfaces::default_send_command(&self.inner, cmd)
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/types/scripts.rs.html b/doc/src/fred/types/scripts.rs.html new file mode 100644 index 00000000..7877c280 --- /dev/null +++ b/doc/src/fred/types/scripts.rs.html @@ -0,0 +1,703 @@ +scripts.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+
use crate::{
+  clients::RedisClient,
+  interfaces::{FunctionInterface, LuaInterface},
+  prelude::{FromRedis, RedisError, RedisResult},
+  types::{MultipleKeys, MultipleValues, RedisValue},
+};
+#[cfg(feature = "sha-1")]
+use crate::{prelude::RedisErrorKind, util::sha1_hash};
+use bytes_utils::Str;
+use std::{
+  cmp::Ordering,
+  collections::HashMap,
+  convert::TryInto,
+  fmt,
+  fmt::Formatter,
+  hash::{Hash, Hasher},
+  ops::Deref,
+};
+
+/// An interface for caching and running lua scripts.
+///
+/// ```rust no_run
+/// # use fred::types::Script;
+/// # use fred::prelude::*;
+/// async fn example(client: &RedisClient) -> Result<(), RedisError> {
+///   let script = Script::from_lua("return ARGV[1]");
+///   assert_eq!(script.sha1(), "098e0f0d1448c0a81dafe820f66d460eb09263da");
+///
+///   let _ = script.load(client).await?;
+///   let result: String = script.evalsha(client, "key", "arg").await?;
+///   assert_eq!(result, "arg");
+///   Ok(())
+/// }
+/// ```
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Script {
+  lua:  Option<Str>,
+  hash: Str,
+}
+
+impl fmt::Display for Script {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    write!(f, "{}", self.hash)
+  }
+}
+
+impl Hash for Script {
+  fn hash<H: Hasher>(&self, state: &mut H) {
+    self.hash.hash(state);
+  }
+}
+
+impl Ord for Script {
+  fn cmp(&self, other: &Self) -> Ordering {
+    self.hash.cmp(&other.hash)
+  }
+}
+
+impl PartialOrd for Script {
+  fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+    Some(self.cmp(other))
+  }
+}
+
+impl Script {
+  /// Create a new `Script` from a lua script.
+  #[cfg(feature = "sha-1")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "sha-1")))]
+  pub fn from_lua<S: Into<Str>>(lua: S) -> Self {
+    let lua: Str = lua.into();
+    let hash = Str::from(sha1_hash(&lua));
+
+    Script { lua: Some(lua), hash }
+  }
+
+  /// Create a new `Script` from a lua hash.
+  pub fn from_hash<S: Into<Str>>(hash: S) -> Self {
+    Script {
+      lua:  None,
+      hash: hash.into(),
+    }
+  }
+
+  /// Read the lua script contents.
+  pub fn lua(&self) -> Option<&Str> {
+    self.lua.as_ref()
+  }
+
+  /// Read the SHA-1 hash for the script.
+  pub fn sha1(&self) -> &Str {
+    &self.hash
+  }
+
+  /// Call `SCRIPT LOAD` on all the associated servers. This must be
+  /// called once before calling [evalsha](Self::evalsha).
+  #[cfg(feature = "sha-1")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "sha-1")))]
+  pub async fn load(&self, client: &RedisClient) -> RedisResult<()> {
+    if let Some(ref lua) = self.lua {
+      client.script_load_cluster::<(), _>(lua.clone()).await
+    } else {
+      Err(RedisError::new(RedisErrorKind::Unknown, "Missing lua script contents."))
+    }
+  }
+
+  /// Send `EVALSHA` to the server with the provided arguments.
+  pub async fn evalsha<R, C, K, V>(&self, client: &C, keys: K, args: V) -> RedisResult<R>
+  where
+    R: FromRedis,
+    C: LuaInterface + Send + Sync,
+    K: Into<MultipleKeys> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    client.evalsha(self.hash.clone(), keys, args).await
+  }
+
+  /// Send `EVALSHA` to the server with the provided arguments. Automatically `SCRIPT LOAD` in case
+  /// of `NOSCRIPT` error and try `EVALSHA` again.
+  #[cfg(feature = "sha-1")]
+  #[cfg_attr(docsrs, doc(cfg(feature = "sha-1")))]
+  pub async fn evalsha_with_reload<R, K, V>(&self, client: &RedisClient, keys: K, args: V) -> RedisResult<R>
+  where
+    R: FromRedis,
+    K: Into<MultipleKeys> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    into!(keys);
+    try_into!(args);
+
+    match client.evalsha(self.hash.clone(), keys.clone(), args.clone()).await {
+      Err(error) if error.details().starts_with("NOSCRIPT") => {
+        self.load(client).await?;
+        client.evalsha(self.hash.clone(), keys, args).await
+      },
+      result => result,
+    }
+  }
+}
+
+/// Possible [flags](https://redis.io/docs/manual/programmability/lua-api/) associated with a [Function](crate::types::Function).
+#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub enum FunctionFlag {
+  NoWrites,
+  AllowOOM,
+  NoCluster,
+  AllowCrossSlotKeys,
+  AllowStale,
+}
+
+impl FunctionFlag {
+  /// Parse the string representation of the flag.
+  #[allow(clippy::should_implement_trait)]
+  pub fn from_str(s: &str) -> Option<Self> {
+    Some(match s {
+      "allow-oom" => FunctionFlag::AllowOOM,
+      "allow-stale" => FunctionFlag::AllowStale,
+      "allow-cross-slot-keys" => FunctionFlag::AllowCrossSlotKeys,
+      "no-writes" => FunctionFlag::NoWrites,
+      "no-cluster" => FunctionFlag::NoCluster,
+      _ => return None,
+    })
+  }
+
+  /// Convert to the string representation of the flag.
+  pub fn to_str(&self) -> &'static str {
+    match self {
+      FunctionFlag::AllowCrossSlotKeys => "allow-cross-slot-keys",
+      FunctionFlag::AllowOOM => "allow-oom",
+      FunctionFlag::NoCluster => "no-cluster",
+      FunctionFlag::NoWrites => "no-writes",
+      FunctionFlag::AllowStale => "allow-stale",
+    }
+  }
+}
+
+/// An individual function within a [Library](crate::types::Library).
+///
+/// See the [library documentation](crate::types::Library) for more information.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Function {
+  pub(crate) name:  Str,
+  pub(crate) flags: Vec<FunctionFlag>,
+}
+
+impl fmt::Display for Function {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    write!(f, "{}", self.name)
+  }
+}
+
+impl Hash for Function {
+  fn hash<H: Hasher>(&self, state: &mut H) {
+    self.name.hash(state);
+  }
+}
+
+impl Ord for Function {
+  fn cmp(&self, other: &Self) -> Ordering {
+    self.name.cmp(&other.name)
+  }
+}
+
+impl PartialOrd for Function {
+  fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+    Some(self.cmp(other))
+  }
+}
+
+impl Function {
+  /// Create a new `Function`.
+  pub fn new<S: Into<Str>>(name: S, flags: Vec<FunctionFlag>) -> Self {
+    Function {
+      name: name.into(),
+      flags,
+    }
+  }
+
+  /// Read the name of the function.
+  pub fn name(&self) -> &Str {
+    &self.name
+  }
+
+  /// Read the flags associated with the function.
+  pub fn flags(&self) -> &[FunctionFlag] {
+    &self.flags
+  }
+
+  /// Send the [fcall](crate::interfaces::FunctionInterface::fcall) command via the provided client.
+  pub async fn fcall<R, C, K, V>(&self, client: &C, keys: K, args: V) -> RedisResult<R>
+  where
+    R: FromRedis,
+    C: FunctionInterface + Send + Sync,
+    K: Into<MultipleKeys> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    client.fcall(self.name.clone(), keys, args).await
+  }
+
+  /// Send the [fcall_ro](crate::interfaces::FunctionInterface::fcall_ro) command via the provided client.
+  pub async fn fcall_ro<R, C, K, V>(&self, client: &C, keys: K, args: V) -> RedisResult<R>
+  where
+    R: FromRedis,
+    C: FunctionInterface + Send + Sync,
+    K: Into<MultipleKeys> + Send,
+    V: TryInto<MultipleValues> + Send,
+    V::Error: Into<RedisError> + Send,
+  {
+    client.fcall_ro(self.name.clone(), keys, args).await
+  }
+}
+
+/// A helper struct for interacting with [libraries and functions](https://redis.io/docs/manual/programmability/functions-intro/).
+///
+/// ```rust no_run
+/// # use fred::types::{FunctionFlag, Library};
+/// let code = "#!lua name=mylib \n redis.register_function('myfunc', function(keys, args) return \
+///             args[1] end)";
+/// let library = Library::from_code(client, code).await?;
+/// assert_eq!(library.name(), "mylib");
+///
+/// if let Some(func) = library.functions().get("myfunc") {
+///   if func.flags().contains(&FunctionFlag::NoWrites) {
+///     let _: () = func.fcall_ro(client, "key", "arg").await?;
+///   } else {
+///     let _: () = func.fcall(client, "key", "arg").await?;
+///   }
+/// }
+/// ```
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Library {
+  name:      Str,
+  functions: HashMap<Str, Function>,
+}
+
+impl fmt::Display for Library {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    write!(f, "{}", self.name)
+  }
+}
+
+impl Hash for Library {
+  fn hash<H: Hasher>(&self, state: &mut H) {
+    self.name.hash(state);
+  }
+}
+
+impl Ord for Library {
+  fn cmp(&self, other: &Self) -> Ordering {
+    self.name.cmp(&other.name)
+  }
+}
+
+impl PartialOrd for Library {
+  fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+    Some(self.cmp(other))
+  }
+}
+
+impl Library {
+  /// Create a new `Library` with the provided code, loading it on all the servers and inspecting the contents via the [FUNCTION LIST](https://redis.io/commands/function-list/) command.
+  ///
+  /// This interface will load the library on the server.
+  pub async fn from_code<S>(client: &RedisClient, code: S) -> Result<Self, RedisError>
+  where
+    S: Into<Str>,
+  {
+    let code = code.into();
+    let name: Str = client.function_load_cluster(true, code).await?;
+    let functions = client
+      .function_list::<RedisValue, _>(Some(name.deref()), false)
+      .await?
+      .as_functions(&name)?;
+
+    Ok(Library {
+      name,
+      functions: functions.into_iter().map(|f| (f.name.clone(), f)).collect(),
+    })
+  }
+
+  /// Create a new `Library` with the associated name, inspecting the library contents via the [FUNCTION LIST](https://redis.io/commands/function-list/) command.
+  ///
+  /// This interface assumes the library is already loaded on the server.
+  pub async fn from_name<S>(client: &RedisClient, name: S) -> Result<Self, RedisError>
+  where
+    S: Into<Str>,
+  {
+    let name = name.into();
+    let functions = client
+      .function_list::<RedisValue, _>(Some(name.deref()), false)
+      .await?
+      .as_functions(&name)?;
+
+    Ok(Library {
+      name,
+      functions: functions.into_iter().map(|f| (f.name.clone(), f)).collect(),
+    })
+  }
+
+  /// Read the name of the library.
+  pub fn name(&self) -> &Str {
+    &self.name
+  }
+
+  /// Read the functions contained within this library.
+  pub fn functions(&self) -> &HashMap<Str, Function> {
+    &self.functions
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/types/sorted_sets.rs.html b/doc/src/fred/types/sorted_sets.rs.html new file mode 100644 index 00000000..4ea49984 --- /dev/null +++ b/doc/src/fred/types/sorted_sets.rs.html @@ -0,0 +1,741 @@ +sorted_sets.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  types::RedisValue,
+  utils,
+};
+use bytes_utils::Str;
+use std::{
+  collections::VecDeque,
+  convert::{TryFrom, TryInto},
+  iter::FromIterator,
+};
+
+/// `MIN|MAX` arguments for `BZMPOP`, etc.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ZCmp {
+  Min,
+  Max,
+}
+
+impl ZCmp {
+  pub(crate) fn to_str(&self) -> &'static str {
+    match self {
+      ZCmp::Min => "MIN",
+      ZCmp::Max => "MAX",
+    }
+  }
+}
+
+/// Convenience struct for `ZINTERSTORE` and `ZUNIONSTORE` when accepting 1 or more `weights` arguments.
+pub struct MultipleWeights {
+  values: Vec<f64>,
+}
+
+impl MultipleWeights {
+  pub fn new() -> MultipleWeights {
+    MultipleWeights { values: Vec::new() }
+  }
+
+  pub fn inner(self) -> Vec<f64> {
+    self.values
+  }
+
+  pub fn len(&self) -> usize {
+    self.values.len()
+  }
+}
+
+impl From<Option<f64>> for MultipleWeights {
+  fn from(d: Option<f64>) -> Self {
+    match d {
+      Some(w) => w.into(),
+      None => MultipleWeights::new(),
+    }
+  }
+}
+
+impl From<f64> for MultipleWeights {
+  fn from(d: f64) -> Self {
+    MultipleWeights { values: vec![d] }
+  }
+}
+
+impl FromIterator<f64> for MultipleWeights {
+  fn from_iter<I: IntoIterator<Item = f64>>(iter: I) -> Self {
+    MultipleWeights {
+      values: iter.into_iter().collect(),
+    }
+  }
+}
+
+impl From<Vec<f64>> for MultipleWeights {
+  fn from(d: Vec<f64>) -> Self {
+    MultipleWeights { values: d }
+  }
+}
+
+impl From<VecDeque<f64>> for MultipleWeights {
+  fn from(d: VecDeque<f64>) -> Self {
+    MultipleWeights {
+      values: d.into_iter().collect(),
+    }
+  }
+}
+
+/// Convenience struct for the `ZADD` command to accept 1 or more `(score, value)` arguments.
+pub struct MultipleZaddValues {
+  values: Vec<(f64, RedisValue)>,
+}
+
+impl MultipleZaddValues {
+  pub fn new() -> MultipleZaddValues {
+    MultipleZaddValues { values: Vec::new() }
+  }
+
+  pub fn inner(self) -> Vec<(f64, RedisValue)> {
+    self.values
+  }
+
+  pub fn len(&self) -> usize {
+    self.values.len()
+  }
+}
+
+impl<T> TryFrom<(f64, T)> for MultipleZaddValues
+where
+  T: TryInto<RedisValue>,
+  T::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from((f, d): (f64, T)) -> Result<Self, Self::Error> {
+    Ok(MultipleZaddValues {
+      values: vec![(f, to!(d)?)],
+    })
+  }
+}
+
+impl<T> FromIterator<(f64, T)> for MultipleZaddValues
+where
+  T: Into<RedisValue>,
+{
+  fn from_iter<I: IntoIterator<Item = (f64, T)>>(iter: I) -> Self {
+    MultipleZaddValues {
+      values: iter.into_iter().map(|(f, d)| (f, d.into())).collect(),
+    }
+  }
+}
+
+impl<T> TryFrom<Vec<(f64, T)>> for MultipleZaddValues
+where
+  T: TryInto<RedisValue>,
+  T::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(d: Vec<(f64, T)>) -> Result<Self, Self::Error> {
+    let mut values = Vec::with_capacity(d.len());
+    for (f, v) in d.into_iter() {
+      values.push((f, to!(v)?));
+    }
+
+    Ok(MultipleZaddValues { values })
+  }
+}
+
+impl<T> TryFrom<VecDeque<(f64, T)>> for MultipleZaddValues
+where
+  T: TryInto<RedisValue>,
+  T::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(d: VecDeque<(f64, T)>) -> Result<Self, Self::Error> {
+    let mut values = Vec::with_capacity(d.len());
+    for (f, v) in d.into_iter() {
+      values.push((f, to!(v)?));
+    }
+
+    Ok(MultipleZaddValues { values })
+  }
+}
+
+/// Ordering options for the ZADD (and related) commands.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum Ordering {
+  GreaterThan,
+  LessThan,
+}
+
+impl Ordering {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      Ordering::GreaterThan => "GT",
+      Ordering::LessThan => "LT",
+    })
+  }
+}
+
+/// Options for the ZRANGE (and related) commands.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ZSort {
+  ByScore,
+  ByLex,
+}
+
+impl ZSort {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      ZSort::ByScore => "BYSCORE",
+      ZSort::ByLex => "BYLEX",
+    })
+  }
+}
+
+/// An index, score, lexicographical, or +|-|+inf|-inf range bound for the ZRANGE command.
+#[derive(Clone, Debug)]
+pub enum ZRangeBound {
+  /// Index ranges (<https://redis.io/commands/zrange#index-ranges>)
+  Index(i64),
+  /// Score ranges (<https://redis.io/commands/zrange#score-ranges>)
+  Score(f64),
+  /// Lexicographical ranges (<https://redis.io/commands/zrange#lexicographical-ranges>)
+  Lex(String),
+  /// Shortcut for the `+` character.
+  InfiniteLex,
+  /// Shortcut for the `-` character.
+  NegInfinityLex,
+  /// Shortcut for the `+inf` range bound.
+  InfiniteScore,
+  /// Shortcut for the `-inf` range bound.
+  NegInfiniteScore,
+}
+
+impl From<i64> for ZRangeBound {
+  fn from(i: i64) -> Self {
+    ZRangeBound::Index(i)
+  }
+}
+
+impl<'a> From<&'a str> for ZRangeBound {
+  fn from(s: &'a str) -> Self {
+    if s == "+inf" {
+      ZRangeBound::InfiniteScore
+    } else if s == "-inf" {
+      ZRangeBound::NegInfiniteScore
+    } else {
+      ZRangeBound::Lex(s.to_owned())
+    }
+  }
+}
+
+impl From<String> for ZRangeBound {
+  fn from(s: String) -> Self {
+    if s == "+inf" {
+      ZRangeBound::InfiniteScore
+    } else if s == "-inf" {
+      ZRangeBound::NegInfiniteScore
+    } else {
+      ZRangeBound::Lex(s)
+    }
+  }
+}
+
+impl<'a> From<&'a String> for ZRangeBound {
+  fn from(s: &'a String) -> Self {
+    s.as_str().into()
+  }
+}
+
+impl TryFrom<f64> for ZRangeBound {
+  type Error = RedisError;
+
+  fn try_from(f: f64) -> Result<Self, Self::Error> {
+    let value = if f.is_infinite() && f.is_sign_negative() {
+      ZRangeBound::NegInfiniteScore
+    } else if f.is_infinite() {
+      ZRangeBound::InfiniteScore
+    } else if f.is_nan() {
+      return Err(RedisError::new(
+        RedisErrorKind::Unknown,
+        "Cannot use NaN as zrange field.",
+      ));
+    } else {
+      ZRangeBound::Score(f)
+    };
+
+    Ok(value)
+  }
+}
+
+/// The type of range interval bound.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum ZRangeKind {
+  Inclusive,
+  Exclusive,
+}
+
+impl Default for ZRangeKind {
+  fn default() -> Self {
+    ZRangeKind::Inclusive
+  }
+}
+
+/// A wrapper struct for a range bound in a sorted set command.
+#[derive(Clone, Debug)]
+pub struct ZRange {
+  pub kind:  ZRangeKind,
+  pub range: ZRangeBound,
+}
+
+impl ZRange {
+  pub(crate) fn into_value(self) -> Result<RedisValue, RedisError> {
+    let value = if self.kind == ZRangeKind::Exclusive {
+      match self.range {
+        ZRangeBound::Index(i) => format!("({}", i).into(),
+        ZRangeBound::Score(f) => utils::f64_to_zrange_bound(f, &self.kind)?.into(),
+        ZRangeBound::Lex(s) => utils::check_lex_str(s, &self.kind).into(),
+        ZRangeBound::InfiniteLex => RedisValue::from_static_str("+"),
+        ZRangeBound::NegInfinityLex => RedisValue::from_static_str("-"),
+        ZRangeBound::InfiniteScore => RedisValue::from_static_str("+inf"),
+        ZRangeBound::NegInfiniteScore => RedisValue::from_static_str("-inf"),
+      }
+    } else {
+      match self.range {
+        ZRangeBound::Index(i) => i.into(),
+        ZRangeBound::Score(f) => f.try_into()?,
+        ZRangeBound::Lex(s) => utils::check_lex_str(s, &self.kind).into(),
+        ZRangeBound::InfiniteLex => RedisValue::from_static_str("+"),
+        ZRangeBound::NegInfinityLex => RedisValue::from_static_str("-"),
+        ZRangeBound::InfiniteScore => RedisValue::from_static_str("+inf"),
+        ZRangeBound::NegInfiniteScore => RedisValue::from_static_str("-inf"),
+      }
+    };
+
+    Ok(value)
+  }
+}
+
+impl From<i64> for ZRange {
+  fn from(i: i64) -> Self {
+    ZRange {
+      kind:  ZRangeKind::default(),
+      range: i.into(),
+    }
+  }
+}
+
+impl<'a> From<&'a str> for ZRange {
+  fn from(s: &'a str) -> Self {
+    ZRange {
+      kind:  ZRangeKind::default(),
+      range: s.into(),
+    }
+  }
+}
+
+impl From<String> for ZRange {
+  fn from(s: String) -> Self {
+    ZRange {
+      kind:  ZRangeKind::default(),
+      range: s.into(),
+    }
+  }
+}
+
+impl<'a> From<&'a String> for ZRange {
+  fn from(s: &'a String) -> Self {
+    ZRange {
+      kind:  ZRangeKind::default(),
+      range: s.as_str().into(),
+    }
+  }
+}
+
+impl TryFrom<f64> for ZRange {
+  type Error = RedisError;
+
+  fn try_from(f: f64) -> Result<Self, Self::Error> {
+    Ok(ZRange {
+      kind:  ZRangeKind::default(),
+      range: f.try_into()?,
+    })
+  }
+}
+
+impl<'a> From<&'a ZRange> for ZRange {
+  fn from(range: &'a ZRange) -> Self {
+    range.clone()
+  }
+}
+
\ No newline at end of file diff --git a/doc/src/fred/types/streams.rs.html b/doc/src/fred/types/streams.rs.html new file mode 100644 index 00000000..6ff82744 --- /dev/null +++ b/doc/src/fred/types/streams.rs.html @@ -0,0 +1,1021 @@ +streams.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+
use crate::{
+  commands::{MAXLEN, MINID},
+  error::{RedisError, RedisErrorKind},
+  types::{LimitCount, RedisKey, RedisValue, StringOrNumber},
+  utils,
+};
+use bytes_utils::Str;
+use std::{
+  collections::{HashMap, VecDeque},
+  convert::{TryFrom, TryInto},
+};
+
+/// Representation for the "=" or "~" operator in `XADD`, etc.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum XCapTrim {
+  Exact,
+  AlmostExact,
+}
+
+impl XCapTrim {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      XCapTrim::Exact => "=",
+      XCapTrim::AlmostExact => "~",
+    })
+  }
+}
+
+impl<'a> TryFrom<&'a str> for XCapTrim {
+  type Error = RedisError;
+
+  fn try_from(s: &'a str) -> Result<Self, Self::Error> {
+    Ok(match s {
+      "=" => XCapTrim::Exact,
+      "~" => XCapTrim::AlmostExact,
+      _ => {
+        return Err(RedisError::new(
+          RedisErrorKind::InvalidArgument,
+          "Invalid XADD trim value.",
+        ))
+      },
+    })
+  }
+}
+
+/// One or more ordered key-value pairs, typically used as an argument for `XADD`.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct MultipleOrderedPairs {
+  values: Vec<(RedisKey, RedisValue)>,
+}
+
+impl MultipleOrderedPairs {
+  pub fn len(&self) -> usize {
+    self.values.len()
+  }
+
+  pub fn inner(self) -> Vec<(RedisKey, RedisValue)> {
+    self.values
+  }
+}
+
+impl From<()> for MultipleOrderedPairs {
+  fn from(_: ()) -> Self {
+    MultipleOrderedPairs { values: Vec::new() }
+  }
+}
+
+impl<K, V> TryFrom<(K, V)> for MultipleOrderedPairs
+where
+  K: Into<RedisKey>,
+  V: TryInto<RedisValue>,
+  V::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from((key, value): (K, V)) -> Result<Self, Self::Error> {
+    Ok(MultipleOrderedPairs {
+      values: vec![(key.into(), to!(value)?)],
+    })
+  }
+}
+
+impl<K, V> TryFrom<Vec<(K, V)>> for MultipleOrderedPairs
+where
+  K: Into<RedisKey>,
+  V: TryInto<RedisValue>,
+  V::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(values: Vec<(K, V)>) -> Result<Self, Self::Error> {
+    Ok(MultipleOrderedPairs {
+      values: values
+        .into_iter()
+        .map(|(key, value)| Ok((key.into(), to!(value)?)))
+        .collect::<Result<Vec<(RedisKey, RedisValue)>, RedisError>>()?,
+    })
+  }
+}
+
+impl<K, V> TryFrom<VecDeque<(K, V)>> for MultipleOrderedPairs
+where
+  K: Into<RedisKey>,
+  V: TryInto<RedisValue>,
+  V::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(values: VecDeque<(K, V)>) -> Result<Self, Self::Error> {
+    Ok(MultipleOrderedPairs {
+      values: values
+        .into_iter()
+        .map(|(key, value)| Ok((key.into(), to!(value)?)))
+        .collect::<Result<Vec<(RedisKey, RedisValue)>, RedisError>>()?,
+    })
+  }
+}
+
+impl<K, V> TryFrom<HashMap<K, V>> for MultipleOrderedPairs
+where
+  K: Into<RedisKey>,
+  V: TryInto<RedisValue>,
+  V::Error: Into<RedisError>,
+{
+  type Error = RedisError;
+
+  fn try_from(values: HashMap<K, V>) -> Result<Self, Self::Error> {
+    Ok(MultipleOrderedPairs {
+      values: values
+        .into_iter()
+        .map(|(key, value)| Ok((key.into(), to!(value)?)))
+        .collect::<Result<Vec<(RedisKey, RedisValue)>, RedisError>>()?,
+    })
+  }
+}
+
+/// One or more IDs for elements in a stream.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct MultipleIDs {
+  inner: Vec<XID>,
+}
+
+impl MultipleIDs {
+  pub fn len(&self) -> usize {
+    self.inner.len()
+  }
+
+  pub fn inner(self) -> Vec<XID> {
+    self.inner
+  }
+}
+
+impl<T> From<T> for MultipleIDs
+where
+  T: Into<XID>,
+{
+  fn from(value: T) -> Self {
+    MultipleIDs {
+      inner: vec![value.into()],
+    }
+  }
+}
+
+impl<T> From<Vec<T>> for MultipleIDs
+where
+  T: Into<XID>,
+{
+  fn from(value: Vec<T>) -> Self {
+    MultipleIDs {
+      inner: value.into_iter().map(|value| value.into()).collect(),
+    }
+  }
+}
+
+impl<T> From<VecDeque<T>> for MultipleIDs
+where
+  T: Into<XID>,
+{
+  fn from(value: VecDeque<T>) -> Self {
+    MultipleIDs {
+      inner: value.into_iter().map(|value| value.into()).collect(),
+    }
+  }
+}
+
+/// The MAXLEN or MINID argument for a stream cap.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum XCapKind {
+  MaxLen,
+  MinID,
+}
+
+impl XCapKind {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      XCapKind::MaxLen => MAXLEN,
+      XCapKind::MinID => MINID,
+    })
+  }
+}
+
+impl<'a> TryFrom<&'a str> for XCapKind {
+  type Error = RedisError;
+
+  fn try_from(value: &'a str) -> Result<Self, Self::Error> {
+    Ok(match value {
+      "MAXLEN" => XCapKind::MaxLen,
+      "MINID" => XCapKind::MinID,
+      _ => {
+        return Err(RedisError::new(
+          RedisErrorKind::InvalidArgument,
+          "Expected MAXLEN or MINID,",
+        ))
+      },
+    })
+  }
+}
+
+/// Stream cap arguments for `XADD`, `XTRIM`, etc.
+///
+/// Equivalent to `[MAXLEN|MINID [=|~] threshold [LIMIT count]]`.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct XCap {
+  inner: Option<(XCapKind, XCapTrim, StringOrNumber, LimitCount)>,
+}
+
+impl XCap {
+  pub(crate) fn into_parts(self) -> Option<(XCapKind, XCapTrim, StringOrNumber, LimitCount)> {
+    self.inner
+  }
+}
+
+impl From<Option<()>> for XCap {
+  fn from(_: Option<()>) -> Self {
+    XCap { inner: None }
+  }
+}
+
+impl<K, T, S> TryFrom<(K, T, S, Option<i64>)> for XCap
+where
+  K: TryInto<XCapKind>,
+  K::Error: Into<RedisError>,
+  T: TryInto<XCapTrim>,
+  T::Error: Into<RedisError>,
+  S: Into<StringOrNumber>,
+{
+  type Error = RedisError;
+
+  fn try_from((kind, trim, threshold, limit): (K, T, S, Option<i64>)) -> Result<Self, Self::Error> {
+    let (kind, trim) = (to!(kind)?, to!(trim)?);
+    Ok(XCap {
+      inner: Some((kind, trim, threshold.into(), limit)),
+    })
+  }
+}
+
+impl<K, T, S> TryFrom<(K, T, S)> for XCap
+where
+  K: TryInto<XCapKind>,
+  K::Error: Into<RedisError>,
+  T: TryInto<XCapTrim>,
+  T::Error: Into<RedisError>,
+  S: Into<StringOrNumber>,
+{
+  type Error = RedisError;
+
+  fn try_from((kind, trim, threshold): (K, T, S)) -> Result<Self, Self::Error> {
+    let (kind, trim) = (to!(kind)?, to!(trim)?);
+    Ok(XCap {
+      inner: Some((kind, trim, threshold.into(), None)),
+    })
+  }
+}
+
+impl<K, S> TryFrom<(K, S)> for XCap
+where
+  K: TryInto<XCapKind>,
+  K::Error: Into<RedisError>,
+  S: Into<StringOrNumber>,
+{
+  type Error = RedisError;
+
+  fn try_from((kind, threshold): (K, S)) -> Result<Self, Self::Error> {
+    let kind = to!(kind)?;
+    Ok(XCap {
+      inner: Some((kind, XCapTrim::Exact, threshold.into(), None)),
+    })
+  }
+}
+
+/// Stream ID arguments for `XADD`, `XREAD`, etc.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum XID {
+  /// The auto-generated key symbol "*".
+  Auto,
+  /// An ID specified by the user such as "12345-0".
+  Manual(Str),
+  /// The highest ID in a stream ("$").
+  Max,
+  /// For `XREADGROUP`, only return new IDs (">").
+  NewInGroup,
+}
+
+impl XID {
+  pub(crate) fn into_str(self) -> Str {
+    match self {
+      XID::Auto => utils::static_str("*"),
+      XID::Max => utils::static_str("$"),
+      XID::NewInGroup => utils::static_str(">"),
+      XID::Manual(s) => s,
+    }
+  }
+}
+
+impl<'a> From<&'a str> for XID {
+  fn from(value: &'a str) -> Self {
+    match value {
+      "*" => XID::Auto,
+      "$" => XID::Max,
+      ">" => XID::NewInGroup,
+      _ => XID::Manual(value.into()),
+    }
+  }
+}
+
+impl<'a> From<&'a String> for XID {
+  fn from(value: &'a String) -> Self {
+    match value.as_ref() {
+      "*" => XID::Auto,
+      "$" => XID::Max,
+      ">" => XID::NewInGroup,
+      _ => XID::Manual(value.into()),
+    }
+  }
+}
+
+impl From<String> for XID {
+  fn from(value: String) -> Self {
+    match value.as_ref() {
+      "*" => XID::Auto,
+      "$" => XID::Max,
+      ">" => XID::NewInGroup,
+      _ => XID::Manual(value.into()),
+    }
+  }
+}
+
+impl From<Str> for XID {
+  fn from(value: Str) -> Self {
+    match &*value {
+      "*" => XID::Auto,
+      "$" => XID::Max,
+      ">" => XID::NewInGroup,
+      _ => XID::Manual(value),
+    }
+  }
+}
+
+/// A struct representing the trailing optional arguments to [XPENDING](https://redis.io/commands/xpending).
+///
+/// See the `From` implementations for various shorthand representations of these arguments. Callers should use `()`
+/// to represent no arguments.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct XPendingArgs {
+  pub idle:     Option<u64>,
+  pub start:    Option<XID>,
+  pub end:      Option<XID>,
+  pub count:    Option<u64>,
+  pub consumer: Option<Str>,
+}
+
+impl XPendingArgs {
+  pub(crate) fn into_parts(self) -> Result<Option<(Option<u64>, XID, XID, u64, Option<Str>)>, RedisError> {
+    let is_empty = self.idle.is_none()
+      && self.start.is_none()
+      && self.end.is_none()
+      && self.count.is_none()
+      && self.consumer.is_none();
+
+    if is_empty {
+      Ok(None)
+    } else {
+      let start = match self.start {
+        Some(s) => s,
+        None => {
+          return Err(RedisError::new(
+            RedisErrorKind::InvalidArgument,
+            "The `start` argument is required in this context.",
+          ))
+        },
+      };
+      let end = match self.end {
+        Some(s) => s,
+        None => {
+          return Err(RedisError::new(
+            RedisErrorKind::InvalidArgument,
+            "The `end` argument is required in this context.",
+          ))
+        },
+      };
+      let count = match self.count {
+        Some(s) => s,
+        None => {
+          return Err(RedisError::new(
+            RedisErrorKind::InvalidArgument,
+            "The `count` argument is required in this context.",
+          ))
+        },
+      };
+
+      Ok(Some((self.idle, start, end, count, self.consumer)))
+    }
+  }
+}
+
+impl From<()> for XPendingArgs {
+  fn from(_: ()) -> Self {
+    XPendingArgs {
+      idle:     None,
+      start:    None,
+      end:      None,
+      count:    None,
+      consumer: None,
+    }
+  }
+}
+
+impl<S, E> From<(S, E, u64)> for XPendingArgs
+where
+  S: Into<XID>,
+  E: Into<XID>,
+{
+  fn from((start, end, count): (S, E, u64)) -> Self {
+    XPendingArgs {
+      idle:     None,
+      start:    Some(start.into()),
+      end:      Some(end.into()),
+      count:    Some(count),
+      consumer: None,
+    }
+  }
+}
+
+impl<S, E, C> From<(S, E, u64, C)> for XPendingArgs
+where
+  S: Into<XID>,
+  E: Into<XID>,
+  C: Into<Str>,
+{
+  fn from((start, end, count, consumer): (S, E, u64, C)) -> Self {
+    XPendingArgs {
+      idle:     None,
+      start:    Some(start.into()),
+      end:      Some(end.into()),
+      count:    Some(count),
+      consumer: Some(consumer.into()),
+    }
+  }
+}
+
+impl<S, E> From<(u64, S, E, u64)> for XPendingArgs
+where
+  S: Into<XID>,
+  E: Into<XID>,
+{
+  fn from((idle, start, end, count): (u64, S, E, u64)) -> Self {
+    XPendingArgs {
+      idle:     Some(idle),
+      start:    Some(start.into()),
+      end:      Some(end.into()),
+      count:    Some(count),
+      consumer: None,
+    }
+  }
+}
+
+impl<S, E, C> From<(u64, S, E, u64, C)> for XPendingArgs
+where
+  S: Into<XID>,
+  E: Into<XID>,
+  C: Into<Str>,
+{
+  fn from((idle, start, end, count, consumer): (u64, S, E, u64, C)) -> Self {
+    XPendingArgs {
+      idle:     Some(idle),
+      start:    Some(start.into()),
+      end:      Some(end.into()),
+      count:    Some(count),
+      consumer: Some(consumer.into()),
+    }
+  }
+}
+
+/// A generic helper type describing the ID and associated map for each record in a stream.
+///
+/// See the [XReadResponse](crate::types::XReadResponse) type for more information.
+pub type XReadValue<I, K, V> = (I, HashMap<K, V>);
+/// A generic helper type describing the top level response from `XREAD` or `XREADGROUP`.
+///
+/// See the [xread](crate::interfaces::StreamsInterface::xread) documentation for more information.
+///
+/// The inner type declarations refer to the following:
+/// * K1 - The type of the outer Redis key for the stream. Usually a `String` or `RedisKey`.
+/// * I - The type of the ID for a stream record ("abc-123"). This is usually a `String`.
+/// * K2 - The type of key in the map associated with each stream record.
+/// * V - The type of value in the map associated with each stream record.
+///
+/// To support heterogeneous values in the map describing each stream element it is recommended to declare the last
+/// type as `RedisValue` and [convert](crate::types::RedisValue::convert) as needed.
+pub type XReadResponse<K1, I, K2, V> = HashMap<K1, Vec<XReadValue<I, K2, V>>>;
+
\ No newline at end of file diff --git a/doc/src/fred/types/timeseries.rs.html b/doc/src/fred/types/timeseries.rs.html new file mode 100644 index 00000000..dcdc0c20 --- /dev/null +++ b/doc/src/fred/types/timeseries.rs.html @@ -0,0 +1,899 @@ +timeseries.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  types::RedisValue,
+  utils,
+};
+use bytes_utils::Str;
+use std::collections::HashMap;
+
+/// Encoding arguments for certain timeseries commands.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum Encoding {
+  Compressed,
+  Uncompressed,
+}
+
+impl Encoding {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      Encoding::Compressed => "COMPRESSED",
+      Encoding::Uncompressed => "UNCOMPRESSED",
+    })
+  }
+}
+
+/// The duplicate policy used with certain timeseries commands.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum DuplicatePolicy {
+  Block,
+  First,
+  Last,
+  Min,
+  Max,
+  Sum,
+}
+
+impl DuplicatePolicy {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      DuplicatePolicy::Block => "BLOCK",
+      DuplicatePolicy::First => "FIRST",
+      DuplicatePolicy::Last => "LAST",
+      DuplicatePolicy::Min => "MIN",
+      DuplicatePolicy::Max => "MAX",
+      DuplicatePolicy::Sum => "SUM",
+    })
+  }
+}
+
+/// A timestamp used in most timeseries commands.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum Timestamp {
+  /// Unix time (milliseconds since epoch).
+  Custom(i64),
+  /// The server's current time, equivalent to "*".
+  Now,
+}
+
+impl Default for Timestamp {
+  fn default() -> Self {
+    Timestamp::Now
+  }
+}
+
+impl Timestamp {
+  pub(crate) fn to_value(&self) -> RedisValue {
+    match *self {
+      Timestamp::Now => RedisValue::String(utils::static_str("*")),
+      Timestamp::Custom(v) => RedisValue::Integer(v),
+    }
+  }
+
+  pub(crate) fn from_str(value: &str) -> Result<Self, RedisError> {
+    match value {
+      "*" => Ok(Timestamp::Now),
+      _ => Ok(Timestamp::Custom(value.parse::<i64>()?)),
+    }
+  }
+}
+
+impl From<i64> for Timestamp {
+  fn from(value: i64) -> Self {
+    Timestamp::Custom(value)
+  }
+}
+
+impl TryFrom<&str> for Timestamp {
+  type Error = RedisError;
+
+  fn try_from(value: &str) -> Result<Self, Self::Error> {
+    Self::from_str(value)
+  }
+}
+
+impl TryFrom<Str> for Timestamp {
+  type Error = RedisError;
+
+  fn try_from(value: Str) -> Result<Self, Self::Error> {
+    Self::from_str(&value)
+  }
+}
+
+impl TryFrom<String> for Timestamp {
+  type Error = RedisError;
+
+  fn try_from(value: String) -> Result<Self, Self::Error> {
+    Self::from_str(&value)
+  }
+}
+
+/// An aggregation policy to use with certain timeseries commands.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum Aggregator {
+  Avg,
+  Sum,
+  Min,
+  Max,
+  Range,
+  Count,
+  First,
+  Last,
+  StdP,
+  StdS,
+  VarP,
+  VarS,
+  TWA,
+}
+
+impl Aggregator {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      Aggregator::Avg => "avg",
+      Aggregator::Sum => "sum",
+      Aggregator::Min => "min",
+      Aggregator::Max => "max",
+      Aggregator::Range => "range",
+      Aggregator::Count => "count",
+      Aggregator::First => "first",
+      Aggregator::Last => "last",
+      Aggregator::StdP => "std.p",
+      Aggregator::StdS => "std.s",
+      Aggregator::VarP => "var.p",
+      Aggregator::VarS => "var.s",
+      Aggregator::TWA => "twa",
+    })
+  }
+}
+
+/// Arguments equivalent to `WITHLABELS | SELECTED_LABELS label...` in various time series GET functions.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum GetLabels {
+  WithLabels,
+  SelectedLabels(Vec<Str>),
+}
+
+impl GetLabels {
+  pub(crate) fn args_len(&self) -> usize {
+    match *self {
+      GetLabels::WithLabels => 1,
+      GetLabels::SelectedLabels(ref s) => 1 + s.len(),
+    }
+  }
+}
+
+impl<S> FromIterator<S> for GetLabels
+where
+  S: Into<Str>,
+{
+  fn from_iter<I: IntoIterator<Item = S>>(iter: I) -> Self {
+    GetLabels::SelectedLabels(iter.into_iter().map(|v| v.into()).collect())
+  }
+}
+
+impl<S, const N: usize> From<[S; N]> for GetLabels
+where
+  S: Into<Str>,
+{
+  fn from(value: [S; N]) -> Self {
+    GetLabels::SelectedLabels(value.into_iter().map(|v| v.into()).collect())
+  }
+}
+
+impl<S> From<Vec<S>> for GetLabels
+where
+  S: Into<Str>,
+{
+  fn from(value: Vec<S>) -> Self {
+    GetLabels::SelectedLabels(value.into_iter().map(|v| v.into()).collect())
+  }
+}
+
+/// A timestamp query used in commands such as `TS.MRANGE`.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum GetTimestamp {
+  /// Equivalent to `-`.
+  Earliest,
+  /// Equivalent to `+`
+  Latest,
+  Custom(i64),
+}
+
+impl GetTimestamp {
+  pub(crate) fn to_value(&self) -> RedisValue {
+    match *self {
+      GetTimestamp::Earliest => static_val!("-"),
+      GetTimestamp::Latest => static_val!("+"),
+      GetTimestamp::Custom(i) => i.into(),
+    }
+  }
+}
+
+impl TryFrom<&str> for GetTimestamp {
+  type Error = RedisError;
+
+  fn try_from(value: &str) -> Result<Self, Self::Error> {
+    Ok(match value {
+      "-" => GetTimestamp::Earliest,
+      "+" => GetTimestamp::Latest,
+      _ => GetTimestamp::Custom(value.parse::<i64>()?),
+    })
+  }
+}
+
+impl From<i64> for GetTimestamp {
+  fn from(value: i64) -> Self {
+    GetTimestamp::Custom(value)
+  }
+}
+
+/// A struct representing `[ALIGN align] AGGREGATION aggregator bucketDuration [BUCKETTIMESTAMP bt] [EMPTY]` in
+/// commands such as `TS.MRANGE`.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct RangeAggregation {
+  pub align:            Option<GetTimestamp>,
+  pub aggregation:      Aggregator,
+  pub bucket_duration:  u64,
+  pub bucket_timestamp: Option<BucketTimestamp>,
+  pub empty:            bool,
+}
+
+impl From<(Aggregator, u64)> for RangeAggregation {
+  fn from((aggregation, duration): (Aggregator, u64)) -> Self {
+    RangeAggregation {
+      aggregation,
+      bucket_duration: duration,
+      align: None,
+      bucket_timestamp: None,
+      empty: false,
+    }
+  }
+}
+
+/// A `REDUCER` argument in commands such as `TS.MRANGE`.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum Reducer {
+  Avg,
+  Sum,
+  Min,
+  Max,
+  Range,
+  Count,
+  StdP,
+  StdS,
+  VarP,
+  VarS,
+}
+
+impl Reducer {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      Reducer::Avg => "avg",
+      Reducer::Sum => "sum",
+      Reducer::Min => "min",
+      Reducer::Max => "max",
+      Reducer::Range => "range",
+      Reducer::Count => "count",
+      Reducer::StdP => "std.p",
+      Reducer::StdS => "std.s",
+      Reducer::VarP => "var.p",
+      Reducer::VarS => "var.s",
+    })
+  }
+}
+
+/// A struct representing `GROUPBY label REDUCE reducer` in commands such as `TS.MRANGE`.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct GroupBy {
+  pub groupby: Str,
+  pub reduce:  Reducer,
+}
+
+impl<S: Into<Str>> From<(S, Reducer)> for GroupBy {
+  fn from((groupby, reduce): (S, Reducer)) -> Self {
+    GroupBy {
+      groupby: groupby.into(),
+      reduce,
+    }
+  }
+}
+
+/// A `BUCKETTIMESTAMP` argument in commands such as `TS.MRANGE`.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum BucketTimestamp {
+  Start,
+  End,
+  Mid,
+}
+
+impl TryFrom<&str> for BucketTimestamp {
+  type Error = RedisError;
+
+  fn try_from(value: &str) -> Result<Self, Self::Error> {
+    Ok(match value {
+      "-" | "start" => BucketTimestamp::Start,
+      "+" | "end" => BucketTimestamp::End,
+      "~" | "mid" => BucketTimestamp::Mid,
+      _ => {
+        return Err(RedisError::new(
+          RedisErrorKind::InvalidArgument,
+          "Invalid bucket timestamp.",
+        ))
+      },
+    })
+  }
+}
+
+impl BucketTimestamp {
+  pub(crate) fn to_str(&self) -> Str {
+    utils::static_str(match *self {
+      BucketTimestamp::Start => "-",
+      BucketTimestamp::End => "+",
+      BucketTimestamp::Mid => "~",
+    })
+  }
+}
+
+/// Shorthand for the result of commands such as `MGET`, `MRANGE`, etc.
+///
+/// * **K** - The key type, usually a `RedisKey`, `Str`, or `String`.
+/// * **Lk** - The label key type, usually a `Str` or `String`.
+/// * **Lv** - The label value type, often some kind of string type.
+///
+/// The fastest/cheapest option is usually `TimeseriesValues<RedisKey, Str, Str>`.
+///
+/// ```rust
+/// # use fred::prelude::*;
+/// # use tokio::time::sleep;
+/// # use std::time::Duration;
+/// # use bytes_utils::Str;
+/// # use fred::types::{RespVersion, GetLabels, Resp2TimeSeriesValues};
+/// async fn example(client: &RedisClient) -> Result<(), RedisError> {
+///   assert_eq!(client.protocol_version(), RespVersion::RESP2);
+///
+///   client
+///     .ts_add("foo", "*", 1.1, None, None, None, None, ("a", "b"))
+///     .await?;
+///   sleep(Duration::from_millis(5)).await;
+///   client
+///     .ts_add("foo", "*", 2.2, None, None, None, None, ("a", "b"))
+///     .await?;
+///   sleep(Duration::from_millis(5)).await;
+///   client
+///     .ts_add("bar", "*", 3.3, None, None, None, None, ("a", "b"))
+///     .await?;
+///   sleep(Duration::from_millis(5)).await;
+///   client
+///     .ts_add("bar", "*", 4.4, None, None, None, None, ("a", "b"))
+///     .await?;
+///
+///   let ranges: Resp2TimeSeriesValues<RedisKey, Str, Str> = client
+///     .ts_mrange(
+///       "-",
+///       "+",
+///       true,
+///       [],
+///       None,
+///       Some(GetLabels::WithLabels),
+///       None,
+///       None,
+///       ["a=b"],
+///       None,
+///     )
+///     .await?;
+///
+///   for (key, labels, values) in ranges.into_iter() {
+///     println!("{} [{:?}] {:?}", key.as_str_lossy(), labels, values);
+///   }
+///   // bar [[("a", "b")]] [(1705355605510, 3.3), (1705355605517, 4.4)]
+///   // foo [[("a", "b")]] [(1705355605498, 1.1), (1705355605504, 2.2)]
+///   Ok(())
+/// }
+/// ```
+///
+/// See [Resp3TimeSeriesValues](crate::types::Resp3TimeSeriesValues) for the RESP3 equivalent.
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+pub type Resp2TimeSeriesValues<K, Lk, Lv> = Vec<(K, Vec<(Lk, Lv)>, Vec<(i64, f64)>)>;
+
+/// The RESP3 equivalent of [Resp2TimeSeriesValues](crate::types::Resp2TimeSeriesValues).
+///
+/// The timeseries interface uses slightly different type signatures in RESP3 mode.
+///
+/// ```rust
+/// # use fred::prelude::*;
+/// # use tokio::time::sleep;
+/// # use std::time::Duration;
+/// # use bytes_utils::Str;
+/// # use fred::types::{RespVersion, GetLabels, Resp3TimeSeriesValues};
+/// async fn example(client: &RedisClient) -> Result<(), RedisError> {
+///   assert_eq!(client.protocol_version(), RespVersion::RESP3);
+///
+///   client
+///     .ts_add("foo", "*", 1.1, None, None, None, None, ("a", "b"))
+///     .await?;
+///   sleep(Duration::from_millis(5)).await;
+///   client
+///     .ts_add("foo", "*", 2.2, None, None, None, None, ("a", "b"))
+///     .await?;
+///   sleep(Duration::from_millis(5)).await;
+///   client
+///     .ts_add("bar", "*", 3.3, None, None, None, None, ("a", "b"))
+///     .await?;
+///   sleep(Duration::from_millis(5)).await;
+///   client
+///     .ts_add("bar", "*", 4.4, None, None, None, None, ("a", "b"))
+///     .await?;
+///
+///   let ranges: Resp3TimeSeriesValues<RedisKey, Str, Str> = client
+///     .ts_mget(false, Some(GetLabels::WithLabels), ["a=b"])
+///     .await?;
+///
+///   for (key, (labels, values)) in ranges.into_iter() {
+///     println!("{} [{:?}] {:?}", key.as_str_lossy(), labels, values);
+///   }
+///   // bar [[("a", "b")]] [(1705355605517, 4.4)]
+///   // foo [[("a", "b")]] [(1705355605504, 2.2)]
+///   Ok(())
+/// }
+/// ```
+#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))]
+pub type Resp3TimeSeriesValues<K, Lk, Lv> = HashMap<K, (Vec<(Lk, Lv)>, Vec<(i64, f64)>)>;
+
\ No newline at end of file diff --git a/doc/src/fred/utils.rs.html b/doc/src/fred/utils.rs.html new file mode 100644 index 00000000..d9e91171 --- /dev/null +++ b/doc/src/fred/utils.rs.html @@ -0,0 +1,1943 @@ +utils.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+
use crate::{
+  error::{RedisError, RedisErrorKind},
+  interfaces::ClientLike,
+  modules::inner::{CommandSender, RedisClientInner},
+  protocol::{
+    command::{RedisCommand, RedisCommandKind},
+    responders::ResponseKind,
+    utils as protocol_utils,
+  },
+  runtime::{
+    broadcast_channel,
+    oneshot_channel,
+    sleep,
+    unbounded_channel,
+    AtomicBool,
+    AtomicUsize,
+    BroadcastSender,
+    RefCount,
+    RefSwap,
+    RwLock,
+  },
+  types::*,
+};
+use bytes::Bytes;
+use bytes_utils::Str;
+use float_cmp::approx_eq;
+use futures::{
+  future::{select, Either},
+  pin_mut,
+  Future,
+  TryFutureExt,
+};
+use rand::{self, distributions::Alphanumeric, Rng};
+use redis_protocol::resp3::types::BytesFrame as Resp3Frame;
+use std::{collections::HashMap, convert::TryInto, f64, sync::atomic::Ordering, time::Duration};
+use url::Url;
+use urlencoding::decode as percent_decode;
+
+#[cfg(any(
+  feature = "enable-native-tls",
+  feature = "enable-rustls",
+  feature = "enable-rustls-ring"
+))]
+use crate::protocol::tls::{TlsConfig, TlsConnector};
+#[cfg(feature = "transactions")]
+use crate::runtime::Mutex;
+#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))]
+use crate::trace;
+#[cfg(feature = "transactions")]
+use std::mem;
+#[cfg(feature = "unix-sockets")]
+use std::path::{Path, PathBuf};
+#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))]
+use tracing_futures::Instrument;
+
+const REDIS_TLS_SCHEME: &str = "rediss";
+const REDIS_CLUSTER_SCHEME_SUFFIX: &str = "-cluster";
+const REDIS_SENTINEL_SCHEME_SUFFIX: &str = "-sentinel";
+const SENTINEL_NAME_QUERY: &str = "sentinelServiceName";
+const CLUSTER_NODE_QUERY: &str = "node";
+#[cfg(feature = "sentinel-auth")]
+const SENTINEL_USERNAME_QUERY: &str = "sentinelUsername";
+#[cfg(feature = "sentinel-auth")]
+const SENTINEL_PASSWORD_QUERY: &str = "sentinelPassword";
+
+/// Create a `Str` from a static str slice without copying.
+pub const fn static_str(s: &'static str) -> Str {
+  // it's already parsed as a string
+  unsafe { Str::from_inner_unchecked(Bytes::from_static(s.as_bytes())) }
+}
+
+/// Create a `Bytes` from static bytes without copying.
+pub fn static_bytes(b: &'static [u8]) -> Bytes {
+  Bytes::from_static(b)
+}
+
+pub fn f64_eq(lhs: f64, rhs: f64) -> bool {
+  approx_eq!(f64, lhs, rhs, ulps = 2)
+}
+
+#[cfg(feature = "i-geo")]
+pub fn f64_opt_eq(lhs: &Option<f64>, rhs: &Option<f64>) -> bool {
+  match *lhs {
+    Some(lhs) => match *rhs {
+      Some(rhs) => f64_eq(lhs, rhs),
+      None => false,
+    },
+    None => rhs.is_none(),
+  }
+}
+
+/// Convert a redis string to an `f64`, supporting "+inf" and "-inf".
+pub fn redis_string_to_f64(s: &str) -> Result<f64, RedisError> {
+  // this is changing in newer versions of redis to lose the "+" prefix
+  if s == "+inf" || s == "inf" {
+    Ok(f64::INFINITY)
+  } else if s == "-inf" {
+    Ok(f64::NEG_INFINITY)
+  } else {
+    s.parse::<f64>().map_err(|_| {
+      RedisError::new(
+        RedisErrorKind::Unknown,
+        format!("Could not convert {} to floating point value.", s),
+      )
+    })
+  }
+}
+
+/// Convert an `f64` to a redis string, supporting "+inf" and "-inf".
+pub fn f64_to_redis_string(d: f64) -> Result<RedisValue, RedisError> {
+  if d.is_infinite() && d.is_sign_negative() {
+    Ok(RedisValue::from_static_str("-inf"))
+  } else if d.is_infinite() {
+    Ok(RedisValue::from_static_str("+inf"))
+  } else if d.is_nan() {
+    Err(RedisError::new(
+      RedisErrorKind::InvalidArgument,
+      "Cannot convert NaN to redis value.",
+    ))
+  } else {
+    Ok(d.to_string().into())
+  }
+}
+
+#[cfg(feature = "i-sorted-sets")]
+pub fn f64_to_zrange_bound(d: f64, kind: &ZRangeKind) -> Result<String, RedisError> {
+  if d.is_infinite() && d.is_sign_negative() {
+    Ok("-inf".into())
+  } else if d.is_infinite() {
+    Ok("+inf".into())
+  } else if d.is_nan() {
+    Err(RedisError::new(
+      RedisErrorKind::InvalidArgument,
+      "Cannot convert NaN to redis value.",
+    ))
+  } else {
+    Ok(match kind {
+      ZRangeKind::Inclusive => d.to_string(),
+      ZRangeKind::Exclusive => format!("({}", d),
+    })
+  }
+}
+
+pub fn incr_with_max(curr: u32, max: u32) -> Option<u32> {
+  if max != 0 && curr >= max {
+    None
+  } else {
+    Some(curr.saturating_add(1))
+  }
+}
+
+pub fn random_string(len: usize) -> String {
+  rand::thread_rng()
+    .sample_iter(&Alphanumeric)
+    .take(len)
+    .map(char::from)
+    .collect()
+}
+
+#[cfg(feature = "i-memory")]
+pub fn convert_or_default<R>(value: RedisValue) -> R
+where
+  R: FromRedis + Default,
+{
+  value.convert().ok().unwrap_or_default()
+}
+
+#[cfg(feature = "transactions")]
+pub fn random_u64(max: u64) -> u64 {
+  rand::thread_rng().gen_range(0 .. max)
+}
+
+pub fn set_client_state(state: &RwLock<ClientState>, new_state: ClientState) {
+  let mut state_guard = state.write();
+  *state_guard = new_state;
+}
+
+pub fn check_and_set_client_state(
+  state: &RwLock<ClientState>,
+  expected: ClientState,
+  new_state: ClientState,
+) -> bool {
+  let mut state_guard = state.write();
+
+  if *state_guard != expected {
+    false
+  } else {
+    *state_guard = new_state;
+    true
+  }
+}
+
+pub fn read_bool_atomic(val: &AtomicBool) -> bool {
+  val.load(Ordering::Acquire)
+}
+
+pub fn set_bool_atomic(val: &AtomicBool, new: bool) -> bool {
+  val.swap(new, Ordering::SeqCst)
+}
+
+pub fn decr_atomic(size: &AtomicUsize) -> usize {
+  size.fetch_sub(1, Ordering::AcqRel).saturating_sub(1)
+}
+
+pub fn incr_atomic(size: &AtomicUsize) -> usize {
+  size.fetch_add(1, Ordering::AcqRel).saturating_add(1)
+}
+
+pub fn read_atomic(size: &AtomicUsize) -> usize {
+  size.load(Ordering::Acquire)
+}
+
+pub fn set_atomic(size: &AtomicUsize, val: usize) -> usize {
+  size.swap(val, Ordering::SeqCst)
+}
+
+pub fn read_locked<T: Clone>(locked: &RwLock<T>) -> T {
+  locked.read().clone()
+}
+
+#[cfg(feature = "transactions")]
+pub fn read_mutex<T: Clone>(locked: &Mutex<T>) -> T {
+  locked.lock().clone()
+}
+
+#[cfg(feature = "transactions")]
+pub fn set_mutex<T>(locked: &Mutex<T>, value: T) -> T {
+  mem::replace(&mut *locked.lock(), value)
+}
+
+#[cfg(feature = "unix-sockets")]
+pub fn path_to_string(path: &Path) -> String {
+  path.as_os_str().to_string_lossy().to_string()
+}
+
+#[cfg(feature = "i-sorted-sets")]
+pub fn check_lex_str(val: String, kind: &ZRangeKind) -> String {
+  let formatted = val.starts_with('(') || val.starts_with('[') || val == "+" || val == "-";
+
+  if formatted {
+    val
+  } else if *kind == ZRangeKind::Exclusive {
+    format!("({}", val)
+  } else {
+    format!("[{}", val)
+  }
+}
+
+/// Parse the response from `FUNCTION LIST`.
+#[cfg(feature = "i-scripts")]
+fn parse_functions(value: &RedisValue) -> Result<Vec<Function>, RedisError> {
+  if let RedisValue::Array(functions) = value {
+    let mut out = Vec::with_capacity(functions.len());
+    for function_block in functions.iter() {
+      let functions: HashMap<Str, RedisValue> = function_block.clone().convert()?;
+      let name = match functions.get("name").and_then(|n| n.as_bytes_str()) {
+        Some(name) => name,
+        None => return Err(RedisError::new_parse("Missing function name.")),
+      };
+      let flags: Vec<FunctionFlag> = functions
+        .get("flags")
+        .and_then(|f| {
+          f.clone()
+            .into_array()
+            .into_iter()
+            .map(|v| FunctionFlag::from_str(v.as_str().unwrap_or_default().as_ref()))
+            .collect()
+        })
+        .unwrap_or_default();
+
+      out.push(Function { name, flags })
+    }
+
+    Ok(out)
+  } else {
+    Err(RedisError::new_parse("Invalid functions block."))
+  }
+}
+
+/// Check and parse the response to `FUNCTION LIST`.
+#[cfg(feature = "i-scripts")]
+pub fn value_to_functions(value: &RedisValue, name: &str) -> Result<Vec<Function>, RedisError> {
+  if let RedisValue::Array(ref libraries) = value {
+    for library in libraries.iter() {
+      let properties: HashMap<Str, RedisValue> = library.clone().convert()?;
+      let should_parse = properties
+        .get("library_name")
+        .and_then(|v| v.as_str())
+        .map(|s| s == name)
+        .unwrap_or(false);
+
+      if should_parse {
+        if let Some(functions) = properties.get("functions") {
+          return parse_functions(functions);
+        }
+      }
+    }
+
+    Err(RedisError::new_parse(format!("Missing library '{}'", name)))
+  } else {
+    Err(RedisError::new_parse("Expected array."))
+  }
+}
+
+pub async fn timeout<T, Fut, E>(ft: Fut, timeout: Duration) -> Result<T, RedisError>
+where
+  E: Into<RedisError>,
+  Fut: Future<Output = Result<T, E>>,
+{
+  if !timeout.is_zero() {
+    let sleep_ft = sleep(timeout);
+    pin_mut!(sleep_ft);
+    pin_mut!(ft);
+
+    trace!("Using timeout: {:?}", timeout);
+    match select(ft, sleep_ft).await {
+      Either::Left((lhs, _)) => lhs.map_err(|e| e.into()),
+      Either::Right((_, _)) => Err(RedisError::new(RedisErrorKind::Timeout, "Request timed out.")),
+    }
+  } else {
+    ft.await.map_err(|e| e.into())
+  }
+}
+
+/// Disconnect any state shared with the last router task spawned by the client.
+pub fn reset_router_task(inner: &RefCount<RedisClientInner>) {
+  let _guard = inner._lock.lock();
+
+  if !inner.has_command_rx() {
+    _trace!(inner, "Resetting command channel before connecting.");
+    // another connection task is running. this will let the command channel drain, then it'll drop everything on
+    // the old connection/router interface.
+    let (tx, rx) = unbounded_channel();
+    #[cfg(feature = "glommio")]
+    let tx = tx.into();
+
+    let old_command_tx = inner.swap_command_tx(tx);
+    inner.store_command_rx(rx, true);
+    close_router_channel(inner, old_command_tx);
+  }
+}
+
+/// Whether the router should check and interrupt the blocked command.
+async fn should_enforce_blocking_policy(inner: &RefCount<RedisClientInner>, command: &RedisCommand) -> bool {
+  if command.kind.closes_connection() {
+    return false;
+  }
+  if matches!(inner.config.blocking, Blocking::Error | Blocking::Interrupt) {
+    inner.backchannel.write().await.is_blocked()
+  } else {
+    false
+  }
+}
+
+/// Interrupt the currently blocked connection (if found) with the provided flag.
+pub async fn interrupt_blocked_connection(
+  inner: &RefCount<RedisClientInner>,
+  flag: ClientUnblockFlag,
+) -> Result<(), RedisError> {
+  let connection_id = {
+    let backchannel = inner.backchannel.write().await;
+    let server = match backchannel.blocked_server() {
+      Some(server) => server,
+      None => return Err(RedisError::new(RedisErrorKind::Unknown, "Connection is not blocked.")),
+    };
+    let id = match backchannel.connection_id(&server) {
+      Some(id) => id,
+      None => {
+        return Err(RedisError::new(
+          RedisErrorKind::Unknown,
+          "Failed to read connection ID.",
+        ))
+      },
+    };
+
+    _debug!(inner, "Sending CLIENT UNBLOCK to {}, ID: {}", server, id);
+    id
+  };
+
+  let command = RedisCommand::new(RedisCommandKind::ClientUnblock, vec![
+    connection_id.into(),
+    flag.to_str().into(),
+  ]);
+  let frame = backchannel_request_response(inner, command, true).await?;
+  protocol_utils::frame_to_results(frame).map(|_| ())
+}
+
+/// Check the status of the connection (usually before sending a command) to determine whether the connection should
+/// be unblocked automatically.
+async fn check_blocking_policy(inner: &RefCount<RedisClientInner>, command: &RedisCommand) -> Result<(), RedisError> {
+  if should_enforce_blocking_policy(inner, command).await {
+    _debug!(
+      inner,
+      "Checking to enforce blocking policy for {}",
+      command.kind.to_str_debug()
+    );
+
+    if inner.config.blocking == Blocking::Error {
+      return Err(RedisError::new(
+        RedisErrorKind::InvalidCommand,
+        "Error sending command while connection is blocked.",
+      ));
+    } else if inner.config.blocking == Blocking::Interrupt {
+      if let Err(e) = interrupt_blocked_connection(inner, ClientUnblockFlag::Error).await {
+        _error!(inner, "Failed to interrupt blocked connection: {:?}", e);
+      }
+    }
+  }
+
+  Ok(())
+}
+
+/// Prepare the command options, returning the timeout duration to apply.
+pub fn prepare_command<C: ClientLike>(client: &C, command: &mut RedisCommand) -> Duration {
+  client.change_command(command);
+  command.inherit_options(client.inner());
+  command
+    .timeout_dur
+    .unwrap_or_else(|| client.inner().default_command_timeout())
+}
+
+/// Send a command to the server using the default response handler.
+pub async fn basic_request_response<C, F, R>(client: &C, func: F) -> Result<Resp3Frame, RedisError>
+where
+  C: ClientLike,
+  R: Into<RedisCommand>,
+  F: FnOnce() -> Result<R, RedisError>,
+{
+  let inner = client.inner();
+  let mut command: RedisCommand = func()?.into();
+  let (tx, rx) = oneshot_channel();
+  command.response = ResponseKind::Respond(Some(tx));
+
+  let timed_out = command.timed_out.clone();
+  let timeout_dur = prepare_command(client, &mut command);
+  check_blocking_policy(inner, &command).await?;
+  client.send_command(command)?;
+
+  timeout(rx, timeout_dur)
+    .and_then(|r| async { r })
+    .map_err(move |error| {
+      set_bool_atomic(&timed_out, true);
+      error
+    })
+    .await
+}
+
+/// Send a command to the server, with tracing.
+#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))]
+#[allow(clippy::needless_borrows_for_generic_args)]
+// despite what clippy says, this^ actually matters for tracing `record` calls (at least it seems where `V: Copy`)
+pub async fn request_response<C, F, R>(client: &C, func: F) -> Result<Resp3Frame, RedisError>
+where
+  C: ClientLike,
+  R: Into<RedisCommand>,
+  F: FnOnce() -> Result<R, RedisError>,
+{
+  let inner = client.inner();
+  if !inner.should_trace() {
+    return basic_request_response(client, func).await;
+  }
+
+  let cmd_span = trace::create_command_span(inner);
+  let end_cmd_span = cmd_span.clone();
+
+  let (mut command, rx, req_size) = {
+    let args_span = trace::create_args_span(cmd_span.id(), inner);
+    #[allow(clippy::let_unit_value)]
+    let _guard = args_span.enter();
+    let (tx, rx) = oneshot_channel();
+
+    let mut command: RedisCommand = func()?.into();
+    command.response = ResponseKind::Respond(Some(tx));
+
+    let req_size = protocol_utils::args_size(command.args());
+    args_span.record("num_args", &command.args().len());
+    (command, rx, req_size)
+  };
+  cmd_span.record("cmd.name", &command.kind.to_str_debug());
+  cmd_span.record("cmd.req", &req_size);
+
+  let queued_span = trace::create_queued_span(cmd_span.id(), inner);
+  let timed_out = command.timed_out.clone();
+  _trace!(
+    inner,
+    "Setting command trace ID: {:?} for {} ({})",
+    cmd_span.id(),
+    command.kind.to_str_debug(),
+    command.debug_id()
+  );
+  command.traces.cmd = Some(cmd_span.clone());
+  command.traces.queued = Some(queued_span);
+
+  let timeout_dur = prepare_command(client, &mut command);
+  check_blocking_policy(inner, &command).await?;
+  client.send_command(command)?;
+
+  timeout(rx, timeout_dur)
+    .and_then(|r| async { r })
+    .map_err(move |error| {
+      set_bool_atomic(&timed_out, true);
+      error
+    })
+    .and_then(|frame| async move {
+      trace::record_response_size(&end_cmd_span, &frame);
+      Ok::<_, RedisError>(frame)
+    })
+    .instrument(cmd_span)
+    .await
+}
+
+#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))]
+pub async fn request_response<C, F, R>(client: &C, func: F) -> Result<Resp3Frame, RedisError>
+where
+  C: ClientLike,
+  R: Into<RedisCommand>,
+  F: FnOnce() -> Result<R, RedisError>,
+{
+  basic_request_response(client, func).await
+}
+
+/// Send a command on the backchannel connection.
+///
+/// A new connection may be created.
+pub async fn backchannel_request_response(
+  inner: &RefCount<RedisClientInner>,
+  command: RedisCommand,
+  use_blocked: bool,
+) -> Result<Resp3Frame, RedisError> {
+  let mut backchannel = inner.backchannel.write().await;
+  let server = backchannel.find_server(inner, &command, use_blocked)?;
+  backchannel.request_response(inner, &server, command).await
+}
+
+/// Check for a scan pattern without a hash tag, or with a wildcard in the hash tag.
+///
+/// These patterns will result in scanning a random node if used against a clustered redis.
+pub fn clustered_scan_pattern_has_hash_tag(inner: &RefCount<RedisClientInner>, pattern: &str) -> bool {
+  let (mut i, mut j, mut has_wildcard) = (None, None, false);
+  for (idx, c) in pattern.chars().enumerate() {
+    if c == '{' && i.is_none() {
+      i = Some(idx);
+    }
+    if c == '}' && j.is_none() && i.is_some() {
+      j = Some(idx);
+      break;
+    }
+    if c == '*' && i.is_some() {
+      has_wildcard = true;
+    }
+  }
+
+  if i.is_none() || j.is_none() {
+    return false;
+  }
+
+  if has_wildcard {
+    _warn!(
+      inner,
+      "Found wildcard in scan pattern hash tag. You may not be scanning the correct node."
+    );
+  }
+
+  true
+}
+
+/// A generic TryInto wrapper to work with the Infallible error type in the blanket From implementation.
+pub fn try_into<S, D>(val: S) -> Result<D, RedisError>
+where
+  S: TryInto<D>,
+  S::Error: Into<RedisError>,
+{
+  val.try_into().map_err(|e| e.into())
+}
+
+pub fn try_into_vec<S>(values: Vec<S>) -> Result<Vec<RedisValue>, RedisError>
+where
+  S: TryInto<RedisValue>,
+  S::Error: Into<RedisError>,
+{
+  let mut out = Vec::with_capacity(values.len());
+  for value in values.into_iter() {
+    out.push(try_into(value)?);
+  }
+
+  Ok(out)
+}
+
+pub fn add_jitter(delay: u64, jitter: u32) -> u64 {
+  if jitter == 0 {
+    delay
+  } else {
+    delay.saturating_add(rand::thread_rng().gen_range(0 .. jitter as u64))
+  }
+}
+
+pub fn into_redis_map<I, K, V>(mut iter: I) -> Result<HashMap<RedisKey, RedisValue>, RedisError>
+where
+  I: Iterator<Item = (K, V)>,
+  K: TryInto<RedisKey>,
+  K::Error: Into<RedisError>,
+  V: TryInto<RedisValue>,
+  V::Error: Into<RedisError>,
+{
+  let (lower, upper) = iter.size_hint();
+  let capacity = if let Some(upper) = upper { upper } else { lower };
+  let mut out = HashMap::with_capacity(capacity);
+
+  while let Some((key, value)) = iter.next() {
+    out.insert(to!(key)?, to!(value)?);
+  }
+  Ok(out)
+}
+
+pub fn flatten_nested_array_values(value: RedisValue, depth: usize) -> RedisValue {
+  if depth == 0 {
+    return value;
+  }
+
+  match value {
+    RedisValue::Array(values) => {
+      let inner_size = values.iter().fold(0, |s, v| s + v.array_len().unwrap_or(1));
+      let mut out = Vec::with_capacity(inner_size);
+
+      for value in values.into_iter() {
+        match value {
+          RedisValue::Array(inner) => {
+            for value in inner.into_iter() {
+              out.push(flatten_nested_array_values(value, depth - 1));
+            }
+          },
+          _ => out.push(value),
+        }
+      }
+      RedisValue::Array(out)
+    },
+    RedisValue::Map(values) => {
+      let mut out = HashMap::with_capacity(values.len());
+
+      for (key, value) in values.inner().into_iter() {
+        let value = if value.is_array() {
+          flatten_nested_array_values(value, depth - 1)
+        } else {
+          value
+        };
+
+        out.insert(key, value);
+      }
+      RedisValue::Map(RedisMap { inner: out })
+    },
+    _ => value,
+  }
+}
+
+pub fn is_maybe_array_map(arr: &[RedisValue]) -> bool {
+  if !arr.is_empty() && arr.len() % 2 == 0 {
+    arr.chunks(2).all(|chunk| !chunk[0].is_aggregate_type())
+  } else {
+    false
+  }
+}
+
+#[cfg(any(
+  feature = "enable-native-tls",
+  feature = "enable-rustls",
+  feature = "enable-rustls-ring"
+))]
+pub fn check_tls_features() {}
+
+#[cfg(not(any(
+  feature = "enable-native-tls",
+  feature = "enable-rustls",
+  feature = "enable-rustls-ring"
+)))]
+pub fn check_tls_features() {
+  warn!("TLS features are not enabled, but a TLS feature may have been used.");
+}
+
+#[cfg(all(
+  feature = "enable-native-tls",
+  not(any(feature = "enable-rustls", feature = "enable-rustls-ring"))
+))]
+pub fn tls_config_from_url(tls: bool) -> Result<Option<TlsConfig>, RedisError> {
+  if tls {
+    TlsConnector::default_native_tls().map(|c| Some(c.into()))
+  } else {
+    Ok(None)
+  }
+}
+
+#[cfg(all(
+  any(feature = "enable-rustls", feature = "enable-rustls-ring"),
+  not(feature = "enable-native-tls")
+))]
+pub fn tls_config_from_url(tls: bool) -> Result<Option<TlsConfig>, RedisError> {
+  if tls {
+    TlsConnector::default_rustls().map(|c| Some(c.into()))
+  } else {
+    Ok(None)
+  }
+}
+
+#[cfg(all(
+  feature = "enable-native-tls",
+  any(feature = "enable-rustls", feature = "enable-rustls-ring")
+))]
+pub fn tls_config_from_url(tls: bool) -> Result<Option<TlsConfig>, RedisError> {
+  // default to native-tls when both are enabled
+  if tls {
+    TlsConnector::default_native_tls().map(|c| Some(c.into()))
+  } else {
+    Ok(None)
+  }
+}
+
+pub fn swap_new_broadcast_channel<T: Clone>(old: &RefSwap<RefCount<BroadcastSender<T>>>, capacity: usize) {
+  let new = broadcast_channel(capacity).0;
+  old.swap(RefCount::new(new));
+}
+
+pub fn url_uses_tls(url: &Url) -> bool {
+  url.scheme().starts_with(REDIS_TLS_SCHEME)
+}
+
+pub fn url_is_clustered(url: &Url) -> bool {
+  url.scheme().ends_with(REDIS_CLUSTER_SCHEME_SUFFIX)
+}
+
+pub fn url_is_sentinel(url: &Url) -> bool {
+  url.scheme().ends_with(REDIS_SENTINEL_SCHEME_SUFFIX)
+}
+
+pub fn parse_url(url: &str, default_port: Option<u16>) -> Result<(Url, String, u16, bool), RedisError> {
+  let url = Url::parse(url)?;
+  let host = if let Some(host) = url.host_str() {
+    host.to_owned()
+  } else {
+    return Err(RedisError::new(RedisErrorKind::Config, "Invalid or missing host."));
+  };
+  let port = if let Some(port) = url.port().or(default_port) {
+    port
+  } else {
+    return Err(RedisError::new(RedisErrorKind::Config, "Invalid or missing port."));
+  };
+
+  let tls = url_uses_tls(&url);
+  if tls {
+    check_tls_features();
+  }
+
+  Ok((url, host, port, tls))
+}
+
+pub fn url_is_unix_socket(url: &Url) -> bool {
+  url.scheme() == "redis+unix"
+}
+
+#[cfg(feature = "unix-sockets")]
+pub fn parse_unix_url(url: &str) -> Result<(Url, PathBuf), RedisError> {
+  let url = Url::parse(url)?;
+  let path: PathBuf = url.path().into();
+  Ok((url, path))
+}
+
+pub fn parse_url_db(url: &Url) -> Result<Option<u8>, RedisError> {
+  let parts: Vec<&str> = if let Some(parts) = url.path_segments() {
+    parts.collect()
+  } else {
+    return Ok(None);
+  };
+
+  if parts.len() > 1 {
+    return Err(RedisError::new(RedisErrorKind::Config, "Invalid database path."));
+  } else if parts.is_empty() {
+    return Ok(None);
+  }
+  // handle empty paths with a / prefix
+  if parts[0].trim() == "" {
+    return Ok(None);
+  }
+
+  Ok(Some(parts[0].parse()?))
+}
+
+pub fn parse_url_credentials(url: &Url) -> Result<(Option<String>, Option<String>), RedisError> {
+  let username = if url.username().is_empty() {
+    None
+  } else {
+    let username = percent_decode(url.username())?;
+    Some(username.into_owned())
+  };
+  let password = percent_decode(url.password().unwrap_or_default())?;
+  let password = if password.is_empty() {
+    None
+  } else {
+    Some(password.into_owned())
+  };
+
+  Ok((username, password))
+}
+
+pub fn parse_url_other_nodes(url: &Url) -> Result<Vec<Server>, RedisError> {
+  let mut out = Vec::new();
+
+  for (key, value) in url.query_pairs().into_iter() {
+    if key == CLUSTER_NODE_QUERY {
+      let parts: Vec<&str> = value.split(':').collect();
+      if parts.len() != 2 {
+        return Err(RedisError::new(
+          RedisErrorKind::Config,
+          format!("Invalid host:port for cluster node: {}", value),
+        ));
+      }
+
+      let host = parts[0].to_owned();
+      let port = parts[1].parse::<u16>()?;
+      out.push(Server::new(host, port));
+    }
+  }
+
+  Ok(out)
+}
+
+pub fn parse_url_sentinel_service_name(url: &Url) -> Result<String, RedisError> {
+  for (key, value) in url.query_pairs().into_iter() {
+    if key == SENTINEL_NAME_QUERY {
+      return Ok(value.to_string());
+    }
+  }
+
+  Err(RedisError::new(
+    RedisErrorKind::Config,
+    "Invalid or missing sentinel service name query parameter.",
+  ))
+}
+
+#[cfg(feature = "sentinel-auth")]
+pub fn parse_url_sentinel_username(url: &Url) -> Option<String> {
+  url.query_pairs().find_map(|(key, value)| {
+    if key == SENTINEL_USERNAME_QUERY {
+      Some(value.to_string())
+    } else {
+      None
+    }
+  })
+}
+
+#[cfg(feature = "sentinel-auth")]
+pub fn parse_url_sentinel_password(url: &Url) -> Option<String> {
+  url.query_pairs().find_map(|(key, value)| {
+    if key == SENTINEL_PASSWORD_QUERY {
+      Some(value.to_string())
+    } else {
+      None
+    }
+  })
+}
+
+pub async fn clear_backchannel_state(inner: &RefCount<RedisClientInner>) {
+  inner.backchannel.write().await.clear_router_state(inner).await;
+}
+
+/// Send QUIT to the servers and clean up the old router task's state.
+fn close_router_channel(inner: &RefCount<RedisClientInner>, command_tx: RefCount<CommandSender>) {
+  inner.notifications.broadcast_close();
+  inner.reset_server_state();
+
+  let command = RedisCommand::new(RedisCommandKind::Quit, vec![]);
+  inner.counters.incr_cmd_buffer_len();
+  if let Err(_) = command_tx.send(command.into()) {
+    inner.counters.decr_cmd_buffer_len();
+    _warn!(inner, "Failed to send QUIT when dropping old command channel.");
+  }
+}
+
+#[cfg(test)]
+mod tests {
+  use super::*;
+  use crate::{error::RedisError, types::RedisValue};
+  use std::{convert::TryInto, fmt::Debug};
+
+  fn m<V>(v: V) -> RedisValue
+  where
+    V: TryInto<RedisValue> + Debug,
+    V::Error: Into<RedisError> + Debug,
+  {
+    v.try_into().unwrap()
+  }
+
+  fn a(v: Vec<RedisValue>) -> RedisValue {
+    RedisValue::Array(v)
+  }
+
+  #[test]
+  fn should_not_panic_with_zero_jitter() {
+    assert_eq!(add_jitter(10, 0), 10);
+  }
+
+  #[test]
+  fn should_flatten_xread_example() {
+    // 127.0.0.1:6379> xread count 2 streams foo bar 1643479648480-0 1643479834990-0
+    // 1) 1) "foo"
+    //    2) 1) 1) "1643479650336-0"
+    //          2) 1) "count"
+    //             2) "3"
+    // 2) 1) "bar"
+    //    2) 1) 1) "1643479837746-0"
+    //          2) 1) "count"
+    //             2) "5"
+    //       2) 1) "1643479925582-0"
+    //          2) 1) "count"
+    //             2) "6"
+    let actual: RedisValue = vec![
+      a(vec![
+        m("foo"),
+        a(vec![a(vec![m("1643479650336-0"), a(vec![m("count"), m(3)])])]),
+      ]),
+      a(vec![
+        m("bar"),
+        a(vec![
+          a(vec![m("1643479837746-0"), a(vec![m("count"), m(5)])]),
+          a(vec![m("1643479925582-0"), a(vec![m("count"), m(6)])]),
+        ]),
+      ]),
+    ]
+    .into_iter()
+    .collect();
+
+    // flatten the top level nested array into something that can be cast to a map
+    let expected: RedisValue = vec![
+      m("foo"),
+      a(vec![a(vec![m("1643479650336-0"), a(vec![m("count"), m(3)])])]),
+      m("bar"),
+      a(vec![
+        a(vec![m("1643479837746-0"), a(vec![m("count"), m(5)])]),
+        a(vec![m("1643479925582-0"), a(vec![m("count"), m(6)])]),
+      ]),
+    ]
+    .into_iter()
+    .collect();
+
+    assert_eq!(flatten_nested_array_values(actual, 1), expected);
+  }
+
+  #[test]
+  fn should_parse_url_credentials_no_creds() {
+    let url = Url::parse("redis://localhost:6379").unwrap();
+    let (username, password) = parse_url_credentials(&url).unwrap();
+
+    assert_eq!(username, None);
+    assert_eq!(password, None);
+  }
+
+  #[test]
+  fn should_parse_url_credentials_with_creds() {
+    let url = Url::parse("redis://default:abc123@localhost:6379").unwrap();
+    let (username, password) = parse_url_credentials(&url).unwrap();
+
+    assert_eq!(username.unwrap(), "default");
+    assert_eq!(password.unwrap(), "abc123");
+  }
+
+  #[test]
+  fn should_parse_url_credentials_with_percent_encoded_creds() {
+    let url = Url::parse("redis://default:abc%2F123@localhost:6379").unwrap();
+    let (username, password) = parse_url_credentials(&url).unwrap();
+
+    assert_eq!(username.unwrap(), "default");
+    assert_eq!(password.unwrap(), "abc/123");
+  }
+}
+
\ No newline at end of file diff --git a/doc/static.files/COPYRIGHT-23e9bde6c69aea69.txt b/doc/static.files/COPYRIGHT-23e9bde6c69aea69.txt new file mode 100644 index 00000000..1447df79 --- /dev/null +++ b/doc/static.files/COPYRIGHT-23e9bde6c69aea69.txt @@ -0,0 +1,50 @@ +# REUSE-IgnoreStart + +These documentation pages include resources by third parties. This copyright +file applies only to those resources. The following third party resources are +included, and carry their own copyright notices and license terms: + +* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2): + + Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ + with Reserved Font Name Fira Sans. + + Copyright (c) 2014, Telefonica S.A. + + Licensed under the SIL Open Font License, Version 1.1. + See FiraSans-LICENSE.txt. + +* rustdoc.css, main.js, and playpen.js: + + Copyright 2015 The Rust Developers. + Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or + the MIT license (LICENSE-MIT.txt) at your option. + +* normalize.css: + + Copyright (c) Nicolas Gallagher and Jonathan Neal. + Licensed under the MIT license (see LICENSE-MIT.txt). + +* Source Code Pro (SourceCodePro-Regular.ttf.woff2, + SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2): + + Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), + with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark + of Adobe Systems Incorporated in the United States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceCodePro-LICENSE.txt. + +* Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2, + SourceSerif4-It.ttf.woff2): + + Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name + 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United + States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceSerif4-LICENSE.md. + +This copyright file is intended to be distributed with rustdoc output. + +# REUSE-IgnoreEnd diff --git a/doc/static.files/FiraSans-LICENSE-db4b642586e02d97.txt b/doc/static.files/FiraSans-LICENSE-db4b642586e02d97.txt new file mode 100644 index 00000000..d7e9c149 --- /dev/null +++ b/doc/static.files/FiraSans-LICENSE-db4b642586e02d97.txt @@ -0,0 +1,98 @@ +// REUSE-IgnoreStart + +Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. +with Reserved Font Name < Fira >, + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/doc/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 b/doc/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 new file mode 100644 index 00000000..7a1e5fc5 Binary files /dev/null and b/doc/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 differ diff --git a/doc/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 b/doc/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 new file mode 100644 index 00000000..e766e06c Binary files /dev/null and b/doc/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 differ diff --git a/doc/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt b/doc/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt new file mode 100644 index 00000000..16fe87b0 --- /dev/null +++ b/doc/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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. diff --git a/doc/static.files/LICENSE-MIT-65090b722b3f6c56.txt b/doc/static.files/LICENSE-MIT-65090b722b3f6c56.txt new file mode 100644 index 00000000..31aa7938 --- /dev/null +++ b/doc/static.files/LICENSE-MIT-65090b722b3f6c56.txt @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/doc/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 b/doc/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 new file mode 100644 index 00000000..1866ad4b Binary files /dev/null and b/doc/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 differ diff --git a/doc/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt b/doc/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt new file mode 100644 index 00000000..4b3edc29 --- /dev/null +++ b/doc/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt @@ -0,0 +1,103 @@ +// REUSE-IgnoreStart + +Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/), + +with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, +NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, +Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco, +NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic, +Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/doc/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 b/doc/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 new file mode 100644 index 00000000..462c34ef Binary files /dev/null and b/doc/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 differ diff --git a/doc/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt b/doc/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt new file mode 100644 index 00000000..0d2941e1 --- /dev/null +++ b/doc/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt @@ -0,0 +1,97 @@ +// REUSE-IgnoreStart + +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/doc/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 b/doc/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 new file mode 100644 index 00000000..10b558e0 Binary files /dev/null and b/doc/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 differ diff --git a/doc/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 b/doc/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 new file mode 100644 index 00000000..5ec64eef Binary files /dev/null and b/doc/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 differ diff --git a/doc/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 b/doc/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 new file mode 100644 index 00000000..181a07f6 Binary files /dev/null and b/doc/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 differ diff --git a/doc/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 b/doc/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 new file mode 100644 index 00000000..2ae08a7b Binary files /dev/null and b/doc/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 differ diff --git a/doc/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md b/doc/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md new file mode 100644 index 00000000..175fa4f4 --- /dev/null +++ b/doc/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md @@ -0,0 +1,98 @@ + + +Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. +Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + + diff --git a/doc/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 b/doc/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 new file mode 100644 index 00000000..0263fc30 Binary files /dev/null and b/doc/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 differ diff --git a/doc/static.files/favicon-2c020d218678b618.svg b/doc/static.files/favicon-2c020d218678b618.svg new file mode 100644 index 00000000..8b34b511 --- /dev/null +++ b/doc/static.files/favicon-2c020d218678b618.svg @@ -0,0 +1,24 @@ + + + + + diff --git a/doc/static.files/favicon-32x32-422f7d1d52889060.png b/doc/static.files/favicon-32x32-422f7d1d52889060.png new file mode 100644 index 00000000..69b8613c Binary files /dev/null and b/doc/static.files/favicon-32x32-422f7d1d52889060.png differ diff --git a/doc/static.files/main-cb0df477c2d67d00.js b/doc/static.files/main-cb0df477c2d67d00.js new file mode 100644 index 00000000..6357eea3 --- /dev/null +++ b/doc/static.files/main-cb0df477c2d67d00.js @@ -0,0 +1,11 @@ +"use strict";window.RUSTDOC_TOOLTIP_HOVER_MS=300;window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS=450;function resourcePath(basename,extension){return getVar("root-path")+basename+getVar("resource-suffix")+extension}function hideMain(){addClass(document.getElementById(MAIN_ID),"hidden")}function showMain(){removeClass(document.getElementById(MAIN_ID),"hidden")}function blurHandler(event,parentElem,hideCallback){if(!parentElem.contains(document.activeElement)&&!parentElem.contains(event.relatedTarget)){hideCallback()}}window.rootPath=getVar("root-path");window.currentCrate=getVar("current-crate");function setMobileTopbar(){const mobileTopbar=document.querySelector(".mobile-topbar");const locationTitle=document.querySelector(".sidebar h2.location");if(mobileTopbar){const mobileTitle=document.createElement("h2");mobileTitle.className="location";if(hasClass(document.querySelector(".rustdoc"),"crate")){mobileTitle.innerHTML=`Crate ${window.currentCrate}`}else if(locationTitle){mobileTitle.innerHTML=locationTitle.innerHTML}mobileTopbar.appendChild(mobileTitle)}}function getVirtualKey(ev){if("key"in ev&&typeof ev.key!=="undefined"){return ev.key}const c=ev.charCode||ev.keyCode;if(c===27){return"Escape"}return String.fromCharCode(c)}const MAIN_ID="main-content";const SETTINGS_BUTTON_ID="settings-menu";const ALTERNATIVE_DISPLAY_ID="alternative-display";const NOT_DISPLAYED_ID="not-displayed";const HELP_BUTTON_ID="help-button";function getSettingsButton(){return document.getElementById(SETTINGS_BUTTON_ID)}function getHelpButton(){return document.getElementById(HELP_BUTTON_ID)}function getNakedUrl(){return window.location.href.split("?")[0].split("#")[0]}function insertAfter(newNode,referenceNode){referenceNode.parentNode.insertBefore(newNode,referenceNode.nextSibling)}function getOrCreateSection(id,classes){let el=document.getElementById(id);if(!el){el=document.createElement("section");el.id=id;el.className=classes;insertAfter(el,document.getElementById(MAIN_ID))}return el}function getAlternativeDisplayElem(){return getOrCreateSection(ALTERNATIVE_DISPLAY_ID,"content hidden")}function getNotDisplayedElem(){return getOrCreateSection(NOT_DISPLAYED_ID,"hidden")}function switchDisplayedElement(elemToDisplay){const el=getAlternativeDisplayElem();if(el.children.length>0){getNotDisplayedElem().appendChild(el.firstElementChild)}if(elemToDisplay===null){addClass(el,"hidden");showMain();return}el.appendChild(elemToDisplay);hideMain();removeClass(el,"hidden")}function browserSupportsHistoryApi(){return window.history&&typeof window.history.pushState==="function"}function preLoadCss(cssUrl){const link=document.createElement("link");link.href=cssUrl;link.rel="preload";link.as="style";document.getElementsByTagName("head")[0].appendChild(link)}(function(){const isHelpPage=window.location.pathname.endsWith("/help.html");function loadScript(url,errorCallback){const script=document.createElement("script");script.src=url;if(errorCallback!==undefined){script.onerror=errorCallback}document.head.append(script)}getSettingsButton().onclick=event=>{if(event.ctrlKey||event.altKey||event.metaKey){return}window.hideAllModals(false);addClass(getSettingsButton(),"rotate");event.preventDefault();loadScript(getVar("static-root-path")+getVar("settings-js"));setTimeout(()=>{const themes=getVar("themes").split(",");for(const theme of themes){if(theme!==""){preLoadCss(getVar("root-path")+theme+".css")}}},0)};window.searchState={loadingText:"Loading search results...",input:document.getElementsByClassName("search-input")[0],outputElement:()=>{let el=document.getElementById("search");if(!el){el=document.createElement("section");el.id="search";getNotDisplayedElem().appendChild(el)}return el},title:document.title,titleBeforeSearch:document.title,timeout:null,currentTab:0,focusedByTab:[null,null,null],clearInputTimeout:()=>{if(searchState.timeout!==null){clearTimeout(searchState.timeout);searchState.timeout=null}},isDisplayed:()=>searchState.outputElement().parentElement.id===ALTERNATIVE_DISPLAY_ID,focus:()=>{searchState.input.focus()},defocus:()=>{searchState.input.blur()},showResults:search=>{if(search===null||typeof search==="undefined"){search=searchState.outputElement()}switchDisplayedElement(search);searchState.mouseMovedAfterSearch=false;document.title=searchState.title},removeQueryParameters:()=>{document.title=searchState.titleBeforeSearch;if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.hash)}},hideResults:()=>{switchDisplayedElement(null);searchState.removeQueryParameters()},getQueryStringParams:()=>{const params={};window.location.search.substring(1).split("&").map(s=>{const pair=s.split("=").map(x=>x.replace(/\+/g," "));params[decodeURIComponent(pair[0])]=typeof pair[1]==="undefined"?null:decodeURIComponent(pair[1])});return params},setup:()=>{const search_input=searchState.input;if(!searchState.input){return}let searchLoaded=false;function sendSearchForm(){document.getElementsByClassName("search-form")[0].submit()}function loadSearch(){if(!searchLoaded){searchLoaded=true;loadScript(getVar("static-root-path")+getVar("search-js"),sendSearchForm);loadScript(resourcePath("search-index",".js"),sendSearchForm)}}search_input.addEventListener("focus",()=>{search_input.origPlaceholder=search_input.placeholder;search_input.placeholder="Type your search here.";loadSearch()});if(search_input.value!==""){loadSearch()}const params=searchState.getQueryStringParams();if(params.search!==undefined){searchState.setLoadingSearch();loadSearch()}},setLoadingSearch:()=>{const search=searchState.outputElement();search.innerHTML="

"+searchState.loadingText+"

";searchState.showResults(search)},descShards:new Map(),loadDesc:async function({descShard,descIndex}){if(descShard.promise===null){descShard.promise=new Promise((resolve,reject)=>{descShard.resolve=resolve;const ds=descShard;const fname=`${ds.crate}-desc-${ds.shard}-`;const url=resourcePath(`search.desc/${descShard.crate}/${fname}`,".js",);loadScript(url,reject)})}const list=await descShard.promise;return list[descIndex]},loadedDescShard:function(crate,shard,data){this.descShards.get(crate)[shard].resolve(data.split("\n"))},};const toggleAllDocsId="toggle-all-docs";let savedHash="";function handleHashes(ev){if(ev!==null&&searchState.isDisplayed()&&ev.newURL){switchDisplayedElement(null);const hash=ev.newURL.slice(ev.newURL.indexOf("#")+1);if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.search+"#"+hash)}const elem=document.getElementById(hash);if(elem){elem.scrollIntoView()}}const pageId=window.location.hash.replace(/^#/,"");if(savedHash!==pageId){savedHash=pageId;if(pageId!==""){expandSection(pageId)}}if(savedHash.startsWith("impl-")){const splitAt=savedHash.indexOf("/");if(splitAt!==-1){const implId=savedHash.slice(0,splitAt);const assocId=savedHash.slice(splitAt+1);const implElems=document.querySelectorAll(`details > summary > section[id^="${implId}"]`,);onEachLazy(implElems,implElem=>{const numbered=/^(.+?)-([0-9]+)$/.exec(implElem.id);if(implElem.id!==implId&&(!numbered||numbered[1]!==implId)){return false}return onEachLazy(implElem.parentElement.parentElement.querySelectorAll(`[id^="${assocId}"]`),item=>{const numbered=/^(.+?)-([0-9]+)$/.exec(item.id);if(item.id===assocId||(numbered&&numbered[1]===assocId)){openParentDetails(item);item.scrollIntoView();setTimeout(()=>{window.location.replace("#"+item.id)},0);return true}},)})}}}function onHashChange(ev){hideSidebar();handleHashes(ev)}function openParentDetails(elem){while(elem){if(elem.tagName==="DETAILS"){elem.open=true}elem=elem.parentNode}}function expandSection(id){openParentDetails(document.getElementById(id))}function handleEscape(ev){searchState.clearInputTimeout();searchState.hideResults();ev.preventDefault();searchState.defocus();window.hideAllModals(true)}function handleShortcut(ev){const disableShortcuts=getSettingValue("disable-shortcuts")==="true";if(ev.ctrlKey||ev.altKey||ev.metaKey||disableShortcuts){return}if(document.activeElement.tagName==="INPUT"&&document.activeElement.type!=="checkbox"&&document.activeElement.type!=="radio"){switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break}}else{switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break;case"s":case"S":case"/":ev.preventDefault();searchState.focus();break;case"+":ev.preventDefault();expandAllDocs();break;case"-":ev.preventDefault();collapseAllDocs();break;case"?":showHelp();break;default:break}}}document.addEventListener("keypress",handleShortcut);document.addEventListener("keydown",handleShortcut);function addSidebarItems(){if(!window.SIDEBAR_ITEMS){return}const sidebar=document.getElementsByClassName("sidebar-elems")[0];function block(shortty,id,longty){const filtered=window.SIDEBAR_ITEMS[shortty];if(!filtered){return}const modpath=hasClass(document.querySelector(".rustdoc"),"mod")?"../":"";const h3=document.createElement("h3");h3.innerHTML=`${longty}`;const ul=document.createElement("ul");ul.className="block "+shortty;for(const name of filtered){let path;if(shortty==="mod"){path=`${modpath}${name}/index.html`}else{path=`${modpath}${shortty}.${name}.html`}let current_page=document.location.href.toString();if(current_page.endsWith("/")){current_page+="index.html"}const link=document.createElement("a");link.href=path;link.textContent=name;const li=document.createElement("li");if(link.href===current_page){li.classList.add("current")}li.appendChild(link);ul.appendChild(li)}sidebar.appendChild(h3);sidebar.appendChild(ul)}if(sidebar){block("primitive","primitives","Primitive Types");block("mod","modules","Modules");block("macro","macros","Macros");block("struct","structs","Structs");block("enum","enums","Enums");block("constant","constants","Constants");block("static","static","Statics");block("trait","traits","Traits");block("fn","functions","Functions");block("type","types","Type Aliases");block("union","unions","Unions");block("foreigntype","foreign-types","Foreign Types");block("keyword","keywords","Keywords");block("attr","attributes","Attribute Macros");block("derive","derives","Derive Macros");block("traitalias","trait-aliases","Trait Aliases")}}window.register_implementors=imp=>{const implementors=document.getElementById("implementors-list");const synthetic_implementors=document.getElementById("synthetic-implementors-list");const inlined_types=new Set();const TEXT_IDX=0;const SYNTHETIC_IDX=1;const TYPES_IDX=2;if(synthetic_implementors){onEachLazy(synthetic_implementors.getElementsByClassName("impl"),el=>{const aliases=el.getAttribute("data-aliases");if(!aliases){return}aliases.split(",").forEach(alias=>{inlined_types.add(alias)})})}let currentNbImpls=implementors.getElementsByClassName("impl").length;const traitName=document.querySelector(".main-heading h1 > .trait").textContent;const baseIdName="impl-"+traitName+"-";const libs=Object.getOwnPropertyNames(imp);const script=document.querySelector("script[data-ignore-extern-crates]");const ignoreExternCrates=new Set((script?script.getAttribute("data-ignore-extern-crates"):"").split(","),);for(const lib of libs){if(lib===window.currentCrate||ignoreExternCrates.has(lib)){continue}const structs=imp[lib];struct_loop:for(const struct of structs){const list=struct[SYNTHETIC_IDX]?synthetic_implementors:implementors;if(struct[SYNTHETIC_IDX]){for(const struct_type of struct[TYPES_IDX]){if(inlined_types.has(struct_type)){continue struct_loop}inlined_types.add(struct_type)}}const code=document.createElement("h3");code.innerHTML=struct[TEXT_IDX];addClass(code,"code-header");onEachLazy(code.getElementsByTagName("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});const currentId=baseIdName+currentNbImpls;const anchor=document.createElement("a");anchor.href="#"+currentId;addClass(anchor,"anchor");const display=document.createElement("div");display.id=currentId;addClass(display,"impl");display.appendChild(anchor);display.appendChild(code);list.appendChild(display);currentNbImpls+=1}}};if(window.pending_implementors){window.register_implementors(window.pending_implementors)}window.register_type_impls=imp=>{if(!imp||!imp[window.currentCrate]){return}window.pending_type_impls=null;const idMap=new Map();let implementations=document.getElementById("implementations-list");let trait_implementations=document.getElementById("trait-implementations-list");let trait_implementations_header=document.getElementById("trait-implementations");const script=document.querySelector("script[data-self-path]");const selfPath=script?script.getAttribute("data-self-path"):null;const mainContent=document.querySelector("#main-content");const sidebarSection=document.querySelector(".sidebar section");let methods=document.querySelector(".sidebar .block.method");let associatedTypes=document.querySelector(".sidebar .block.associatedtype");let associatedConstants=document.querySelector(".sidebar .block.associatedconstant");let sidebarTraitList=document.querySelector(".sidebar .block.trait-implementation");for(const impList of imp[window.currentCrate]){const types=impList.slice(2);const text=impList[0];const isTrait=impList[1]!==0;const traitName=impList[1];if(types.indexOf(selfPath)===-1){continue}let outputList=isTrait?trait_implementations:implementations;if(outputList===null){const outputListName=isTrait?"Trait Implementations":"Implementations";const outputListId=isTrait?"trait-implementations-list":"implementations-list";const outputListHeaderId=isTrait?"trait-implementations":"implementations";const outputListHeader=document.createElement("h2");outputListHeader.id=outputListHeaderId;outputListHeader.innerText=outputListName;outputList=document.createElement("div");outputList.id=outputListId;if(isTrait){const link=document.createElement("a");link.href=`#${outputListHeaderId}`;link.innerText="Trait Implementations";const h=document.createElement("h3");h.appendChild(link);trait_implementations=outputList;trait_implementations_header=outputListHeader;sidebarSection.appendChild(h);sidebarTraitList=document.createElement("ul");sidebarTraitList.className="block trait-implementation";sidebarSection.appendChild(sidebarTraitList);mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}else{implementations=outputList;if(trait_implementations){mainContent.insertBefore(outputListHeader,trait_implementations_header);mainContent.insertBefore(outputList,trait_implementations_header)}else{const mainContent=document.querySelector("#main-content");mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}}}const template=document.createElement("template");template.innerHTML=text;onEachLazy(template.content.querySelectorAll("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});onEachLazy(template.content.querySelectorAll("[id]"),el=>{let i=0;if(idMap.has(el.id)){i=idMap.get(el.id)}else if(document.getElementById(el.id)){i=1;while(document.getElementById(`${el.id}-${2 * i}`)){i=2*i}while(document.getElementById(`${el.id}-${i}`)){i+=1}}if(i!==0){const oldHref=`#${el.id}`;const newHref=`#${el.id}-${i}`;el.id=`${el.id}-${i}`;onEachLazy(template.content.querySelectorAll("a[href]"),link=>{if(link.getAttribute("href")===oldHref){link.href=newHref}})}idMap.set(el.id,i+1)});const templateAssocItems=template.content.querySelectorAll("section.tymethod, "+"section.method, section.associatedtype, section.associatedconstant");if(isTrait){const li=document.createElement("li");const a=document.createElement("a");a.href=`#${template.content.querySelector(".impl").id}`;a.textContent=traitName;li.appendChild(a);sidebarTraitList.append(li)}else{onEachLazy(templateAssocItems,item=>{let block=hasClass(item,"associatedtype")?associatedTypes:(hasClass(item,"associatedconstant")?associatedConstants:(methods));if(!block){const blockTitle=hasClass(item,"associatedtype")?"Associated Types":(hasClass(item,"associatedconstant")?"Associated Constants":("Methods"));const blockClass=hasClass(item,"associatedtype")?"associatedtype":(hasClass(item,"associatedconstant")?"associatedconstant":("method"));const blockHeader=document.createElement("h3");const blockLink=document.createElement("a");blockLink.href="#implementations";blockLink.innerText=blockTitle;blockHeader.appendChild(blockLink);block=document.createElement("ul");block.className=`block ${blockClass}`;const insertionReference=methods||sidebarTraitList;if(insertionReference){const insertionReferenceH=insertionReference.previousElementSibling;sidebarSection.insertBefore(blockHeader,insertionReferenceH);sidebarSection.insertBefore(block,insertionReferenceH)}else{sidebarSection.appendChild(blockHeader);sidebarSection.appendChild(block)}if(hasClass(item,"associatedtype")){associatedTypes=block}else if(hasClass(item,"associatedconstant")){associatedConstants=block}else{methods=block}}const li=document.createElement("li");const a=document.createElement("a");a.innerText=item.id.split("-")[0].split(".")[1];a.href=`#${item.id}`;li.appendChild(a);block.appendChild(li)})}outputList.appendChild(template.content)}for(const list of[methods,associatedTypes,associatedConstants,sidebarTraitList]){if(!list){continue}const newChildren=Array.prototype.slice.call(list.children);newChildren.sort((a,b)=>{const aI=a.innerText;const bI=b.innerText;return aIbI?1:0});list.replaceChildren(...newChildren)}};if(window.pending_type_impls){window.register_type_impls(window.pending_type_impls)}function addSidebarCrates(){if(!window.ALL_CRATES){return}const sidebarElems=document.getElementsByClassName("sidebar-elems")[0];if(!sidebarElems){return}const h3=document.createElement("h3");h3.innerHTML="Crates";const ul=document.createElement("ul");ul.className="block crate";for(const crate of window.ALL_CRATES){const link=document.createElement("a");link.href=window.rootPath+crate+"/index.html";link.textContent=crate;const li=document.createElement("li");if(window.rootPath!=="./"&&crate===window.currentCrate){li.className="current"}li.appendChild(link);ul.appendChild(li)}sidebarElems.appendChild(h3);sidebarElems.appendChild(ul)}function expandAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);removeClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hasClass(e,"type-contents-toggle")&&!hasClass(e,"more-examples-toggle")){e.open=true}});innerToggle.title="collapse all docs";innerToggle.children[0].innerText="\u2212"}function collapseAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);addClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(e.parentNode.id!=="implementations-list"||(!hasClass(e,"implementors-toggle")&&!hasClass(e,"type-contents-toggle"))){e.open=false}});innerToggle.title="expand all docs";innerToggle.children[0].innerText="+"}function toggleAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);if(!innerToggle){return}if(hasClass(innerToggle,"will-expand")){expandAllDocs()}else{collapseAllDocs()}}(function(){const toggles=document.getElementById(toggleAllDocsId);if(toggles){toggles.onclick=toggleAllDocs}const hideMethodDocs=getSettingValue("auto-hide-method-docs")==="true";const hideImplementations=getSettingValue("auto-hide-trait-implementations")==="true";const hideLargeItemContents=getSettingValue("auto-hide-large-items")!=="false";function setImplementorsTogglesOpen(id,open){const list=document.getElementById(id);if(list!==null){onEachLazy(list.getElementsByClassName("implementors-toggle"),e=>{e.open=open})}}if(hideImplementations){setImplementorsTogglesOpen("trait-implementations-list",false);setImplementorsTogglesOpen("blanket-implementations-list",false)}onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hideLargeItemContents&&hasClass(e,"type-contents-toggle")){e.open=true}if(hideMethodDocs&&hasClass(e,"method-toggle")){e.open=false}})}());window.rustdoc_add_line_numbers_to_examples=()=>{onEachLazy(document.getElementsByClassName("rust-example-rendered"),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");if(line_numbers.length>0){return}const count=x.textContent.split("\n").length;const elems=[];for(let i=0;i{onEachLazy(document.getElementsByClassName("rust-example-rendered"),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");for(const node of line_numbers){parent.removeChild(node)}})};if(getSettingValue("line-numbers")==="true"){window.rustdoc_add_line_numbers_to_examples()}function showSidebar(){window.hideAllModals(false);const sidebar=document.getElementsByClassName("sidebar")[0];addClass(sidebar,"shown")}function hideSidebar(){const sidebar=document.getElementsByClassName("sidebar")[0];removeClass(sidebar,"shown")}window.addEventListener("resize",()=>{if(window.CURRENT_TOOLTIP_ELEMENT){const base=window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE;const force_visible=base.TOOLTIP_FORCE_VISIBLE;hideTooltip(false);if(force_visible){showTooltip(base);base.TOOLTIP_FORCE_VISIBLE=true}}});const mainElem=document.getElementById(MAIN_ID);if(mainElem){mainElem.addEventListener("click",hideSidebar)}onEachLazy(document.querySelectorAll("a[href^='#']"),el=>{el.addEventListener("click",()=>{expandSection(el.hash.slice(1));hideSidebar()})});onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"),el=>{el.addEventListener("click",e=>{if(e.target.tagName!=="SUMMARY"&&e.target.tagName!=="A"){e.preventDefault()}})});function showTooltip(e){const notable_ty=e.getAttribute("data-notable-ty");if(!window.NOTABLE_TRAITS&¬able_ty){const data=document.getElementById("notable-traits-data");if(data){window.NOTABLE_TRAITS=JSON.parse(data.innerText)}else{throw new Error("showTooltip() called with notable without any notable traits!")}}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE===e){clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);return}window.hideAllModals(false);const wrapper=document.createElement("div");if(notable_ty){wrapper.innerHTML="
"+window.NOTABLE_TRAITS[notable_ty]+"
"}else{if(e.getAttribute("title")!==null){e.setAttribute("data-title",e.getAttribute("title"));e.removeAttribute("title")}if(e.getAttribute("data-title")!==null){const titleContent=document.createElement("div");titleContent.className="content";titleContent.appendChild(document.createTextNode(e.getAttribute("data-title")));wrapper.appendChild(titleContent)}}wrapper.className="tooltip popover";const focusCatcher=document.createElement("div");focusCatcher.setAttribute("tabindex","0");focusCatcher.onfocus=hideTooltip;wrapper.appendChild(focusCatcher);const pos=e.getBoundingClientRect();wrapper.style.top=(pos.top+window.scrollY+pos.height)+"px";wrapper.style.left=0;wrapper.style.right="auto";wrapper.style.visibility="hidden";document.body.appendChild(wrapper);const wrapperPos=wrapper.getBoundingClientRect();const finalPos=pos.left+window.scrollX-wrapperPos.width+24;if(finalPos>0){wrapper.style.left=finalPos+"px"}else{wrapper.style.setProperty("--popover-arrow-offset",(wrapperPos.right-pos.right+4)+"px",)}wrapper.style.visibility="";window.CURRENT_TOOLTIP_ELEMENT=wrapper;window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE=e;clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);wrapper.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}clearTooltipHoverTimeout(e)};wrapper.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return}if(!e.TOOLTIP_FORCE_VISIBLE&&!e.contains(ev.relatedTarget)){setTooltipHoverTimeout(e,false);addClass(wrapper,"fade-out")}}}function setTooltipHoverTimeout(element,show){clearTooltipHoverTimeout(element);if(!show&&!window.CURRENT_TOOLTIP_ELEMENT){return}if(show&&window.CURRENT_TOOLTIP_ELEMENT){return}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE!==element){return}element.TOOLTIP_HOVER_TIMEOUT=setTimeout(()=>{if(show){showTooltip(element)}else if(!element.TOOLTIP_FORCE_VISIBLE){hideTooltip(false)}},show?window.RUSTDOC_TOOLTIP_HOVER_MS:window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS)}function clearTooltipHoverTimeout(element){if(element.TOOLTIP_HOVER_TIMEOUT!==undefined){removeClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out");clearTimeout(element.TOOLTIP_HOVER_TIMEOUT);delete element.TOOLTIP_HOVER_TIMEOUT}}function tooltipBlurHandler(event){if(window.CURRENT_TOOLTIP_ELEMENT&&!window.CURRENT_TOOLTIP_ELEMENT.contains(document.activeElement)&&!window.CURRENT_TOOLTIP_ELEMENT.contains(event.relatedTarget)&&!window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(document.activeElement)&&!window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(event.relatedTarget)){setTimeout(()=>hideTooltip(false),0)}}function hideTooltip(focus){if(window.CURRENT_TOOLTIP_ELEMENT){if(window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE){if(focus){window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus()}window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE=false}document.body.removeChild(window.CURRENT_TOOLTIP_ELEMENT);clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);window.CURRENT_TOOLTIP_ELEMENT=null}}onEachLazy(document.getElementsByClassName("tooltip"),e=>{e.onclick=()=>{e.TOOLTIP_FORCE_VISIBLE=e.TOOLTIP_FORCE_VISIBLE?false:true;if(window.CURRENT_TOOLTIP_ELEMENT&&!e.TOOLTIP_FORCE_VISIBLE){hideTooltip(true)}else{showTooltip(e);window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex","0");window.CURRENT_TOOLTIP_ELEMENT.focus();window.CURRENT_TOOLTIP_ELEMENT.onblur=tooltipBlurHandler}return false};e.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointermove=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return}if(!e.TOOLTIP_FORCE_VISIBLE&&window.CURRENT_TOOLTIP_ELEMENT&&!window.CURRENT_TOOLTIP_ELEMENT.contains(ev.relatedTarget)){setTooltipHoverTimeout(e,false);addClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out")}}});const sidebar_menu_toggle=document.getElementsByClassName("sidebar-menu-toggle")[0];if(sidebar_menu_toggle){sidebar_menu_toggle.addEventListener("click",()=>{const sidebar=document.getElementsByClassName("sidebar")[0];if(!hasClass(sidebar,"shown")){showSidebar()}else{hideSidebar()}})}function helpBlurHandler(event){blurHandler(event,getHelpButton(),window.hidePopoverMenus)}function buildHelpMenu(){const book_info=document.createElement("span");const channel=getVar("channel");book_info.className="top";book_info.innerHTML=`You can find more information in \ +the rustdoc book.`;const shortcuts=[["?","Show this help dialog"],["S / /","Focus the search field"],["↑","Move up in search results"],["↓","Move down in search results"],["← / →","Switch result tab (when results focused)"],["⏎","Go to active search result"],["+","Expand all sections"],["-","Collapse all sections"],].map(x=>"
"+x[0].split(" ").map((y,index)=>((index&1)===0?""+y+"":" "+y+" ")).join("")+"
"+x[1]+"
").join("");const div_shortcuts=document.createElement("div");addClass(div_shortcuts,"shortcuts");div_shortcuts.innerHTML="

Keyboard Shortcuts

"+shortcuts+"
";const infos=[`For a full list of all search features, take a look here.`,"Prefix searches with a type followed by a colon (e.g., fn:) to \ + restrict the search to a given item kind.","Accepted kinds are: fn, mod, struct, \ + enum, trait, type, macro, \ + and const.","Search functions by type signature (e.g., vec -> usize or \ + -> vec or String, enum:Cow -> bool)","You can look for items with an exact name by putting double quotes around \ + your request: \"string\"","Look for functions that accept or return \ + slices and \ + arrays by writing \ + square brackets (e.g., -> [u8] or [] -> Option)","Look for items inside another one by searching for a path: vec::Vec",].map(x=>"

"+x+"

").join("");const div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="

Search Tricks

"+infos;const rustdoc_version=document.createElement("span");rustdoc_version.className="bottom";const rustdoc_version_code=document.createElement("code");rustdoc_version_code.innerText="rustdoc "+getVar("rustdoc-version");rustdoc_version.appendChild(rustdoc_version_code);const container=document.createElement("div");if(!isHelpPage){container.className="popover"}container.id="help";container.style.display="none";const side_by_side=document.createElement("div");side_by_side.className="side-by-side";side_by_side.appendChild(div_shortcuts);side_by_side.appendChild(div_infos);container.appendChild(book_info);container.appendChild(side_by_side);container.appendChild(rustdoc_version);if(isHelpPage){const help_section=document.createElement("section");help_section.appendChild(container);document.getElementById("main-content").appendChild(help_section);container.style.display="block"}else{const help_button=getHelpButton();help_button.appendChild(container);container.onblur=helpBlurHandler;help_button.onblur=helpBlurHandler;help_button.children[0].onblur=helpBlurHandler}return container}window.hideAllModals=switchFocus=>{hideSidebar();window.hidePopoverMenus();hideTooltip(switchFocus)};window.hidePopoverMenus=()=>{onEachLazy(document.querySelectorAll(".search-form .popover"),elem=>{elem.style.display="none"})};function getHelpMenu(buildNeeded){let menu=getHelpButton().querySelector(".popover");if(!menu&&buildNeeded){menu=buildHelpMenu()}return menu}function showHelp(){getHelpButton().querySelector("a").focus();const menu=getHelpMenu(true);if(menu.style.display==="none"){window.hideAllModals();menu.style.display=""}}if(isHelpPage){showHelp();document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault()})}else{document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault();const menu=getHelpMenu(true);const shouldShowHelp=menu.style.display==="none";if(shouldShowHelp){showHelp()}else{window.hidePopoverMenus()}})}setMobileTopbar();addSidebarItems();addSidebarCrates();onHashChange(null);window.addEventListener("hashchange",onHashChange);searchState.setup()}());(function(){const SIDEBAR_MIN=100;const SIDEBAR_MAX=500;const RUSTDOC_MOBILE_BREAKPOINT=700;const BODY_MIN=400;const SIDEBAR_VANISH_THRESHOLD=SIDEBAR_MIN/2;const sidebarButton=document.getElementById("sidebar-button");if(sidebarButton){sidebarButton.addEventListener("click",e=>{removeClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","false");if(document.querySelector(".rustdoc.src")){window.rustdocToggleSrcSidebar()}e.preventDefault()})}let currentPointerId=null;let desiredSidebarSize=null;let pendingSidebarResizingFrame=false;const resizer=document.querySelector(".sidebar-resizer");const sidebar=document.querySelector(".sidebar");if(!resizer||!sidebar){return}const isSrcPage=hasClass(document.body,"src");function hideSidebar(){if(isSrcPage){window.rustdocCloseSourceSidebar();updateLocalStorage("src-sidebar-width",null);document.documentElement.style.removeProperty("--src-sidebar-width");sidebar.style.removeProperty("--src-sidebar-width");resizer.style.removeProperty("--src-sidebar-width")}else{addClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","true");updateLocalStorage("desktop-sidebar-width",null);document.documentElement.style.removeProperty("--desktop-sidebar-width");sidebar.style.removeProperty("--desktop-sidebar-width");resizer.style.removeProperty("--desktop-sidebar-width")}}function showSidebar(){if(isSrcPage){window.rustdocShowSourceSidebar()}else{removeClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","false")}}function changeSidebarSize(size){if(isSrcPage){updateLocalStorage("src-sidebar-width",size);sidebar.style.setProperty("--src-sidebar-width",size+"px");resizer.style.setProperty("--src-sidebar-width",size+"px")}else{updateLocalStorage("desktop-sidebar-width",size);sidebar.style.setProperty("--desktop-sidebar-width",size+"px");resizer.style.setProperty("--desktop-sidebar-width",size+"px")}}function isSidebarHidden(){return isSrcPage?!hasClass(document.documentElement,"src-sidebar-expanded"):hasClass(document.documentElement,"hide-sidebar")}function resize(e){if(currentPointerId===null||currentPointerId!==e.pointerId){return}e.preventDefault();const pos=e.clientX-3;if(pos=SIDEBAR_MIN){if(isSidebarHidden()){showSidebar()}const constrainedPos=Math.min(pos,window.innerWidth-BODY_MIN,SIDEBAR_MAX);changeSidebarSize(constrainedPos);desiredSidebarSize=constrainedPos;if(pendingSidebarResizingFrame!==false){clearTimeout(pendingSidebarResizingFrame)}pendingSidebarResizingFrame=setTimeout(()=>{if(currentPointerId===null||pendingSidebarResizingFrame===false){return}pendingSidebarResizingFrame=false;document.documentElement.style.setProperty("--resizing-sidebar-width",desiredSidebarSize+"px",)},100)}}window.addEventListener("resize",()=>{if(window.innerWidth=(window.innerWidth-BODY_MIN)){changeSidebarSize(window.innerWidth-BODY_MIN)}else if(desiredSidebarSize!==null&&desiredSidebarSize>SIDEBAR_MIN){changeSidebarSize(desiredSidebarSize)}});function stopResize(e){if(currentPointerId===null){return}if(e){e.preventDefault()}desiredSidebarSize=sidebar.getBoundingClientRect().width;removeClass(resizer,"active");window.removeEventListener("pointermove",resize,false);window.removeEventListener("pointerup",stopResize,false);removeClass(document.documentElement,"sidebar-resizing");document.documentElement.style.removeProperty("--resizing-sidebar-width");if(resizer.releasePointerCapture){resizer.releasePointerCapture(currentPointerId);currentPointerId=null}}function initResize(e){if(currentPointerId!==null||e.altKey||e.ctrlKey||e.metaKey||e.button!==0){return}if(resizer.setPointerCapture){resizer.setPointerCapture(e.pointerId);if(!resizer.hasPointerCapture(e.pointerId)){resizer.releasePointerCapture(e.pointerId);return}currentPointerId=e.pointerId}window.hideAllModals(false);e.preventDefault();window.addEventListener("pointermove",resize,false);window.addEventListener("pointercancel",stopResize,false);window.addEventListener("pointerup",stopResize,false);addClass(resizer,"active");addClass(document.documentElement,"sidebar-resizing");const pos=e.clientX-sidebar.offsetLeft-3;document.documentElement.style.setProperty("--resizing-sidebar-width",pos+"px");desiredSidebarSize=null}resizer.addEventListener("pointerdown",initResize,false)}());(function(){function copyContentToClipboard(content){const el=document.createElement("textarea");el.value=content;el.setAttribute("readonly","");el.style.position="absolute";el.style.left="-9999px";document.body.appendChild(el);el.select();document.execCommand("copy");document.body.removeChild(el)}function copyButtonAnimation(button){button.classList.add("clicked");if(button.reset_button_timeout!==undefined){window.clearTimeout(button.reset_button_timeout)}button.reset_button_timeout=window.setTimeout(()=>{button.reset_button_timeout=undefined;button.classList.remove("clicked")},1000)}const but=document.getElementById("copy-path");if(!but){return}but.onclick=()=>{const parent=but.parentElement;const path=[];onEach(parent.childNodes,child=>{if(child.tagName==="A"){path.push(child.textContent)}});copyContentToClipboard(path.join("::"));copyButtonAnimation(but)};function copyCode(codeElem){if(!codeElem){return}copyContentToClipboard(codeElem.textContent)}function getExampleWrap(event){let elem=event.target;while(!hasClass(elem,"example-wrap")){if(elem===document.body||elem.tagName==="A"||elem.tagName==="BUTTON"||hasClass(elem,"docblock")){return null}elem=elem.parentElement}return elem}function addCopyButton(event){const elem=getExampleWrap(event);if(elem===null){return}elem.removeEventListener("mouseover",addCopyButton);const parent=document.createElement("div");parent.className="button-holder";const runButton=elem.querySelector(".test-arrow");if(runButton!==null){parent.appendChild(runButton)}elem.appendChild(parent);const copyButton=document.createElement("button");copyButton.className="copy-button";copyButton.title="Copy code to clipboard";copyButton.addEventListener("click",()=>{copyCode(elem.querySelector("pre > code"));copyButtonAnimation(copyButton)});parent.appendChild(copyButton)}function showHideCodeExampleButtons(event){const elem=getExampleWrap(event);if(elem===null){return}const buttons=elem.querySelector(".button-holder");if(buttons===null){return}buttons.classList.toggle("keep-visible")}onEachLazy(document.querySelectorAll(".docblock .example-wrap"),elem=>{elem.addEventListener("mouseover",addCopyButton);elem.addEventListener("click",showHideCodeExampleButtons)})}()) \ No newline at end of file diff --git a/doc/static.files/normalize-76eba96aa4d2e634.css b/doc/static.files/normalize-76eba96aa4d2e634.css new file mode 100644 index 00000000..469959f1 --- /dev/null +++ b/doc/static.files/normalize-76eba96aa4d2e634.css @@ -0,0 +1,2 @@ + /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type="button"],[type="reset"],[type="submit"],button{-webkit-appearance:button}[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none} \ No newline at end of file diff --git a/doc/static.files/noscript-3b12f09e550e0385.css b/doc/static.files/noscript-3b12f09e550e0385.css new file mode 100644 index 00000000..befe4163 --- /dev/null +++ b/doc/static.files/noscript-3b12f09e550e0385.css @@ -0,0 +1 @@ + #main-content .attributes{margin-left:0 !important;}#copy-path,#sidebar-button,.sidebar-resizer{display:none !important;}nav.sub{display:none;}.src .sidebar{display:none;}.notable-traits{display:none;}:root,:root:not([data-theme]){--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--mobile-sidebar-menu-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--settings-menu-filter:none;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);--sidebar-resizer-hover:hsl(207,90%,66%);--sidebar-resizer-active:hsl(207,90%,54%);}@media (prefers-color-scheme:dark){:root,:root:not([data-theme]){--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);--sidebar-resizer-hover:hsl(207,30%,54%);--sidebar-resizer-active:hsl(207,90%,54%);}} \ No newline at end of file diff --git a/doc/static.files/rust-logo-151179464ae7ed46.svg b/doc/static.files/rust-logo-151179464ae7ed46.svg new file mode 100644 index 00000000..62424d8f --- /dev/null +++ b/doc/static.files/rust-logo-151179464ae7ed46.svg @@ -0,0 +1,61 @@ + + + diff --git a/doc/static.files/rustdoc-492a78a4a87dcc01.css b/doc/static.files/rustdoc-492a78a4a87dcc01.css new file mode 100644 index 00000000..e9199f95 --- /dev/null +++ b/doc/static.files/rustdoc-492a78a4a87dcc01.css @@ -0,0 +1,47 @@ + :root{--nav-sub-mobile-padding:8px;--search-typename-width:6.75rem;--desktop-sidebar-width:200px;--src-sidebar-width:300px;--desktop-sidebar-z-index:100;--sidebar-elems-left-padding:24px;--clipboard-image:url('data:image/svg+xml,\ +\ +\ +');--copy-path-height:34px;--copy-path-width:33px;--checkmark-image:url('data:image/svg+xml,\ +\ +');--button-left-margin:4px;--button-border-radius:2px;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:400;src:local('Fira Sans'),url("FiraSans-Regular-018c141bf0843ffd.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:500;src:local('Fira Sans Medium'),url("FiraSans-Medium-8f9a781e4970d388.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:400;src:local('Source Serif 4'),url("SourceSerif4-Regular-46f98efaafac5295.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:italic;font-weight:400;src:local('Source Serif 4 Italic'),url("SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:700;src:local('Source Serif 4 Bold'),url("SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:400;src:url("SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:italic;font-weight:400;src:url("SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:600;src:url("SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'NanumBarunGothic';src:url("NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2") format("woff2");font-display:swap;unicode-range:U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF;}*{box-sizing:border-box;}body{font:1rem/1.5 "Source Serif 4",NanumBarunGothic,serif;margin:0;position:relative;overflow-wrap:break-word;overflow-wrap:anywhere;font-feature-settings:"kern","liga";background-color:var(--main-background-color);color:var(--main-color);}h1{font-size:1.5rem;}h2{font-size:1.375rem;}h3{font-size:1.25rem;}h1,h2,h3,h4,h5,h6{font-weight:500;}h1,h2,h3,h4{margin:25px 0 15px 0;padding-bottom:6px;}.docblock h3,.docblock h4,h5,h6{margin:15px 0 5px 0;}.docblock>h2:first-child,.docblock>h3:first-child,.docblock>h4:first-child,.docblock>h5:first-child,.docblock>h6:first-child{margin-top:0;}.main-heading h1{margin:0;padding:0;flex-grow:1;overflow-wrap:break-word;overflow-wrap:anywhere;}.main-heading{display:flex;flex-wrap:wrap;padding-bottom:6px;margin-bottom:15px;}.content h2,.top-doc .docblock>h3,.top-doc .docblock>h4{border-bottom:1px solid var(--headings-border-bottom-color);}h1,h2{line-height:1.25;padding-top:3px;padding-bottom:9px;}h3.code-header{font-size:1.125rem;}h4.code-header{font-size:1rem;}.code-header{font-weight:600;margin:0;padding:0;white-space:pre-wrap;}#crate-search,h1,h2,h3,h4,h5,h6,.sidebar,.mobile-topbar,.search-input,.search-results .result-name,.item-name>a,.out-of-band,span.since,a.src,#help-button>a,summary.hideme,.scraped-example-list,ul.all-items{font-family:"Fira Sans",Arial,NanumBarunGothic,sans-serif;}#toggle-all-docs,a.anchor,.section-header a,#src-sidebar a,.rust a,.sidebar h2 a,.sidebar h3 a,.mobile-topbar h2 a,h1 a,.search-results a,.stab,.result-name i{color:var(--main-color);}span.enum,a.enum,span.struct,a.struct,span.union,a.union,span.primitive,a.primitive,span.type,a.type,span.foreigntype,a.foreigntype{color:var(--type-link-color);}span.trait,a.trait,span.traitalias,a.traitalias{color:var(--trait-link-color);}span.associatedtype,a.associatedtype,span.constant,a.constant,span.static,a.static{color:var(--assoc-item-link-color);}span.fn,a.fn,span.method,a.method,span.tymethod,a.tymethod{color:var(--function-link-color);}span.attr,a.attr,span.derive,a.derive,span.macro,a.macro{color:var(--macro-link-color);}span.mod,a.mod{color:var(--mod-link-color);}span.keyword,a.keyword{color:var(--keyword-link-color);}a{color:var(--link-color);text-decoration:none;}ol,ul{padding-left:24px;}ul ul,ol ul,ul ol,ol ol{margin-bottom:.625em;}p,.docblock>.warning{margin:0 0 .75em 0;}p:last-child,.docblock>.warning:last-child{margin:0;}button{padding:1px 6px;cursor:pointer;}button#toggle-all-docs{padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.rustdoc{display:flex;flex-direction:row;flex-wrap:nowrap;}main{position:relative;flex-grow:1;padding:10px 15px 40px 45px;min-width:0;}.src main{padding:15px;}.width-limiter{max-width:960px;margin-right:auto;}details:not(.toggle) summary{margin-bottom:.6em;}code,pre,.code-header{font-family:"Source Code Pro",monospace;}.docblock code,.docblock-short code{border-radius:3px;padding:0 0.125em;}.docblock pre code,.docblock-short pre code{padding:0;}pre{padding:14px;line-height:1.5;}pre.item-decl{overflow-x:auto;}.item-decl .type-contents-toggle{contain:initial;}.src .content pre{padding:20px;}.rustdoc.src .example-wrap pre.src-line-numbers{padding:20px 0 20px 4px;}img{max-width:100%;}.logo-container{line-height:0;display:block;}.rust-logo{filter:var(--rust-logo-filter);}.sidebar{font-size:0.875rem;flex:0 0 var(--desktop-sidebar-width);width:var(--desktop-sidebar-width);overflow-y:scroll;overscroll-behavior:contain;position:sticky;height:100vh;top:0;left:0;z-index:var(--desktop-sidebar-z-index);}.rustdoc.src .sidebar{flex-basis:50px;width:50px;border-right:1px solid;overflow-x:hidden;overflow-y:hidden;}.hide-sidebar .sidebar,.hide-sidebar .sidebar-resizer{display:none;}.sidebar-resizer{touch-action:none;width:9px;cursor:col-resize;z-index:calc(var(--desktop-sidebar-z-index) + 1);position:fixed;height:100%;left:calc(var(--desktop-sidebar-width) + 1px);}.rustdoc.src .sidebar-resizer{left:49px;}.src-sidebar-expanded .src .sidebar-resizer{left:var(--src-sidebar-width);}.sidebar-resizing{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;}.sidebar-resizing*{cursor:col-resize !important;}.sidebar-resizing .sidebar{position:fixed;}.sidebar-resizing>body{padding-left:var(--resizing-sidebar-width);}.sidebar-resizer:hover,.sidebar-resizer:active,.sidebar-resizer:focus,.sidebar-resizer.active{width:10px;margin:0;left:var(--desktop-sidebar-width);border-left:solid 1px var(--sidebar-resizer-hover);}.src-sidebar-expanded .rustdoc.src .sidebar-resizer:hover,.src-sidebar-expanded .rustdoc.src .sidebar-resizer:active,.src-sidebar-expanded .rustdoc.src .sidebar-resizer:focus,.src-sidebar-expanded .rustdoc.src .sidebar-resizer.active{left:calc(var(--src-sidebar-width) - 1px);}@media (pointer:coarse){.sidebar-resizer{display:none !important;}}.sidebar-resizer.active{padding:0 140px;width:2px;margin-left:-140px;border-left:none;}.sidebar-resizer.active:before{border-left:solid 2px var(--sidebar-resizer-active);display:block;height:100%;content:"";}.sidebar,.mobile-topbar,.sidebar-menu-toggle,#src-sidebar{background-color:var(--sidebar-background-color);}.src .sidebar>*{visibility:hidden;}.src-sidebar-expanded .src .sidebar{overflow-y:auto;flex-basis:var(--src-sidebar-width);width:var(--src-sidebar-width);}.src-sidebar-expanded .src .sidebar>*{visibility:visible;}#all-types{margin-top:1em;}*{scrollbar-width:initial;scrollbar-color:var(--scrollbar-color);}.sidebar{scrollbar-width:thin;scrollbar-color:var(--scrollbar-color);}::-webkit-scrollbar{width:12px;}.sidebar::-webkit-scrollbar{width:8px;}::-webkit-scrollbar-track{-webkit-box-shadow:inset 0;background-color:var(--scrollbar-track-background-color);}.sidebar::-webkit-scrollbar-track{background-color:var(--scrollbar-track-background-color);}::-webkit-scrollbar-thumb,.sidebar::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb-background-color);}.hidden{display:none !important;}.logo-container>img{height:48px;width:48px;}ul.block,.block li{padding:0;margin:0;list-style:none;}.sidebar-elems a,.sidebar>h2 a{display:block;padding:0.25rem;margin-right:0.25rem;border-left:solid var(--sidebar-elems-left-padding) transparent;margin-left:calc(-0.25rem - var(--sidebar-elems-left-padding));background-clip:border-box;}.sidebar h2{text-wrap:balance;overflow-wrap:anywhere;padding:0;margin:0.7rem 0;}.sidebar h3{text-wrap:balance;overflow-wrap:anywhere;font-size:1.125rem;padding:0;margin:0;}.sidebar-elems,.sidebar>.version,.sidebar>h2{padding-left:var(--sidebar-elems-left-padding);}.sidebar a{color:var(--sidebar-link-color);}.sidebar .current,.sidebar .current a,.sidebar-crate a.logo-container:hover+h2 a,.sidebar a:hover:not(.logo-container){background-color:var(--sidebar-current-link-background-color);}.sidebar-elems .block{margin-bottom:2em;}.sidebar-elems .block li a{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}.sidebar-crate{display:flex;align-items:center;justify-content:center;margin:14px 32px 1rem;row-gap:10px;column-gap:32px;flex-wrap:wrap;}.sidebar-crate h2{flex-grow:1;margin:0 -8px;align-self:start;}.sidebar-crate .logo-container{margin:0 calc(-16px - var(--sidebar-elems-left-padding));padding:0 var(--sidebar-elems-left-padding);text-align:center;}.sidebar-crate .logo-container img{margin-top:-16px;border-top:solid 16px transparent;box-sizing:content-box;position:relative;background-clip:border-box;z-index:1;}.sidebar-crate h2 a{display:block;border-left:solid var(--sidebar-elems-left-padding) transparent;background-clip:border-box;margin:0 calc(-24px + 0.25rem) 0 calc(-0.2rem - var(--sidebar-elems-left-padding));padding:calc((16px - 0.57rem ) / 2 ) 0.25rem;padding-left:0.2rem;}.sidebar-crate h2 .version{display:block;font-weight:normal;font-size:1rem;overflow-wrap:break-word;}.sidebar-crate+.version{margin-top:-1rem;margin-bottom:1rem;}.mobile-topbar{display:none;}.rustdoc .example-wrap{display:flex;position:relative;margin-bottom:10px;}.rustdoc .example-wrap>pre{border-radius:6px;}.rustdoc .example-wrap:last-child{margin-bottom:0px;}.rustdoc .example-wrap pre{margin:0;flex-grow:1;}.rustdoc:not(.src) .example-wrap pre{overflow:auto hidden;}.rustdoc .example-wrap pre.example-line-numbers,.rustdoc .example-wrap pre.src-line-numbers{flex-grow:0;min-width:fit-content;overflow:initial;text-align:right;-webkit-user-select:none;user-select:none;padding:14px 8px;color:var(--src-line-numbers-span-color);}.rustdoc .example-wrap pre.src-line-numbers{padding:14px 0;}.src-line-numbers a,.src-line-numbers span{color:var(--src-line-numbers-span-color);padding:0 8px;}.src-line-numbers :target{background-color:transparent;border-right:none;padding:0 8px;}.src-line-numbers .line-highlighted{background-color:var(--src-line-number-highlighted-background-color);}.search-loading{text-align:center;}.docblock-short{overflow-wrap:break-word;overflow-wrap:anywhere;}.docblock :not(pre)>code,.docblock-short code{white-space:pre-wrap;}.top-doc .docblock h2{font-size:1.375rem;}.top-doc .docblock h3{font-size:1.25rem;}.top-doc .docblock h4,.top-doc .docblock h5{font-size:1.125rem;}.top-doc .docblock h6{font-size:1rem;}.docblock h5{font-size:1rem;}.docblock h6{font-size:0.875rem;}.docblock{margin-left:24px;position:relative;}.docblock>:not(.more-examples-toggle):not(.example-wrap){max-width:100%;overflow-x:auto;}.out-of-band{flex-grow:0;font-size:1.125rem;}.docblock code,.docblock-short code,pre,.rustdoc.src .example-wrap{background-color:var(--code-block-background-color);}#main-content{position:relative;}.docblock table{margin:.5em 0;border-collapse:collapse;}.docblock table td,.docblock table th{padding:.5em;border:1px solid var(--border-color);}.docblock table tbody tr:nth-child(2n){background:var(--table-alt-row-background-color);}.docblock .stab,.docblock-short .stab{display:inline-block;}div.where{white-space:pre-wrap;font-size:0.875rem;}.item-info{display:block;margin-left:24px;}.item-info code{font-size:0.875rem;}#main-content>.item-info{margin-left:0;}nav.sub{flex-grow:1;flex-flow:row nowrap;margin:4px 0 25px 0;display:flex;align-items:center;}.search-form{position:relative;display:flex;height:34px;flex-grow:1;}.src nav.sub{margin:0 0 15px 0;}.section-header{display:block;position:relative;}.section-header:hover>.anchor,.impl:hover>.anchor,.trait-impl:hover>.anchor,.variant:hover>.anchor{display:initial;}.anchor{display:none;position:absolute;left:-0.5em;background:none !important;}.anchor.field{left:-5px;}.section-header>.anchor{left:-15px;padding-right:8px;}h2.section-header>.anchor{padding-right:6px;}a.doc-anchor{color:var(--main-color);display:none;position:absolute;left:-17px;padding-right:10px;padding-left:3px;}*:hover>.doc-anchor{display:block;}.top-doc>.docblock>*:first-child>.doc-anchor{display:none !important;}.main-heading a:hover,.example-wrap .rust a:hover,.all-items a:hover,.docblock a:not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),.docblock-short a:not(.scrape-help):not(.tooltip):hover,.item-info a{text-decoration:underline;}.crate.block li.current a{font-weight:500;}table,.item-table{overflow-wrap:break-word;}.item-table{display:table;padding:0;margin:0;width:100%;}.item-table>li{display:table-row;}.item-table>li>div{display:table-cell;}.item-table>li>.item-name{padding-right:1.25rem;}.search-results-title{margin-top:0;white-space:nowrap;display:flex;align-items:baseline;}#crate-search-div{position:relative;min-width:5em;}#crate-search{min-width:115px;padding:0 23px 0 4px;max-width:100%;text-overflow:ellipsis;border:1px solid var(--border-color);border-radius:4px;outline:none;cursor:pointer;-moz-appearance:none;-webkit-appearance:none;text-indent:0.01px;background-color:var(--main-background-color);color:inherit;line-height:1.5;font-weight:500;}#crate-search:hover,#crate-search:focus{border-color:var(--crate-search-hover-border);}#crate-search-div::after{pointer-events:none;width:100%;height:100%;position:absolute;top:0;left:0;content:"";background-repeat:no-repeat;background-size:20px;background-position:calc(100% - 2px) 56%;background-image:url('data:image/svg+xml, \ + ');filter:var(--crate-search-div-filter);}#crate-search-div:hover::after,#crate-search-div:focus-within::after{filter:var(--crate-search-div-hover-filter);}#crate-search>option{font-size:1rem;}.search-input{-webkit-appearance:none;outline:none;border:1px solid var(--border-color);border-radius:2px;padding:8px;font-size:1rem;flex-grow:1;background-color:var(--button-background-color);color:var(--search-color);}.search-input:focus{border-color:var(--search-input-focused-border-color);}.search-results{display:none;}.search-results.active{display:block;}.search-results>a{display:flex;margin-left:2px;margin-right:2px;border-bottom:1px solid var(--search-result-border-color);gap:1em;}.search-results>a>div.desc{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex:2;}.search-results a:hover,.search-results a:focus{background-color:var(--search-result-link-focus-background-color);}.search-results .result-name{display:flex;align-items:center;justify-content:start;flex:3;}.search-results .result-name .alias{color:var(--search-results-alias-color);}.search-results .result-name .grey{color:var(--search-results-grey-color);}.search-results .result-name .typename{color:var(--search-results-grey-color);font-size:0.875rem;width:var(--search-typename-width);}.search-results .result-name .path{word-break:break-all;max-width:calc(100% - var(--search-typename-width));display:inline-block;}.search-results .result-name .path>*{display:inline;}.popover{position:absolute;top:100%;right:0;z-index:calc(var(--desktop-sidebar-z-index) + 1);margin-top:7px;border-radius:3px;border:1px solid var(--border-color);background-color:var(--main-background-color);color:var(--main-color);--popover-arrow-offset:11px;}.popover::before{content:'';position:absolute;right:var(--popover-arrow-offset);border:solid var(--border-color);border-width:1px 1px 0 0;background-color:var(--main-background-color);padding:4px;transform:rotate(-45deg);top:-5px;}.setting-line{margin:1.2em 0.6em;}.setting-radio input,.setting-check input{margin-right:0.3em;height:1.2rem;width:1.2rem;border:2px solid var(--settings-input-border-color);outline:none;-webkit-appearance:none;cursor:pointer;}.setting-radio input{border-radius:50%;}.setting-radio span,.setting-check span{padding-bottom:1px;}.setting-radio{margin-top:0.1em;margin-bottom:0.1em;min-width:3.8em;padding:0.3em;display:inline-flex;align-items:center;cursor:pointer;}.setting-radio+.setting-radio{margin-left:0.5em;}.setting-check{margin-right:20px;display:flex;align-items:center;cursor:pointer;}.setting-radio input:checked{box-shadow:inset 0 0 0 3px var(--main-background-color);background-color:var(--settings-input-color);}.setting-check input:checked{background-color:var(--settings-input-color);border-width:1px;content:url('data:image/svg+xml,\ + \ + ');}.setting-radio input:focus,.setting-check input:focus{box-shadow:0 0 1px 1px var(--settings-input-color);}.setting-radio input:checked:focus{box-shadow:inset 0 0 0 3px var(--main-background-color),0 0 2px 2px var(--settings-input-color);}.setting-radio input:hover,.setting-check input:hover{border-color:var(--settings-input-color) !important;}#help.popover{max-width:600px;--popover-arrow-offset:48px;}#help dt{float:left;clear:left;margin-right:0.5rem;}#help span.top,#help span.bottom{text-align:center;display:block;font-size:1.125rem;}#help span.top{margin:10px 0;border-bottom:1px solid var(--border-color);padding-bottom:4px;margin-bottom:6px;}#help span.bottom{clear:both;border-top:1px solid var(--border-color);}.side-by-side>div{width:50%;float:left;padding:0 20px 20px 17px;}.item-info .stab{display:block;padding:3px;margin-bottom:5px;}.item-name .stab{margin-left:0.3125em;}.stab{padding:0 2px;font-size:0.875rem;font-weight:normal;color:var(--main-color);background-color:var(--stab-background-color);width:fit-content;white-space:pre-wrap;border-radius:3px;display:inline;vertical-align:baseline;}.stab.portability>code{background:none;color:var(--stab-code-color);}.stab .emoji,.item-info .stab::before{font-size:1.25rem;}.stab .emoji{margin-right:0.3rem;}.item-info .stab::before{content:"\0";width:0;display:inline-block;color:transparent;}.emoji{text-shadow:1px 0 0 black,-1px 0 0 black,0 1px 0 black,0 -1px 0 black;}.since{font-weight:normal;font-size:initial;}.rightside{padding-left:12px;float:right;}.rightside:not(a),.out-of-band{color:var(--right-side-color);}pre.rust{tab-size:4;-moz-tab-size:4;}pre.rust .kw{color:var(--code-highlight-kw-color);}pre.rust .kw-2{color:var(--code-highlight-kw-2-color);}pre.rust .lifetime{color:var(--code-highlight-lifetime-color);}pre.rust .prelude-ty{color:var(--code-highlight-prelude-color);}pre.rust .prelude-val{color:var(--code-highlight-prelude-val-color);}pre.rust .string{color:var(--code-highlight-string-color);}pre.rust .number{color:var(--code-highlight-number-color);}pre.rust .bool-val{color:var(--code-highlight-literal-color);}pre.rust .self{color:var(--code-highlight-self-color);}pre.rust .attr{color:var(--code-highlight-attribute-color);}pre.rust .macro,pre.rust .macro-nonterminal{color:var(--code-highlight-macro-color);}pre.rust .question-mark{font-weight:bold;color:var(--code-highlight-question-mark-color);}pre.rust .comment{color:var(--code-highlight-comment-color);}pre.rust .doccomment{color:var(--code-highlight-doc-comment-color);}.rustdoc.src .example-wrap pre.rust a{background:var(--codeblock-link-background);}.example-wrap.compile_fail,.example-wrap.should_panic{border-left:2px solid var(--codeblock-error-color);}.ignore.example-wrap{border-left:2px solid var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover,.example-wrap.should_panic:hover{border-left:2px solid var(--codeblock-error-hover-color);}.example-wrap.ignore:hover{border-left:2px solid var(--codeblock-ignore-hover-color);}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip{color:var(--codeblock-error-color);}.example-wrap.ignore .tooltip{color:var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover .tooltip,.example-wrap.should_panic:hover .tooltip{color:var(--codeblock-error-hover-color);}.example-wrap.ignore:hover .tooltip{color:var(--codeblock-ignore-hover-color);}.example-wrap .tooltip{position:absolute;display:block;left:-25px;top:5px;margin:0;line-height:1;}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip,.example-wrap.ignore .tooltip{font-weight:bold;font-size:1.25rem;}.content .docblock .warning{border-left:2px solid var(--warning-border-color);padding:14px;position:relative;overflow-x:visible !important;}.content .docblock .warning::before{color:var(--warning-border-color);content:"ⓘ";position:absolute;left:-25px;top:5px;font-weight:bold;font-size:1.25rem;}.top-doc>.docblock>.warning:first-child::before{top:20px;}.example-wrap>a.test-arrow,.example-wrap .button-holder{visibility:hidden;position:absolute;top:4px;right:4px;z-index:1;}a.test-arrow{height:var(--copy-path-height);padding:6px 4px 0 11px;}a.test-arrow::before{content:url('data:image/svg+xml,');}.example-wrap .button-holder{display:flex;}@media not (pointer:coarse){.example-wrap:hover>a.test-arrow,.example-wrap:hover>.button-holder{visibility:visible;}}.example-wrap .button-holder.keep-visible{visibility:visible;}.example-wrap .button-holder .copy-button,.example-wrap .test-arrow{background:var(--main-background-color);cursor:pointer;border-radius:var(--button-border-radius);height:var(--copy-path-height);width:var(--copy-path-width);}.example-wrap .button-holder .copy-button{margin-left:var(--button-left-margin);padding:2px 0 0 4px;border:0;}.example-wrap .button-holder .copy-button::before,.example-wrap .test-arrow::before{filter:var(--copy-path-img-filter);}.example-wrap .button-holder .copy-button::before{content:var(--clipboard-image);}.example-wrap .button-holder .copy-button:hover::before,.example-wrap .test-arrow:hover::before{filter:var(--copy-path-img-hover-filter);}.example-wrap .button-holder .copy-button.clicked::before{content:var(--checkmark-image);padding-right:5px;}.code-attribute{font-weight:300;color:var(--code-attribute-color);}.item-spacer{width:100%;height:12px;display:block;}.out-of-band>span.since{font-size:1.25rem;}.sub-variant h4{font-size:1rem;font-weight:400;margin-top:0;margin-bottom:0;}.sub-variant{margin-left:24px;margin-bottom:40px;}.sub-variant>.sub-variant-field{margin-left:24px;}@keyframes targetfadein{from{background-color:var(--main-background-color);}10%{background-color:var(--target-border-color);}to{background-color:var(--target-background-color);}}:target{padding-right:3px;background-color:var(--target-background-color);border-right:3px solid var(--target-border-color);animation:0.65s cubic-bezier(0,0,0.1,1.0) 0.1s targetfadein;}.code-header a.tooltip{color:inherit;margin-right:15px;position:relative;}.code-header a.tooltip:hover{color:var(--link-color);}a.tooltip:hover::after{position:absolute;top:calc(100% - 10px);left:-15px;right:-15px;height:20px;content:"\00a0";}.fade-out{opacity:0;transition:opacity 0.45s cubic-bezier(0,0,0.1,1.0);}.popover.tooltip .content{margin:0.25em 0.5em;}.popover.tooltip .content pre,.popover.tooltip .content code{background:transparent;margin:0;padding:0;font-size:1.25rem;white-space:pre-wrap;}.popover.tooltip .content>h3:first-child{margin:0 0 5px 0;}.search-failed{text-align:center;margin-top:20px;display:none;}.search-failed.active{display:block;}.search-failed>ul{text-align:left;max-width:570px;margin-left:auto;margin-right:auto;}#search-tabs{display:flex;flex-direction:row;gap:1px;margin-bottom:4px;}#search-tabs button{text-align:center;font-size:1.125rem;border:0;border-top:2px solid;flex:1;line-height:1.5;color:inherit;}#search-tabs button:not(.selected){background-color:var(--search-tab-button-not-selected-background);border-top-color:var(--search-tab-button-not-selected-border-top-color);}#search-tabs button:hover,#search-tabs button.selected{background-color:var(--search-tab-button-selected-background);border-top-color:var(--search-tab-button-selected-border-top-color);}#search-tabs .count{font-size:1rem;font-variant-numeric:tabular-nums;color:var(--search-tab-title-count-color);}#search .error code{border-radius:3px;background-color:var(--search-error-code-background-color);}.search-corrections{font-weight:normal;}#src-sidebar{width:100%;overflow:auto;}#src-sidebar div.files>a:hover,details.dir-entry summary:hover,#src-sidebar div.files>a:focus,details.dir-entry summary:focus{background-color:var(--src-sidebar-background-hover);}#src-sidebar div.files>a.selected{background-color:var(--src-sidebar-background-selected);}.src-sidebar-title{position:sticky;top:0;display:flex;padding:8px 8px 0 48px;margin-bottom:7px;background:var(--sidebar-background-color);border-bottom:1px solid var(--border-color);}#settings-menu,#help-button{margin-left:var(--button-left-margin);display:flex;}#sidebar-button{display:none;line-height:0;}.hide-sidebar #sidebar-button,.src #sidebar-button{display:flex;margin-right:4px;position:fixed;left:6px;height:34px;width:34px;background-color:var(--main-background-color);z-index:1;}.src #sidebar-button{left:8px;z-index:calc(var(--desktop-sidebar-z-index) + 1);}.hide-sidebar .src #sidebar-button{position:static;}#settings-menu>a,#help-button>a,#sidebar-button>a{display:flex;align-items:center;justify-content:center;background-color:var(--button-background-color);border:1px solid var(--border-color);border-radius:var(--button-border-radius);color:var(--settings-button-color);font-size:20px;width:33px;}#settings-menu>a:hover,#settings-menu>a:focus,#help-button>a:hover,#help-button>a:focus,#sidebar-button>a:hover,#sidebar-button>a:focus{border-color:var(--settings-button-border-focus);}#settings-menu>a{line-height:0;font-size:0;}#settings-menu>a:before{content:url('data:image/svg+xml,\ + ');width:22px;height:22px;filter:var(--settings-menu-filter);}#sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');width:22px;height:22px;}#copy-path{color:var(--copy-path-button-color);background:var(--main-background-color);height:var(--copy-path-height);width:var(--copy-path-width);margin-left:10px;padding:0;padding-left:2px;border:0;font-size:0;}#copy-path::before{filter:var(--copy-path-img-filter);content:var(--clipboard-image);}#copy-path:hover::before{filter:var(--copy-path-img-hover-filter);}#copy-path.clicked::before{content:var(--checkmark-image);}@keyframes rotating{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}#settings-menu.rotate>a img{animation:rotating 2s linear infinite;}kbd{display:inline-block;padding:3px 5px;font:15px monospace;line-height:10px;vertical-align:middle;border:solid 1px var(--border-color);border-radius:3px;color:var(--kbd-color);background-color:var(--kbd-background);box-shadow:inset 0 -1px 0 var(--kbd-box-shadow-color);}ul.all-items>li{list-style:none;}details.dir-entry{padding-left:4px;}details.dir-entry>summary{margin:0 0 0 -4px;padding:0 0 0 4px;cursor:pointer;}details.dir-entry div.folders,details.dir-entry div.files{padding-left:23px;}details.dir-entry a{display:block;}details.toggle{contain:layout;position:relative;}details.toggle>summary.hideme{cursor:pointer;font-size:1rem;}details.toggle>summary{list-style:none;outline:none;}details.toggle>summary::-webkit-details-marker,details.toggle>summary::marker{display:none;}details.toggle>summary.hideme>span{margin-left:9px;}details.toggle>summary::before{background:url('data:image/svg+xml,') no-repeat top left;content:"";cursor:pointer;width:16px;height:16px;display:inline-block;vertical-align:middle;opacity:.5;filter:var(--toggle-filter);}details.toggle>summary.hideme>span,.more-examples-toggle summary,.more-examples-toggle .hide-more{color:var(--toggles-color);}details.toggle>summary::after{content:"Expand";overflow:hidden;width:0;height:0;position:absolute;}details.toggle>summary.hideme::after{content:"";}details.toggle>summary:focus::before,details.toggle>summary:hover::before{opacity:1;}details.toggle>summary:focus-visible::before{outline:1px dotted #000;outline-offset:1px;}details.non-exhaustive{margin-bottom:8px;}details.toggle>summary.hideme::before{position:relative;}details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;top:4px;}.impl-items>details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;}details.toggle[open] >summary.hideme{position:absolute;}details.toggle[open] >summary.hideme>span{display:none;}details.toggle[open] >summary::before{background:url('data:image/svg+xml,') no-repeat top left;}details.toggle[open] >summary::after{content:"Collapse";}.docblock summary>*{display:inline-block;}.docblock>.example-wrap:first-child .tooltip{margin-top:16px;}.src #sidebar-button>a:before,.sidebar-menu-toggle:before{content:url('data:image/svg+xml,\ + ');opacity:0.75;}.sidebar-menu-toggle:hover:before,.sidebar-menu-toggle:active:before,.sidebar-menu-toggle:focus:before{opacity:1;}.src #sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');opacity:0.75;}@media (max-width:850px){#search-tabs .count{display:block;}}@media (max-width:700px){*[id]{scroll-margin-top:45px;}.rustdoc{display:block;}main{padding-left:15px;padding-top:0px;}.main-heading{flex-direction:column;}.out-of-band{text-align:left;margin-left:initial;padding:initial;}.out-of-band .since::before{content:"Since ";}.sidebar .logo-container,.sidebar .location,.sidebar-resizer{display:none;}.sidebar{position:fixed;top:45px;left:-1000px;z-index:11;height:calc(100vh - 45px);width:200px;}.src main,.rustdoc.src .sidebar{top:0;padding:0;height:100vh;border:0;}.src .search-form{margin-left:40px;}.hide-sidebar .search-form{margin-left:32px;}.hide-sidebar .src .search-form{margin-left:0;}.sidebar.shown,.src-sidebar-expanded .src .sidebar,.rustdoc:not(.src) .sidebar:focus-within{left:0;}.mobile-topbar h2{padding-bottom:0;margin:auto 0.5em auto auto;overflow:hidden;font-size:24px;white-space:nowrap;text-overflow:ellipsis;}.mobile-topbar .logo-container>img{max-width:35px;max-height:35px;margin:5px 0 5px 20px;}.mobile-topbar{display:flex;flex-direction:row;position:sticky;z-index:10;font-size:2rem;height:45px;width:100%;left:0;top:0;}.hide-sidebar .mobile-topbar{display:none;}.sidebar-menu-toggle{width:45px;border:none;line-height:0;}.hide-sidebar .sidebar-menu-toggle{display:none;}.sidebar-elems{margin-top:1em;}.anchor{display:none !important;}#main-content>details.toggle>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}#copy-path,#help-button{display:none;}#sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');width:22px;height:22px;}.sidebar-menu-toggle:before{filter:var(--mobile-sidebar-menu-filter);}.sidebar-menu-toggle:hover{background:var(--main-background-color);}.item-table,.item-row,.item-table>li,.item-table>li>div,.search-results>a,.search-results>a>div{display:block;}.search-results>a{padding:5px 0px;}.search-results>a>div.desc,.item-table>li>div.desc{padding-left:2em;}.search-results .result-name{display:block;}.search-results .result-name .typename{width:initial;margin-right:0;}.search-results .result-name .typename,.search-results .result-name .path{display:inline;}.src-sidebar-expanded .src .sidebar{position:fixed;max-width:100vw;width:100vw;}.src .src-sidebar-title{padding-top:0;}details.toggle:not(.top-doc)>summary{margin-left:10px;}.impl-items>details.toggle>summary:not(.hideme)::before,#main-content>details.toggle:not(.top-doc)>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}.impl-items>.item-info{margin-left:34px;}.src nav.sub{margin:0;padding:var(--nav-sub-mobile-padding);}}@media (min-width:701px){.scraped-example-title{position:absolute;z-index:10;background:var(--main-background-color);bottom:8px;right:5px;padding:2px 4px;box-shadow:0 0 4px var(--main-background-color);}.item-table>li>.item-name{width:33%;}.item-table>li>div{overflow-wrap:anywhere;}}@media print{nav.sidebar,nav.sub,.out-of-band,a.src,#copy-path,details.toggle[open] >summary::before,details.toggle>summary::before,details.toggle.top-doc>summary{display:none;}.docblock{margin-left:0;}main{padding:10px;}}@media (max-width:464px){.docblock{margin-left:12px;}.docblock code{overflow-wrap:break-word;overflow-wrap:anywhere;}nav.sub{flex-direction:column;}.search-form{align-self:stretch;}}.variant,.implementors-toggle>summary,.impl,#implementors-list>.docblock,.impl-items>section,.impl-items>.toggle>summary,.methods>section,.methods>.toggle>summary{margin-bottom:0.75em;}.variants>.docblock,.implementors-toggle>.docblock,.impl-items>.toggle[open]:not(:last-child),.methods>.toggle[open]:not(:last-child),.implementors-toggle[open]:not(:last-child){margin-bottom:2em;}#trait-implementations-list .impl-items>.toggle:not(:last-child),#synthetic-implementations-list .impl-items>.toggle:not(:last-child),#blanket-implementations-list .impl-items>.toggle:not(:last-child){margin-bottom:1em;}.scraped-example-list .scrape-help{margin-left:10px;padding:0 4px;font-weight:normal;font-size:12px;position:relative;bottom:1px;border:1px solid var(--scrape-example-help-border-color);border-radius:50px;color:var(--scrape-example-help-color);}.scraped-example-list .scrape-help:hover{border-color:var(--scrape-example-help-hover-border-color);color:var(--scrape-example-help-hover-color);}.scraped-example{position:relative;}.scraped-example .code-wrapper{position:relative;display:flex;flex-direction:row;flex-wrap:wrap;width:100%;}.scraped-example:not(.expanded) .code-wrapper{max-height:calc(1.5em * 5 + 10px);}.scraped-example:not(.expanded) .code-wrapper pre{overflow-y:hidden;padding-bottom:0;max-height:calc(1.5em * 5 + 10px);}.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper,.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper pre{max-height:calc(1.5em * 10 + 10px);}.scraped-example .code-wrapper .next,.scraped-example .code-wrapper .prev,.scraped-example .code-wrapper .expand{color:var(--main-color);position:absolute;top:0.25em;z-index:1;padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.scraped-example .code-wrapper .prev{right:2.25em;}.scraped-example .code-wrapper .next{right:1.25em;}.scraped-example .code-wrapper .expand{right:0.25em;}.scraped-example:not(.expanded) .code-wrapper::before,.scraped-example:not(.expanded) .code-wrapper::after{content:" ";width:100%;height:5px;position:absolute;z-index:1;}.scraped-example:not(.expanded) .code-wrapper::before{top:0;background:linear-gradient(to bottom,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example:not(.expanded) .code-wrapper::after{bottom:0;background:linear-gradient(to top,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example .code-wrapper .example-wrap{width:100%;overflow-y:hidden;margin-bottom:0;}.scraped-example:not(.expanded) .code-wrapper .example-wrap{overflow-x:hidden;}.scraped-example .example-wrap .rust span.highlight{background:var(--scrape-example-code-line-highlight);}.scraped-example .example-wrap .rust span.highlight.focus{background:var(--scrape-example-code-line-highlight-focus);}.more-examples-toggle{max-width:calc(100% + 25px);margin-top:10px;margin-left:-25px;}.more-examples-toggle .hide-more{margin-left:25px;cursor:pointer;}.more-scraped-examples{margin-left:25px;position:relative;}.toggle-line{position:absolute;top:5px;bottom:0;right:calc(100% + 10px);padding:0 4px;cursor:pointer;}.toggle-line-inner{min-width:2px;height:100%;background:var(--scrape-example-toggle-line-background);}.toggle-line:hover .toggle-line-inner{background:var(--scrape-example-toggle-line-hover-background);}.more-scraped-examples .scraped-example,.example-links{margin-top:20px;}.more-scraped-examples .scraped-example:first-child{margin-top:5px;}.example-links ul{margin-bottom:0;}:root[data-theme="light"],:root:not([data-theme]){--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--mobile-sidebar-menu-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--settings-menu-filter:none;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);--sidebar-resizer-hover:hsl(207,90%,66%);--sidebar-resizer-active:hsl(207,90%,54%);}:root[data-theme="dark"]{--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--settings-menu-filter:none;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);--sidebar-resizer-hover:hsl(207,30%,54%);--sidebar-resizer-active:hsl(207,90%,54%);}:root[data-theme="ayu"]{--main-background-color:#0f1419;--main-color:#c5c5c5;--settings-input-color:#ffb454;--settings-input-border-color:#999;--settings-button-color:#fff;--settings-button-border-focus:#e0e0e0;--sidebar-background-color:#14191f;--sidebar-background-color-hover:rgba(70,70,70,0.33);--code-block-background-color:#191f26;--scrollbar-track-background-color:transparent;--scrollbar-thumb-background-color:#5c6773;--scrollbar-color:#5c6773 #24292f;--headings-border-bottom-color:#5c6773;--border-color:#5c6773;--button-background-color:#141920;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#5c6773;--copy-path-button-color:#fff;--copy-path-img-filter:invert(70%);--copy-path-img-hover-filter:invert(100%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ffa0a5;--trait-link-color:#39afd7;--assoc-item-link-color:#39afd7;--function-link-color:#fdd687;--macro-link-color:#a37acc;--keyword-link-color:#39afd7;--mod-link-color:#39afd7;--link-color:#39afd7;--sidebar-link-color:#53b1db;--sidebar-current-link-background-color:transparent;--search-result-link-focus-background-color:#3c3c3c;--search-result-border-color:#aaa3;--search-color:#fff;--search-error-code-background-color:#4f4c4c;--search-results-alias-color:#c5c5c5;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:none;--search-tab-button-not-selected-background:transparent !important;--search-tab-button-selected-border-top-color:none;--search-tab-button-selected-background:#141920 !important;--settings-menu-filter:invert(100%);--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ff7733;--code-highlight-kw-2-color:#ff7733;--code-highlight-lifetime-color:#ff7733;--code-highlight-prelude-color:#69f2df;--code-highlight-prelude-val-color:#ff7733;--code-highlight-number-color:#b8cc52;--code-highlight-string-color:#b8cc52;--code-highlight-literal-color:#ff7733;--code-highlight-attribute-color:#e6e1cf;--code-highlight-self-color:#36a3d9;--code-highlight-macro-color:#a37acc;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#788797;--code-highlight-doc-comment-color:#a1ac88;--src-line-numbers-span-color:#5c6773;--src-line-number-highlighted-background-color:rgba(255,236,164,0.06);--target-background-color:rgba(255,236,164,0.06);--target-border-color:rgba(255,180,76,0.85);--kbd-color:#c5c5c5;--kbd-background:#314559;--kbd-box-shadow-color:#5c6773;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg) brightness(94%) contrast(94%);--crate-search-div-hover-filter:invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg) brightness(113%) contrast(76%);--crate-search-hover-border:#e0e0e0;--src-sidebar-background-selected:#14191f;--src-sidebar-background-hover:#14191f;--table-alt-row-background-color:#191f26;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(15,20,25,1);--scrape-example-code-wrapper-background-end:rgba(15,20,25,0);--sidebar-resizer-hover:hsl(34,50%,33%);--sidebar-resizer-active:hsl(34,100%,66%);}:root[data-theme="ayu"] h1,:root[data-theme="ayu"] h2,:root[data-theme="ayu"] h3,:root[data-theme="ayu"] h4,:where(:root[data-theme="ayu"]) h1 a,:root[data-theme="ayu"] .sidebar h2 a,:root[data-theme="ayu"] .sidebar h3 a{color:#fff;}:root[data-theme="ayu"] .docblock code{color:#ffb454;}:root[data-theme="ayu"] .docblock a>code{color:#39AFD7 !important;}:root[data-theme="ayu"] .code-header,:root[data-theme="ayu"] .docblock pre>code,:root[data-theme="ayu"] pre,:root[data-theme="ayu"] pre>code,:root[data-theme="ayu"] .item-info code,:root[data-theme="ayu"] .rustdoc.source .example-wrap{color:#e6e1cf;}:root[data-theme="ayu"] .sidebar .current,:root[data-theme="ayu"] .sidebar .current a,:root[data-theme="ayu"] .sidebar a:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:hover,:root[data-theme="ayu"] details.dir-entry summary:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:focus,:root[data-theme="ayu"] details.dir-entry summary:focus,:root[data-theme="ayu"] #src-sidebar div.files>a.selected{color:#ffb44c;}:root[data-theme="ayu"] .sidebar-elems .location{color:#ff7733;}:root[data-theme="ayu"] .src-line-numbers .line-highlighted{color:#708090;padding-right:7px;border-right:1px solid #ffb44c;}:root[data-theme="ayu"] .search-results a:hover,:root[data-theme="ayu"] .search-results a:focus{color:#fff !important;background-color:#3c3c3c;}:root[data-theme="ayu"] .search-results a{color:#0096cf;}:root[data-theme="ayu"] .search-results a div.desc{color:#c5c5c5;}:root[data-theme="ayu"] .result-name .primitive>i,:root[data-theme="ayu"] .result-name .keyword>i{color:#788797;}:root[data-theme="ayu"] #search-tabs>button.selected{border-bottom:1px solid #ffb44c !important;border-top:none;}:root[data-theme="ayu"] #search-tabs>button:not(.selected){border:none;background-color:transparent !important;}:root[data-theme="ayu"] #search-tabs>button:hover{border-bottom:1px solid rgba(242,151,24,0.3);}:root[data-theme="ayu"] #settings-menu>a img,:root[data-theme="ayu"] #sidebar-button>a:before{filter:invert(100);} \ No newline at end of file diff --git a/doc/static.files/scrape-examples-ef1e698c1d417c0c.js b/doc/static.files/scrape-examples-ef1e698c1d417c0c.js new file mode 100644 index 00000000..ba830e37 --- /dev/null +++ b/doc/static.files/scrape-examples-ef1e698c1d417c0c.js @@ -0,0 +1 @@ +"use strict";(function(){const DEFAULT_MAX_LINES=5;const HIDDEN_MAX_LINES=10;function scrollToLoc(elt,loc,isHidden){const lines=elt.querySelector(".src-line-numbers");let scrollOffset;const maxLines=isHidden?HIDDEN_MAX_LINES:DEFAULT_MAX_LINES;if(loc[1]-loc[0]>maxLines){const line=Math.max(0,loc[0]-1);scrollOffset=lines.children[line].offsetTop}else{const wrapper=elt.querySelector(".code-wrapper");const halfHeight=wrapper.offsetHeight/2;const offsetTop=lines.children[loc[0]].offsetTop;const lastLine=lines.children[loc[1]];const offsetBot=lastLine.offsetTop+lastLine.offsetHeight;const offsetMid=(offsetTop+offsetBot)/2;scrollOffset=offsetMid-halfHeight}lines.scrollTo(0,scrollOffset);elt.querySelector(".rust").scrollTo(0,scrollOffset)}function updateScrapedExample(example,isHidden){const locs=JSON.parse(example.attributes.getNamedItem("data-locs").textContent);let locIndex=0;const highlights=Array.prototype.slice.call(example.querySelectorAll(".highlight"));const link=example.querySelector(".scraped-example-title a");if(locs.length>1){const onChangeLoc=changeIndex=>{removeClass(highlights[locIndex],"focus");changeIndex();scrollToLoc(example,locs[locIndex][0],isHidden);addClass(highlights[locIndex],"focus");const url=locs[locIndex][1];const title=locs[locIndex][2];link.href=url;link.innerHTML=title};example.querySelector(".prev").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex-1+locs.length)%locs.length})});example.querySelector(".next").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex+1)%locs.length})})}const expandButton=example.querySelector(".expand");if(expandButton){expandButton.addEventListener("click",()=>{if(hasClass(example,"expanded")){removeClass(example,"expanded");scrollToLoc(example,locs[0][0],isHidden)}else{addClass(example,"expanded")}})}scrollToLoc(example,locs[0][0],isHidden)}const firstExamples=document.querySelectorAll(".scraped-example-list > .scraped-example");onEachLazy(firstExamples,el=>updateScrapedExample(el,false));onEachLazy(document.querySelectorAll(".more-examples-toggle"),toggle=>{onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"),button=>{button.addEventListener("click",()=>{toggle.open=false})});const moreExamples=toggle.querySelectorAll(".scraped-example");toggle.querySelector("summary").addEventListener("click",()=>{setTimeout(()=>{onEachLazy(moreExamples,el=>updateScrapedExample(el,true))})},{once:true})})})() \ No newline at end of file diff --git a/doc/static.files/search-a2a4ff0acfd716f8.js b/doc/static.files/search-a2a4ff0acfd716f8.js new file mode 100644 index 00000000..01adaf78 --- /dev/null +++ b/doc/static.files/search-a2a4ff0acfd716f8.js @@ -0,0 +1,5 @@ +"use strict";if(!Array.prototype.toSpliced){Array.prototype.toSpliced=function(){const me=this.slice();Array.prototype.splice.apply(me,arguments);return me}}(function(){const itemTypes=["keyword","primitive","mod","externcrate","import","struct","enum","fn","type","static","trait","impl","tymethod","method","structfield","variant","macro","associatedtype","constant","associatedconstant","union","foreigntype","existential","attr","derive","traitalias","generic",];const longItemTypes=["keyword","primitive type","module","extern crate","re-export","struct","enum","function","type alias","static","trait","","trait method","method","struct field","enum variant","macro","assoc type","constant","assoc const","union","foreign type","existential type","attribute macro","derive macro","trait alias",];const TY_GENERIC=itemTypes.indexOf("generic");const TY_IMPORT=itemTypes.indexOf("import");const ROOT_PATH=typeof window!=="undefined"?window.rootPath:"../";const UNBOXING_LIMIT=5;const REGEX_IDENT=/\p{ID_Start}\p{ID_Continue}*|_\p{ID_Continue}+/uy;const REGEX_INVALID_TYPE_FILTER=/[^a-z]/ui;function printTab(nb){let iter=0;let foundCurrentTab=false;let foundCurrentResultSet=false;onEachLazy(document.getElementById("search-tabs").childNodes,elem=>{if(nb===iter){addClass(elem,"selected");foundCurrentTab=true}else{removeClass(elem,"selected")}iter+=1});const isTypeSearch=(nb>0||iter===1);iter=0;onEachLazy(document.getElementById("results").childNodes,elem=>{if(nb===iter){addClass(elem,"active");foundCurrentResultSet=true}else{removeClass(elem,"active")}iter+=1});if(foundCurrentTab&&foundCurrentResultSet){searchState.currentTab=nb;const correctionsElem=document.getElementsByClassName("search-corrections");if(isTypeSearch){removeClass(correctionsElem[0],"hidden")}else{addClass(correctionsElem[0],"hidden")}}else if(nb!==0){printTab(0)}}const editDistanceState={current:[],prev:[],prevPrev:[],calculate:function calculate(a,b,limit){if(a.lengthlimit){return limit+1}while(b.length>0&&b[0]===a[0]){a=a.substring(1);b=b.substring(1)}while(b.length>0&&b[b.length-1]===a[a.length-1]){a=a.substring(0,a.length-1);b=b.substring(0,b.length-1)}if(b.length===0){return minDist}const aLength=a.length;const bLength=b.length;for(let i=0;i<=bLength;++i){this.current[i]=0;this.prev[i]=i;this.prevPrev[i]=Number.MAX_VALUE}for(let i=1;i<=aLength;++i){this.current[0]=i;const aIdx=i-1;for(let j=1;j<=bLength;++j){const bIdx=j-1;const substitutionCost=a[aIdx]===b[bIdx]?0:1;this.current[j]=Math.min(this.prev[j]+1,this.current[j-1]+1,this.prev[j-1]+substitutionCost,);if((i>1)&&(j>1)&&(a[aIdx]===b[bIdx-1])&&(a[aIdx-1]===b[bIdx])){this.current[j]=Math.min(this.current[j],this.prevPrev[j-2]+1,)}}const prevPrevTmp=this.prevPrev;this.prevPrev=this.prev;this.prev=this.current;this.current=prevPrevTmp}const distance=this.prev[bLength];return distance<=limit?distance:(limit+1)},};function editDistance(a,b,limit){return editDistanceState.calculate(a,b,limit)}function initSearch(rawSearchIndex){const MAX_RESULTS=200;const NO_TYPE_FILTER=-1;let searchIndex;let searchIndexDeprecated;let searchIndexEmptyDesc;let functionTypeFingerprint;let currentResults;const typeNameIdMap=new Map();const ALIASES=new Map();const typeNameIdOfArray=buildTypeMapIndex("array");const typeNameIdOfSlice=buildTypeMapIndex("slice");const typeNameIdOfArrayOrSlice=buildTypeMapIndex("[]");const typeNameIdOfTuple=buildTypeMapIndex("tuple");const typeNameIdOfUnit=buildTypeMapIndex("unit");const typeNameIdOfTupleOrUnit=buildTypeMapIndex("()");const typeNameIdOfFn=buildTypeMapIndex("fn");const typeNameIdOfFnMut=buildTypeMapIndex("fnmut");const typeNameIdOfFnOnce=buildTypeMapIndex("fnonce");const typeNameIdOfHof=buildTypeMapIndex("->");function buildTypeMapIndex(name,isAssocType){if(name===""||name===null){return null}if(typeNameIdMap.has(name)){const obj=typeNameIdMap.get(name);obj.assocOnly=isAssocType&&obj.assocOnly;return obj.id}else{const id=typeNameIdMap.size;typeNameIdMap.set(name,{id,assocOnly:isAssocType});return id}}function isSpecialStartCharacter(c){return"<\"".indexOf(c)!==-1}function isEndCharacter(c){return"=,>-])".indexOf(c)!==-1}function itemTypeFromName(typename){const index=itemTypes.findIndex(i=>i===typename);if(index<0){throw["Unknown type filter ",typename]}return index}function getStringElem(query,parserState,isInGenerics){if(isInGenerics){throw["Unexpected ","\""," in generics"]}else if(query.literalSearch){throw["Cannot have more than one literal search element"]}else if(parserState.totalElems-parserState.genericsElems>0){throw["Cannot use literal search when there is more than one element"]}parserState.pos+=1;const start=parserState.pos;const end=getIdentEndPosition(parserState);if(parserState.pos>=parserState.length){throw["Unclosed ","\""]}else if(parserState.userQuery[end]!=="\""){throw["Unexpected ",parserState.userQuery[end]," in a string element"]}else if(start===end){throw["Cannot have empty string element"]}parserState.pos+=1;query.literalSearch=true}function isPathStart(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="::"}function isReturnArrow(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="->"}function consumeIdent(parserState){REGEX_IDENT.lastIndex=parserState.pos;const match=parserState.userQuery.match(REGEX_IDENT);if(match){parserState.pos+=match[0].length;return true}return false}function isSeparatorCharacter(c){return c===","||c==="="}function isPathSeparator(c){return c===":"||c===" "}function prevIs(parserState,lookingFor){let pos=parserState.pos;while(pos>0){const c=parserState.userQuery[pos-1];if(c===lookingFor){return true}else if(c!==" "){break}pos-=1}return false}function isLastElemGeneric(elems,parserState){return(elems.length>0&&elems[elems.length-1].generics.length>0)||prevIs(parserState,">")}function skipWhitespace(parserState){while(parserState.pos0){throw["Cannot have more than one element if you use quotes"]}const typeFilter=parserState.typeFilter;parserState.typeFilter=null;if(name==="!"){if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive never type ","!"," and ",typeFilter," both specified",]}if(generics.length!==0){throw["Never type ","!"," does not accept generic parameters",]}const bindingName=parserState.isInBinding;parserState.isInBinding=null;return makePrimitiveElement("never",{bindingName})}const quadcolon=/::\s*::/.exec(path);if(path.startsWith("::")){throw["Paths cannot start with ","::"]}else if(path.endsWith("::")){throw["Paths cannot end with ","::"]}else if(quadcolon!==null){throw["Unexpected ",quadcolon[0]]}const pathSegments=path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/);if(pathSegments.length===0||(pathSegments.length===1&&pathSegments[0]==="")){if(generics.length>0||prevIs(parserState,">")){throw["Found generics without a path"]}else{throw["Unexpected ",parserState.userQuery[parserState.pos]]}}for(const[i,pathSegment]of pathSegments.entries()){if(pathSegment==="!"){if(i!==0){throw["Never type ","!"," is not associated item"]}pathSegments[i]="never"}}parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1}const bindingName=parserState.isInBinding;parserState.isInBinding=null;const bindings=new Map();const pathLast=pathSegments[pathSegments.length-1];return{name:name.trim(),id:null,fullPath:pathSegments,pathWithoutLast:pathSegments.slice(0,pathSegments.length-1),pathLast,normalizedPathLast:pathLast.replace(/_/g,""),generics:generics.filter(gen=>{if(gen.bindingName!==null){if(gen.name!==null){gen.bindingName.generics.unshift(gen)}bindings.set(gen.bindingName.name,gen.bindingName.generics);return false}return true}),bindings,typeFilter,bindingName,}}function getIdentEndPosition(parserState){let afterIdent=consumeIdent(parserState);let end=parserState.pos;let macroExclamation=-1;while(parserState.pos0){throw["Unexpected ",c," after ",parserState.userQuery[parserState.pos-1]," (not a valid identifier)"]}else{throw["Unexpected ",c," (not a valid identifier)"]}parserState.pos+=1;afterIdent=consumeIdent(parserState);end=parserState.pos}if(macroExclamation!==-1){if(parserState.typeFilter===null){parserState.typeFilter="macro"}else if(parserState.typeFilter!=="macro"){throw["Invalid search type: macro ","!"," and ",parserState.typeFilter," both specified",]}end=macroExclamation}return end}function getFilteredNextElem(query,parserState,elems,isInGenerics){const start=parserState.pos;if(parserState.userQuery[parserState.pos]===":"&&!isPathStart(parserState)){throw["Expected type filter before ",":"]}getNextElem(query,parserState,elems,isInGenerics);if(parserState.userQuery[parserState.pos]===":"&&!isPathStart(parserState)){if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}if(elems.length===0){throw["Expected type filter before ",":"]}else if(query.literalSearch){throw["Cannot use quotes on type filter"]}const typeFilterElem=elems.pop();checkExtraTypeFilterCharacters(start,parserState);parserState.typeFilter=typeFilterElem.name;parserState.pos+=1;parserState.totalElems-=1;query.literalSearch=false;getNextElem(query,parserState,elems,isInGenerics)}}function getNextElem(query,parserState,elems,isInGenerics){const generics=[];skipWhitespace(parserState);let start=parserState.pos;let end;if("[(".indexOf(parserState.userQuery[parserState.pos])!==-1){let endChar=")";let name="()";let friendlyName="tuple";if(parserState.userQuery[parserState.pos]==="["){endChar="]";name="[]";friendlyName="slice"}parserState.pos+=1;const{foundSeparator}=getItemsBefore(query,parserState,generics,endChar);const typeFilter=parserState.typeFilter;const bindingName=parserState.isInBinding;parserState.typeFilter=null;parserState.isInBinding=null;for(const gen of generics){if(gen.bindingName!==null){throw["Type parameter ","=",` cannot be within ${friendlyName} `,name]}}if(name==="()"&&!foundSeparator&&generics.length===1&&typeFilter===null){elems.push(generics[0])}else if(name==="()"&&generics.length===1&&generics[0].name==="->"){generics[0].typeFilter=typeFilter;elems.push(generics[0])}else{if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive ",name," and ",typeFilter," both specified",]}parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1}elems.push(makePrimitiveElement(name,{bindingName,generics}))}}else if(parserState.userQuery[parserState.pos]==="&"){if(parserState.typeFilter!==null&&parserState.typeFilter!=="primitive"){throw["Invalid search type: primitive ","&"," and ",parserState.typeFilter," both specified",]}parserState.typeFilter=null;parserState.pos+=1;let c=parserState.userQuery[parserState.pos];while(c===" "&&parserState.pos=end){throw["Found generics without a path"]}parserState.pos+=1;getItemsBefore(query,parserState,generics,">")}else if(parserState.pos=end){throw["Found generics without a path"]}if(parserState.isInBinding){throw["Unexpected ","("," after ","="]}parserState.pos+=1;const typeFilter=parserState.typeFilter;parserState.typeFilter=null;getItemsBefore(query,parserState,generics,")");skipWhitespace(parserState);if(isReturnArrow(parserState)){parserState.pos+=2;skipWhitespace(parserState);getFilteredNextElem(query,parserState,generics,isInGenerics);generics[generics.length-1].bindingName=makePrimitiveElement("output")}else{generics.push(makePrimitiveElement(null,{bindingName:makePrimitiveElement("output"),typeFilter:null,}))}parserState.typeFilter=typeFilter}if(isStringElem){skipWhitespace(parserState)}if(start>=end&&generics.length===0){return}if(parserState.userQuery[parserState.pos]==="="){if(parserState.isInBinding){throw["Cannot write ","="," twice in a binding"]}if(!isInGenerics){throw["Type parameter ","="," must be within generics list"]}const name=parserState.userQuery.slice(start,end).trim();if(name==="!"){throw["Type parameter ","="," key cannot be ","!"," never type"]}if(name.includes("!")){throw["Type parameter ","="," key cannot be ","!"," macro"]}if(name.includes("::")){throw["Type parameter ","="," key cannot contain ","::"," path"]}if(name.includes(":")){throw["Type parameter ","="," key cannot contain ",":"," type"]}parserState.isInBinding={name,generics}}else{elems.push(createQueryElement(query,parserState,parserState.userQuery.slice(start,end),generics,isInGenerics,),)}}}function getItemsBefore(query,parserState,elems,endChar){let foundStopChar=true;let foundSeparator=false;const oldTypeFilter=parserState.typeFilter;parserState.typeFilter=null;const oldIsInBinding=parserState.isInBinding;parserState.isInBinding=null;let hofParameters=null;let extra="";if(endChar===">"){extra="<"}else if(endChar==="]"){extra="["}else if(endChar===")"){extra="("}else if(endChar===""){extra="->"}else{extra=endChar}while(parserState.pos"," after ","="]}hofParameters=[...elems];elems.length=0;parserState.pos+=2;foundStopChar=true;foundSeparator=false;continue}else if(c===" "){parserState.pos+=1;continue}else if(isSeparatorCharacter(c)){parserState.pos+=1;foundStopChar=true;foundSeparator=true;continue}else if(c===":"&&isPathStart(parserState)){throw["Unexpected ","::",": paths cannot start with ","::"]}else if(isEndCharacter(c)){throw["Unexpected ",c," after ",extra]}if(!foundStopChar){let extra=[];if(isLastElemGeneric(query.elems,parserState)){extra=[" after ",">"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(endChar!==""){throw["Expected ",",",", ","=",", or ",endChar,...extra,", found ",c,]}throw["Expected ",","," or ","=",...extra,", found ",c,]}const posBefore=parserState.pos;getFilteredNextElem(query,parserState,elems,endChar!=="");if(endChar!==""&&parserState.pos>=parserState.length){throw["Unclosed ",extra]}if(posBefore===parserState.pos){parserState.pos+=1}foundStopChar=false}if(parserState.pos>=parserState.length&&endChar!==""){throw["Unclosed ",extra]}parserState.pos+=1;if(hofParameters){foundSeparator=false;if([...elems,...hofParameters].some(x=>x.bindingName)||parserState.isInBinding){throw["Unexpected ","="," within ","->"]}const hofElem=makePrimitiveElement("->",{generics:hofParameters,bindings:new Map([["output",[...elems]]]),typeFilter:null,});elems.length=0;elems[0]=hofElem}parserState.typeFilter=oldTypeFilter;parserState.isInBinding=oldIsInBinding;return{foundSeparator}}function checkExtraTypeFilterCharacters(start,parserState){const query=parserState.userQuery.slice(start,parserState.pos).trim();const match=query.match(REGEX_INVALID_TYPE_FILTER);if(match){throw["Unexpected ",match[0]," in type filter (before ",":",")",]}}function parseInput(query,parserState){let foundStopChar=true;while(parserState.pos"){if(isReturnArrow(parserState)){break}throw["Unexpected ",c," (did you mean ","->","?)"]}else if(parserState.pos>0){throw["Unexpected ",c," after ",parserState.userQuery[parserState.pos-1]]}throw["Unexpected ",c]}else if(c===" "){skipWhitespace(parserState);continue}if(!foundStopChar){let extra="";if(isLastElemGeneric(query.elems,parserState)){extra=[" after ",">"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(parserState.typeFilter!==null){throw["Expected ",","," or ","->",...extra,", found ",c,]}throw["Expected ",",",", ",":"," or ","->",...extra,", found ",c,]}const before=query.elems.length;getFilteredNextElem(query,parserState,query.elems,false);if(query.elems.length===before){parserState.pos+=1}foundStopChar=false}if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}while(parserState.pos"]}break}else{parserState.pos+=1}}}function newParsedQuery(userQuery){return{original:userQuery,userQuery:userQuery.toLowerCase(),elems:[],returned:[],foundElems:0,totalElems:0,literalSearch:false,error:null,correction:null,proposeCorrectionFrom:null,proposeCorrectionTo:null,typeFingerprint:new Uint32Array(4),}}function buildUrl(search,filterCrates){let extra="?search="+encodeURIComponent(search);if(filterCrates!==null){extra+="&filter-crate="+encodeURIComponent(filterCrates)}return getNakedUrl()+extra+window.location.hash}function getFilterCrates(){const elem=document.getElementById("crate-search");if(elem&&elem.value!=="all crates"&&rawSearchIndex.has(elem.value)){return elem.value}return null}function parseQuery(userQuery){function convertTypeFilterOnElem(elem){if(elem.typeFilter!==null){let typeFilter=elem.typeFilter;if(typeFilter==="const"){typeFilter="constant"}elem.typeFilter=itemTypeFromName(typeFilter)}else{elem.typeFilter=NO_TYPE_FILTER}for(const elem2 of elem.generics){convertTypeFilterOnElem(elem2)}for(const constraints of elem.bindings.values()){for(const constraint of constraints){convertTypeFilterOnElem(constraint)}}}userQuery=userQuery.trim().replace(/\r|\n|\t/g," ");const parserState={length:userQuery.length,pos:0,totalElems:0,genericsElems:0,typeFilter:null,isInBinding:null,userQuery:userQuery.toLowerCase(),};let query=newParsedQuery(userQuery);try{parseInput(query,parserState);for(const elem of query.elems){convertTypeFilterOnElem(elem)}for(const elem of query.returned){convertTypeFilterOnElem(elem)}}catch(err){query=newParsedQuery(userQuery);query.error=err;return query}if(!query.literalSearch){query.literalSearch=parserState.totalElems>1}query.foundElems=query.elems.length+query.returned.length;query.totalElems=parserState.totalElems;return query}function createQueryResults(results_in_args,results_returned,results_others,parsedQuery){return{"in_args":results_in_args,"returned":results_returned,"others":results_others,"query":parsedQuery,}}async function execQuery(parsedQuery,filterCrates,currentCrate){const results_others=new Map(),results_in_args=new Map(),results_returned=new Map();function transformResults(results){const duplicates=new Set();const out=[];for(const result of results){if(result.id!==-1){const obj=searchIndex[result.id];obj.dist=result.dist;const res=buildHrefAndPath(obj);obj.displayPath=pathSplitter(res[0]);obj.fullPath=res[2]+"|"+obj.ty;if(duplicates.has(obj.fullPath)){continue}if(obj.ty===TY_IMPORT&&duplicates.has(res[2])){continue}if(duplicates.has(res[2]+"|"+TY_IMPORT)){continue}duplicates.add(obj.fullPath);duplicates.add(res[2]);obj.href=res[1];out.push(obj);if(out.length>=MAX_RESULTS){break}}}return out}async function sortResults(results,isType,preferredCrate){const userQuery=parsedQuery.userQuery;const result_list=[];for(const result of results.values()){result.item=searchIndex[result.id];result.word=searchIndex[result.id].word;result_list.push(result)}result_list.sort((aaa,bbb)=>{let a,b;a=(aaa.word!==userQuery);b=(bbb.word!==userQuery);if(a!==b){return a-b}a=(aaa.index<0);b=(bbb.index<0);if(a!==b){return a-b}a=aaa.path_dist;b=bbb.path_dist;if(a!==b){return a-b}a=aaa.index;b=bbb.index;if(a!==b){return a-b}a=(aaa.dist);b=(bbb.dist);if(a!==b){return a-b}a=searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex);b=searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex);if(a!==b){return a-b}a=(aaa.item.crate!==preferredCrate);b=(bbb.item.crate!==preferredCrate);if(a!==b){return a-b}a=aaa.word.length;b=bbb.word.length;if(a!==b){return a-b}a=aaa.word;b=bbb.word;if(a!==b){return(a>b?+1:-1)}a=searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex);b=searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex);if(a!==b){return a-b}a=aaa.item.ty;b=bbb.item.ty;if(a!==b){return a-b}a=aaa.item.path;b=bbb.item.path;if(a!==b){return(a>b?+1:-1)}return 0});return transformResults(result_list)}function unifyFunctionTypes(fnTypesIn,queryElems,whereClause,mgensIn,solutionCb,unboxingDepth,){if(unboxingDepth>=UNBOXING_LIMIT){return false}const mgens=mgensIn===null?null:new Map(mgensIn);if(queryElems.length===0){return!solutionCb||solutionCb(mgens)}if(!fnTypesIn||fnTypesIn.length===0){return false}const ql=queryElems.length;const fl=fnTypesIn.length;if(ql===1&&queryElems[0].generics.length===0&&queryElems[0].bindings.size===0){const queryElem=queryElems[0];for(const fnType of fnTypesIn){if(!unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgens)){continue}if(fnType.id<0&&queryElem.id<0){if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==queryElem.id){continue}const mgensScratch=new Map(mgens);mgensScratch.set(fnType.id,queryElem.id);if(!solutionCb||solutionCb(mgensScratch)){return true}}else if(!solutionCb||solutionCb(mgens?new Map(mgens):null)){return true}}for(const fnType of fnTypesIn){if(!unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth+1,)){continue}if(fnType.id<0){if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){continue}const mgensScratch=new Map(mgens);mgensScratch.set(fnType.id,0);if(unifyFunctionTypes(whereClause[(-fnType.id)-1],queryElems,whereClause,mgensScratch,solutionCb,unboxingDepth+1,)){return true}}else if(unifyFunctionTypes([...fnType.generics,...Array.from(fnType.bindings.values()).flat()],queryElems,whereClause,mgens?new Map(mgens):null,solutionCb,unboxingDepth+1,)){return true}}return false}const fnTypes=fnTypesIn.slice();const flast=fl-1;const qlast=ql-1;const queryElem=queryElems[qlast];let queryElemsTmp=null;for(let i=flast;i>=0;i-=1){const fnType=fnTypes[i];if(!unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgens)){continue}let mgensScratch;if(fnType.id<0){mgensScratch=new Map(mgens);if(mgensScratch.has(fnType.id)&&mgensScratch.get(fnType.id)!==queryElem.id){continue}mgensScratch.set(fnType.id,queryElem.id)}else{mgensScratch=mgens}fnTypes[i]=fnTypes[flast];fnTypes.length=flast;if(!queryElemsTmp){queryElemsTmp=queryElems.slice(0,qlast)}const passesUnification=unifyFunctionTypes(fnTypes,queryElemsTmp,whereClause,mgensScratch,mgensScratch=>{if(fnType.generics.length===0&&queryElem.generics.length===0&&fnType.bindings.size===0&&queryElem.bindings.size===0){return!solutionCb||solutionCb(mgensScratch)}const solution=unifyFunctionTypeCheckBindings(fnType,queryElem,whereClause,mgensScratch,unboxingDepth,);if(!solution){return false}const simplifiedGenerics=solution.simplifiedGenerics;for(const simplifiedMgens of solution.mgens){const passesUnification=unifyFunctionTypes(simplifiedGenerics,queryElem.generics,whereClause,simplifiedMgens,solutionCb,unboxingDepth,);if(passesUnification){return true}}return false},unboxingDepth,);if(passesUnification){return true}fnTypes[flast]=fnTypes[i];fnTypes[i]=fnType;fnTypes.length=fl}for(let i=flast;i>=0;i-=1){const fnType=fnTypes[i];if(!unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth+1,)){continue}let mgensScratch;if(fnType.id<0){mgensScratch=new Map(mgens);if(mgensScratch.has(fnType.id)&&mgensScratch.get(fnType.id)!==0){continue}mgensScratch.set(fnType.id,0)}else{mgensScratch=mgens}const generics=fnType.id<0?whereClause[(-fnType.id)-1]:fnType.generics;const bindings=fnType.bindings?Array.from(fnType.bindings.values()).flat():[];const passesUnification=unifyFunctionTypes(fnTypes.toSpliced(i,1,...generics,...bindings),queryElems,whereClause,mgensScratch,solutionCb,unboxingDepth+1,);if(passesUnification){return true}}return false}function unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgensIn){if(!typePassesFilter(queryElem.typeFilter,fnType.ty)){return false}if(fnType.id<0&&queryElem.id<0){if(mgensIn){if(mgensIn.has(fnType.id)&&mgensIn.get(fnType.id)!==queryElem.id){return false}for(const[fid,qid]of mgensIn.entries()){if(fnType.id!==fid&&queryElem.id===qid){return false}if(fnType.id===fid&&queryElem.id!==qid){return false}}}return true}else{if(queryElem.id===typeNameIdOfArrayOrSlice&&(fnType.id===typeNameIdOfSlice||fnType.id===typeNameIdOfArray)){}else if(queryElem.id===typeNameIdOfTupleOrUnit&&(fnType.id===typeNameIdOfTuple||fnType.id===typeNameIdOfUnit)){}else if(queryElem.id===typeNameIdOfHof&&(fnType.id===typeNameIdOfFn||fnType.id===typeNameIdOfFnMut||fnType.id===typeNameIdOfFnOnce)){}else if(fnType.id!==queryElem.id||queryElem.id===null){return false}if((fnType.generics.length+fnType.bindings.size)===0&&queryElem.generics.length!==0){return false}if(fnType.bindings.size0){const fnTypePath=fnType.path!==undefined&&fnType.path!==null?fnType.path.split("::"):[];if(queryElemPathLength>fnTypePath.length){return false}let i=0;for(const path of fnTypePath){if(path===queryElem.pathWithoutLast[i]){i+=1;if(i>=queryElemPathLength){break}}}if(i0){let mgensSolutionSet=[mgensIn];for(const[name,constraints]of queryElem.bindings.entries()){if(mgensSolutionSet.length===0){return false}if(!fnType.bindings.has(name)){return false}const fnTypeBindings=fnType.bindings.get(name);mgensSolutionSet=mgensSolutionSet.flatMap(mgens=>{const newSolutions=[];unifyFunctionTypes(fnTypeBindings,constraints,whereClause,mgens,newMgens=>{newSolutions.push(newMgens);return false},unboxingDepth,);return newSolutions})}if(mgensSolutionSet.length===0){return false}const binds=Array.from(fnType.bindings.entries()).flatMap(entry=>{const[name,constraints]=entry;if(queryElem.bindings.has(name)){return[]}else{return constraints}});if(simplifiedGenerics.length>0){simplifiedGenerics=[...simplifiedGenerics,...binds]}else{simplifiedGenerics=binds}return{simplifiedGenerics,mgens:mgensSolutionSet}}return{simplifiedGenerics,mgens:[mgensIn]}}function unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth,){if(unboxingDepth>=UNBOXING_LIMIT){return false}if(fnType.id<0&&queryElem.id>=0){if(!whereClause){return false}if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){return false}const mgensTmp=new Map(mgens);mgensTmp.set(fnType.id,null);return checkIfInList(whereClause[(-fnType.id)-1],queryElem,whereClause,mgensTmp,unboxingDepth,)}else if(fnType.generics.length>0||fnType.bindings.size>0){const simplifiedGenerics=[...fnType.generics,...Array.from(fnType.bindings.values()).flat(),];return checkIfInList(simplifiedGenerics,queryElem,whereClause,mgens,unboxingDepth,)}return false}function checkIfInList(list,elem,whereClause,mgens,unboxingDepth){for(const entry of list){if(checkType(entry,elem,whereClause,mgens,unboxingDepth)){return true}}return false}function checkType(row,elem,whereClause,mgens,unboxingDepth){if(unboxingDepth>=UNBOXING_LIMIT){return false}if(row.bindings.size===0&&elem.bindings.size===0){if(elem.id<0&&mgens===null){return row.id<0||checkIfInList(row.generics,elem,whereClause,mgens,unboxingDepth+1,)}if(row.id>0&&elem.id>0&&elem.pathWithoutLast.length===0&&typePassesFilter(elem.typeFilter,row.ty)&&elem.generics.length===0&&elem.id!==typeNameIdOfArrayOrSlice&&elem.id!==typeNameIdOfTupleOrUnit&&elem.id!==typeNameIdOfHof){return row.id===elem.id||checkIfInList(row.generics,elem,whereClause,mgens,unboxingDepth,)}}return unifyFunctionTypes([row],[elem],whereClause,mgens,null,unboxingDepth)}function checkPath(contains,ty){if(contains.length===0){return 0}const maxPathEditDistance=Math.floor(contains.reduce((acc,next)=>acc+next.length,0)/3,);let ret_dist=maxPathEditDistance+1;const path=ty.path.split("::");if(ty.parent&&ty.parent.name){path.push(ty.parent.name.toLowerCase())}const length=path.length;const clength=contains.length;pathiter:for(let i=length-clength;i>=0;i-=1){let dist_total=0;for(let x=0;xmaxPathEditDistance){continue pathiter}dist_total+=dist}}ret_dist=Math.min(ret_dist,Math.round(dist_total/clength))}return ret_dist>maxPathEditDistance?null:ret_dist}function typePassesFilter(filter,type){if(filter<=NO_TYPE_FILTER||filter===type)return true;const name=itemTypes[type];switch(itemTypes[filter]){case"constant":return name==="associatedconstant";case"fn":return name==="method"||name==="tymethod";case"type":return name==="primitive"||name==="associatedtype";case"trait":return name==="traitalias"}return false}function createAliasFromItem(item){return{crate:item.crate,name:item.name,path:item.path,descShard:item.descShard,descIndex:item.descIndex,exactPath:item.exactPath,ty:item.ty,parent:item.parent,type:item.type,is_alias:true,bitIndex:item.bitIndex,implDisambiguator:item.implDisambiguator,}}async function handleAliases(ret,query,filterCrates,currentCrate){const lowerQuery=query.toLowerCase();const aliases=[];const crateAliases=[];if(filterCrates!==null){if(ALIASES.has(filterCrates)&&ALIASES.get(filterCrates).has(lowerQuery)){const query_aliases=ALIASES.get(filterCrates).get(lowerQuery);for(const alias of query_aliases){aliases.push(createAliasFromItem(searchIndex[alias]))}}}else{for(const[crate,crateAliasesIndex]of ALIASES){if(crateAliasesIndex.has(lowerQuery)){const pushTo=crate===currentCrate?crateAliases:aliases;const query_aliases=crateAliasesIndex.get(lowerQuery);for(const alias of query_aliases){pushTo.push(createAliasFromItem(searchIndex[alias]))}}}}const sortFunc=(aaa,bbb)=>{if(aaa.path{return searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex)?"":searchState.loadDesc(alias)};const[crateDescs,descs]=await Promise.all([Promise.all(crateAliases.map(fetchDesc)),Promise.all(aliases.map(fetchDesc)),]);const pushFunc=alias=>{alias.alias=query;const res=buildHrefAndPath(alias);alias.displayPath=pathSplitter(res[0]);alias.fullPath=alias.displayPath+alias.name;alias.href=res[1];ret.others.unshift(alias);if(ret.others.length>MAX_RESULTS){ret.others.pop()}};aliases.forEach((alias,i)=>{alias.desc=descs[i]});aliases.forEach(pushFunc);crateAliases.forEach((alias,i)=>{alias.desc=crateDescs[i]});crateAliases.forEach(pushFunc)}function addIntoResults(results,fullId,id,index,dist,path_dist,maxEditDistance){if(dist<=maxEditDistance||index!==-1){if(results.has(fullId)){const result=results.get(fullId);if(result.dontValidate||result.dist<=dist){return}}results.set(fullId,{id:id,index:index,dontValidate:parsedQuery.literalSearch,dist:dist,path_dist:path_dist,})}}function handleSingleArg(row,pos,elem,results_others,results_in_args,results_returned,maxEditDistance,){if(!row||(filterCrates!==null&&row.crate!==filterCrates)){return}let path_dist=0;const fullId=row.id;const tfpDist=compareTypeFingerprints(fullId,parsedQuery.typeFingerprint,);if(tfpDist!==null){const in_args=row.type&&row.type.inputs&&checkIfInList(row.type.inputs,elem,row.type.where_clause,null,0);const returned=row.type&&row.type.output&&checkIfInList(row.type.output,elem,row.type.where_clause,null,0);if(in_args){results_in_args.max_dist=Math.max(results_in_args.max_dist||0,tfpDist);const maxDist=results_in_args.sizenormalizedIndex&&normalizedIndex!==-1)){index=normalizedIndex}if(elem.fullPath.length>1){path_dist=checkPath(elem.pathWithoutLast,row);if(path_dist===null){return}}if(parsedQuery.literalSearch){if(row.word===elem.pathLast){addIntoResults(results_others,fullId,pos,index,0,path_dist)}return}const dist=editDistance(row.normalizedName,elem.normalizedPathLast,maxEditDistance);if(index===-1&&dist>maxEditDistance){return}addIntoResults(results_others,fullId,pos,index,dist,path_dist,maxEditDistance)}function handleArgs(row,pos,results){if(!row||(filterCrates!==null&&row.crate!==filterCrates)||!row.type){return}const tfpDist=compareTypeFingerprints(row.id,parsedQuery.typeFingerprint,);if(tfpDist===null){return}if(results.size>=MAX_RESULTS&&tfpDist>results.max_dist){return}if(!unifyFunctionTypes(row.type.inputs,parsedQuery.elems,row.type.where_clause,null,mgens=>{return unifyFunctionTypes(row.type.output,parsedQuery.returned,row.type.where_clause,mgens,null,0,)},0,)){return}results.max_dist=Math.max(results.max_dist||0,tfpDist);addIntoResults(results,row.id,pos,0,tfpDist,0,Number.MAX_VALUE)}function innerRunQuery(){const queryLen=parsedQuery.elems.reduce((acc,next)=>acc+next.pathLast.length,0)+parsedQuery.returned.reduce((acc,next)=>acc+next.pathLast.length,0);const maxEditDistance=Math.floor(queryLen/3);const genericSymbols=new Map();function convertNameToId(elem,isAssocType){const loweredName=elem.pathLast.toLowerCase();if(typeNameIdMap.has(loweredName)&&(isAssocType||!typeNameIdMap.get(loweredName).assocOnly)){elem.id=typeNameIdMap.get(loweredName).id}else if(!parsedQuery.literalSearch){let match=null;let matchDist=maxEditDistance+1;let matchName="";for(const[name,{id,assocOnly}]of typeNameIdMap){const dist=Math.min(editDistance(name,loweredName,maxEditDistance),editDistance(name,elem.normalizedPathLast,maxEditDistance),);if(dist<=matchDist&&dist<=maxEditDistance&&(isAssocType||!assocOnly)){if(dist===matchDist&&matchName>name){continue}match=id;matchDist=dist;matchName=name}}if(match!==null){parsedQuery.correction=matchName}elem.id=match}if((elem.id===null&&parsedQuery.totalElems>1&&elem.typeFilter===-1&&elem.generics.length===0&&elem.bindings.size===0)||elem.typeFilter===TY_GENERIC){if(genericSymbols.has(elem.name)){elem.id=genericSymbols.get(elem.name)}else{elem.id=-(genericSymbols.size+1);genericSymbols.set(elem.name,elem.id)}if(elem.typeFilter===-1&&elem.name.length>=3){const maxPartDistance=Math.floor(elem.name.length/3);let matchDist=maxPartDistance+1;let matchName="";for(const name of typeNameIdMap.keys()){const dist=editDistance(name,elem.name,maxPartDistance);if(dist<=matchDist&&dist<=maxPartDistance){if(dist===matchDist&&matchName>name){continue}matchDist=dist;matchName=name}}if(matchName!==""){parsedQuery.proposeCorrectionFrom=elem.name;parsedQuery.proposeCorrectionTo=matchName}}elem.typeFilter=TY_GENERIC}if(elem.generics.length>0&&elem.typeFilter===TY_GENERIC){parsedQuery.error=["Generic type parameter ",elem.name," does not accept generic parameters",]}for(const elem2 of elem.generics){convertNameToId(elem2)}elem.bindings=new Map(Array.from(elem.bindings.entries()).map(entry=>{const[name,constraints]=entry;if(!typeNameIdMap.has(name)){parsedQuery.error=["Type parameter ",name," does not exist",];return[null,[]]}for(const elem2 of constraints){convertNameToId(elem2)}return[typeNameIdMap.get(name).id,constraints]}),)}const fps=new Set();for(const elem of parsedQuery.elems){convertNameToId(elem);buildFunctionTypeFingerprint(elem,parsedQuery.typeFingerprint,fps)}for(const elem of parsedQuery.returned){convertNameToId(elem);buildFunctionTypeFingerprint(elem,parsedQuery.typeFingerprint,fps)}if(parsedQuery.foundElems===1&&parsedQuery.returned.length===0){if(parsedQuery.elems.length===1){const elem=parsedQuery.elems[0];for(let i=0,nSearchIndex=searchIndex.length;i0){const sortQ=(a,b)=>{const ag=a.generics.length===0&&a.bindings.size===0;const bg=b.generics.length===0&&b.bindings.size===0;if(ag!==bg){return ag-bg}const ai=a.id>0;const bi=b.id>0;return ai-bi};parsedQuery.elems.sort(sortQ);parsedQuery.returned.sort(sortQ);for(let i=0,nSearchIndex=searchIndex.length;i{const descs=await Promise.all(list.map(result=>{return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex)?"":searchState.loadDesc(result)}));for(const[i,result]of list.entries()){result.desc=descs[i]}}));if(parsedQuery.error!==null&&ret.others.length!==0){ret.query.error=null}return ret}function nextTab(direction){const next=(searchState.currentTab+direction+3)%searchState.focusedByTab.length;searchState.focusedByTab[searchState.currentTab]=document.activeElement;printTab(next);focusSearchResult()}function focusSearchResult(){const target=searchState.focusedByTab[searchState.currentTab]||document.querySelectorAll(".search-results.active a").item(0)||document.querySelectorAll("#search-tabs button").item(searchState.currentTab);searchState.focusedByTab[searchState.currentTab]=null;if(target){target.focus()}}function buildHrefAndPath(item){let displayPath;let href;const type=itemTypes[item.ty];const name=item.name;let path=item.path;let exactPath=item.exactPath;if(type==="mod"){displayPath=path+"::";href=ROOT_PATH+path.replace(/::/g,"/")+"/"+name+"/index.html"}else if(type==="import"){displayPath=item.path+"::";href=ROOT_PATH+item.path.replace(/::/g,"/")+"/index.html#reexport."+name}else if(type==="primitive"||type==="keyword"){displayPath="";href=ROOT_PATH+path.replace(/::/g,"/")+"/"+type+"."+name+".html"}else if(type==="externcrate"){displayPath="";href=ROOT_PATH+name+"/index.html"}else if(item.parent!==undefined){const myparent=item.parent;let anchor=type+"."+name;const parentType=itemTypes[myparent.ty];let pageType=parentType;let pageName=myparent.name;exactPath=`${myparent.exactPath}::${myparent.name}`;if(parentType==="primitive"){displayPath=myparent.name+"::"}else if(type==="structfield"&&parentType==="variant"){const enumNameIdx=item.path.lastIndexOf("::");const enumName=item.path.substr(enumNameIdx+2);path=item.path.substr(0,enumNameIdx);displayPath=path+"::"+enumName+"::"+myparent.name+"::";anchor="variant."+myparent.name+".field."+name;pageType="enum";pageName=enumName}else{displayPath=path+"::"+myparent.name+"::"}if(item.implDisambiguator!==null){anchor=item.implDisambiguator+"/"+anchor}href=ROOT_PATH+path.replace(/::/g,"/")+"/"+pageType+"."+pageName+".html#"+anchor}else{displayPath=item.path+"::";href=ROOT_PATH+item.path.replace(/::/g,"/")+"/"+type+"."+name+".html"}return[displayPath,href,`${exactPath}::${name}`]}function pathSplitter(path){const tmp=""+path.replace(/::/g,"::");if(tmp.endsWith("")){return tmp.slice(0,tmp.length-6)}return tmp}async function addTab(array,query,display){const extraClass=display?" active":"";const output=document.createElement("div");if(array.length>0){output.className="search-results "+extraClass;for(const item of array){const name=item.name;const type=itemTypes[item.ty];const longType=longItemTypes[item.ty];const typeName=longType.length!==0?`${longType}`:"?";const link=document.createElement("a");link.className="result-"+type;link.href=item.href;const resultName=document.createElement("div");resultName.className="result-name";resultName.insertAdjacentHTML("beforeend",`${typeName}`);link.appendChild(resultName);let alias=" ";if(item.is_alias){alias=`
\ +${item.alias} - see \ +
`}resultName.insertAdjacentHTML("beforeend",`
${alias}\ +${item.displayPath}${name}\ +
`);const description=document.createElement("div");description.className="desc";description.insertAdjacentHTML("beforeend",item.desc);link.appendChild(description);output.appendChild(link)}}else if(query.error===null){output.className="search-failed"+extraClass;output.innerHTML="No results :(
"+"Try on DuckDuckGo?

"+"Or try looking in one of these:"}return[output,array.length]}function makeTabHeader(tabNb,text,nbElems){const fmtNbElems=nbElems<10?`\u{2007}(${nbElems})\u{2007}\u{2007}`:nbElems<100?`\u{2007}(${nbElems})\u{2007}`:`\u{2007}(${nbElems})`;if(searchState.currentTab===tabNb){return""}return""}async function showResults(results,go_to_first,filterCrates){const search=searchState.outputElement();if(go_to_first||(results.others.length===1&&getSettingValue("go-to-only-result")==="true")){window.onunload=()=>{};searchState.removeQueryParameters();const elem=document.createElement("a");elem.href=results.others[0].href;removeClass(elem,"active");document.body.appendChild(elem);elem.click();return}if(results.query===undefined){results.query=parseQuery(searchState.input.value)}currentResults=results.query.userQuery;const[ret_others,ret_in_args,ret_returned]=await Promise.all([addTab(results.others,results.query,true),addTab(results.in_args,results.query,false),addTab(results.returned,results.query,false),]);let currentTab=searchState.currentTab;if((currentTab===0&&ret_others[1]===0)||(currentTab===1&&ret_in_args[1]===0)||(currentTab===2&&ret_returned[1]===0)){if(ret_others[1]!==0){currentTab=0}else if(ret_in_args[1]!==0){currentTab=1}else if(ret_returned[1]!==0){currentTab=2}}let crates="";if(rawSearchIndex.size>1){crates=" in 
"}let output=`

Results${crates}

`;if(results.query.error!==null){const error=results.query.error;error.forEach((value,index)=>{value=value.split("<").join("<").split(">").join(">");if(index%2!==0){error[index]=`${value.replaceAll(" ", " ")}`}else{error[index]=value}});output+=`

Query parser error: "${error.join("")}".

`;output+="
"+makeTabHeader(0,"In Names",ret_others[1])+"
";currentTab=0}else if(results.query.foundElems<=1&&results.query.returned.length===0){output+="
"+makeTabHeader(0,"In Names",ret_others[1])+makeTabHeader(1,"In Parameters",ret_in_args[1])+makeTabHeader(2,"In Return Types",ret_returned[1])+"
"}else{const signatureTabTitle=results.query.elems.length===0?"In Function Return Types":results.query.returned.length===0?"In Function Parameters":"In Function Signatures";output+="
"+makeTabHeader(0,signatureTabTitle,ret_others[1])+"
";currentTab=0}if(results.query.correction!==null){const orig=results.query.returned.length>0?results.query.returned[0].name:results.query.elems[0].name;output+="

"+`Type "${orig}" not found. `+"Showing results for closest type name "+`"${results.query.correction}" instead.

`}if(results.query.proposeCorrectionFrom!==null){const orig=results.query.proposeCorrectionFrom;const targ=results.query.proposeCorrectionTo;output+="

"+`Type "${orig}" not found and used as generic parameter. `+`Consider searching for "${targ}" instead.

`}const resultsElem=document.createElement("div");resultsElem.id="results";resultsElem.appendChild(ret_others[0]);resultsElem.appendChild(ret_in_args[0]);resultsElem.appendChild(ret_returned[0]);search.innerHTML=output;const crateSearch=document.getElementById("crate-search");if(crateSearch){crateSearch.addEventListener("input",updateCrate)}search.appendChild(resultsElem);searchState.showResults(search);const elems=document.getElementById("search-tabs").childNodes;searchState.focusedByTab=[];let i=0;for(const elem of elems){const j=i;elem.onclick=()=>printTab(j);searchState.focusedByTab.push(null);i+=1}printTab(currentTab)}function updateSearchHistory(url){if(!browserSupportsHistoryApi()){return}const params=searchState.getQueryStringParams();if(!history.state&&!params.search){history.pushState(null,"",url)}else{history.replaceState(null,"",url)}}async function search(forced){const query=parseQuery(searchState.input.value.trim());let filterCrates=getFilterCrates();if(!forced&&query.userQuery===currentResults){if(query.userQuery.length>0){putBackSearch()}return}searchState.setLoadingSearch();const params=searchState.getQueryStringParams();if(filterCrates===null&¶ms["filter-crate"]!==undefined){filterCrates=params["filter-crate"]}searchState.title="\""+query.original+"\" Search - Rust";updateSearchHistory(buildUrl(query.original,filterCrates));await showResults(await execQuery(query,filterCrates,window.currentCrate),params.go_to_first,filterCrates)}function buildItemSearchTypeAll(types,lowercasePaths){return types.length>0?types.map(type=>buildItemSearchType(type,lowercasePaths)):EMPTY_GENERICS_ARRAY}const EMPTY_BINDINGS_MAP=new Map();const EMPTY_GENERICS_ARRAY=[];let TYPES_POOL=new Map();function buildItemSearchType(type,lowercasePaths,isAssocType){const PATH_INDEX_DATA=0;const GENERICS_DATA=1;const BINDINGS_DATA=2;let pathIndex,generics,bindings;if(typeof type==="number"){pathIndex=type;generics=EMPTY_GENERICS_ARRAY;bindings=EMPTY_BINDINGS_MAP}else{pathIndex=type[PATH_INDEX_DATA];generics=buildItemSearchTypeAll(type[GENERICS_DATA],lowercasePaths,);if(type.length>BINDINGS_DATA&&type[BINDINGS_DATA].length>0){bindings=new Map(type[BINDINGS_DATA].map(binding=>{const[assocType,constraints]=binding;return[buildItemSearchType(assocType,lowercasePaths,true).id,buildItemSearchTypeAll(constraints,lowercasePaths),]}))}else{bindings=EMPTY_BINDINGS_MAP}}let result;if(pathIndex<0){result={id:pathIndex,ty:TY_GENERIC,path:null,exactPath:null,generics,bindings,}}else if(pathIndex===0){result={id:null,ty:null,path:null,exactPath:null,generics,bindings,}}else{const item=lowercasePaths[pathIndex-1];result={id:buildTypeMapIndex(item.name,isAssocType),ty:item.ty,path:item.path,exactPath:item.exactPath,generics,bindings,}}const cr=TYPES_POOL.get(result.id);if(cr){if(cr.generics.length===result.generics.length&&cr.generics!==result.generics&&cr.generics.every((x,i)=>result.generics[i]===x)){result.generics=cr.generics}if(cr.bindings.size===result.bindings.size&&cr.bindings!==result.bindings){let ok=true;for(const[k,v]of cr.bindings.entries()){const v2=result.bindings.get(v);if(!v2){ok=false;break}if(v!==v2&&v.length===v2.length&&v.every((x,i)=>v2[i]===x)){result.bindings.set(k,v)}else if(v!==v2){ok=false;break}}if(ok){result.bindings=cr.bindings}}if(cr.ty===result.ty&&cr.path===result.path&&cr.bindings===result.bindings&&cr.generics===result.generics&&cr.ty===result.ty){return cr}}TYPES_POOL.set(result.id,result);return result}function buildFunctionSearchTypeCallback(lowercasePaths){return functionSearchType=>{if(functionSearchType===0){return null}const INPUTS_DATA=0;const OUTPUT_DATA=1;let inputs,output;if(typeof functionSearchType[INPUTS_DATA]==="number"){inputs=[buildItemSearchType(functionSearchType[INPUTS_DATA],lowercasePaths)]}else{inputs=buildItemSearchTypeAll(functionSearchType[INPUTS_DATA],lowercasePaths,)}if(functionSearchType.length>1){if(typeof functionSearchType[OUTPUT_DATA]==="number"){output=[buildItemSearchType(functionSearchType[OUTPUT_DATA],lowercasePaths)]}else{output=buildItemSearchTypeAll(functionSearchType[OUTPUT_DATA],lowercasePaths,)}}else{output=[]}const where_clause=[];const l=functionSearchType.length;for(let i=2;i{k=(~~k+0x7ed55d16)+(k<<12);k=(k ^ 0xc761c23c)^(k>>>19);k=(~~k+0x165667b1)+(k<<5);k=(~~k+0xd3a2646c)^(k<<9);k=(~~k+0xfd7046c5)+(k<<3);return(k ^ 0xb55a4f09)^(k>>>16)};const hashint2=k=>{k=~k+(k<<15);k ^=k>>>12;k+=k<<2;k ^=k>>>4;k=Math.imul(k,2057);return k ^(k>>16)};if(input!==null){const h0a=hashint1(input);const h0b=hashint2(input);const h1a=~~(h0a+Math.imul(h0b,2));const h1b=~~(h0a+Math.imul(h0b,3));const h2a=~~(h0a+Math.imul(h0b,4));const h2b=~~(h0a+Math.imul(h0b,5));output[0]|=(1<<(h0a%32))|(1<<(h1b%32));output[1]|=(1<<(h1a%32))|(1<<(h2b%32));output[2]|=(1<<(h2a%32))|(1<<(h0b%32));fps.add(input)}for(const g of type.generics){buildFunctionTypeFingerprint(g,output,fps)}const fb={id:null,ty:0,generics:EMPTY_GENERICS_ARRAY,bindings:EMPTY_BINDINGS_MAP,};for(const[k,v]of type.bindings.entries()){fb.id=k;fb.generics=v;buildFunctionTypeFingerprint(fb,output,fps)}output[3]=fps.size}function compareTypeFingerprints(fullId,queryFingerprint){const fh0=functionTypeFingerprint[fullId*4];const fh1=functionTypeFingerprint[(fullId*4)+1];const fh2=functionTypeFingerprint[(fullId*4)+2];const[qh0,qh1,qh2]=queryFingerprint;const[in0,in1,in2]=[fh0&qh0,fh1&qh1,fh2&qh2];if((in0 ^ qh0)||(in1 ^ qh1)||(in2 ^ qh2)){return null}return functionTypeFingerprint[(fullId*4)+3]}class VlqHexDecoder{constructor(string,cons){this.string=string;this.cons=cons;this.offset=0;this.backrefQueue=[]}decodeList(){let c=this.string.charCodeAt(this.offset);const ret=[];while(c!==125){ret.push(this.decode());c=this.string.charCodeAt(this.offset)}this.offset+=1;return ret}decode(){let n=0;let c=this.string.charCodeAt(this.offset);if(c===123){this.offset+=1;return this.decodeList()}while(c<96){n=(n<<4)|(c&0xF);this.offset+=1;c=this.string.charCodeAt(this.offset)}n=(n<<4)|(c&0xF);const[sign,value]=[n&1,n>>1];this.offset+=1;return sign?-value:value}next(){const c=this.string.charCodeAt(this.offset);if(c>=48&&c<64){this.offset+=1;return this.backrefQueue[c-48]}if(c===96){this.offset+=1;return this.cons(0)}const result=this.cons(this.decode());this.backrefQueue.unshift(result);if(this.backrefQueue.length>16){this.backrefQueue.pop()}return result}}class RoaringBitmap{constructor(str){const strdecoded=atob(str);const u8array=new Uint8Array(strdecoded.length);for(let j=0;j=4){offsets=[];for(let j=0;j>3]&(1<<(j&0x7))){const runcount=(u8array[i]|(u8array[i+1]<<8));i+=2;this.containers.push(new RoaringBitmapRun(runcount,u8array.slice(i,i+(runcount*4)),));i+=runcount*4}else if(this.cardinalities[j]>=4096){this.containers.push(new RoaringBitmapBits(u8array.slice(i,i+8192)));i+=8192}else{const end=this.cardinalities[j]*2;this.containers.push(new RoaringBitmapArray(this.cardinalities[j],u8array.slice(i,i+end),));i+=end}}}contains(keyvalue){const key=keyvalue>>16;const value=keyvalue&0xFFFF;for(let i=0;i=start&&value<=(start+lenm1)){return true}}return false}}class RoaringBitmapArray{constructor(cardinality,array){this.cardinality=cardinality;this.array=array}contains(value){const l=this.cardinality*2;for(let i=0;i>3]&(1<<(value&7)))}}function buildIndex(rawSearchIndex){searchIndex=[];searchIndexDeprecated=new Map();searchIndexEmptyDesc=new Map();let currentIndex=0;let id=0;for(const crate of rawSearchIndex.values()){id+=crate.t.length+1}functionTypeFingerprint=new Uint32Array((id+1)*4);id=0;for(const[crate,crateCorpus]of rawSearchIndex){const itemDescShardDecoder=new VlqHexDecoder(crateCorpus.D,noop=>noop);let descShard={crate,shard:0,start:0,len:itemDescShardDecoder.next(),promise:null,resolve:null,};const descShardList=[descShard];searchIndexDeprecated.set(crate,new RoaringBitmap(crateCorpus.c));searchIndexEmptyDesc.set(crate,new RoaringBitmap(crateCorpus.e));let descIndex=0;const crateRow={crate,ty:3,name:crate,path:"",descShard,descIndex,exactPath:"",desc:crateCorpus.doc,parent:undefined,type:null,id,word:crate,normalizedName:crate.indexOf("_")===-1?crate:crate.replace(/_/g,""),bitIndex:0,implDisambiguator:null,};id+=1;searchIndex.push(crateRow);currentIndex+=1;if(!searchIndexEmptyDesc.get(crate).contains(0)){descIndex+=1}const itemTypes=crateCorpus.t;const itemNames=crateCorpus.n;const itemPaths=new Map(crateCorpus.q);const itemReexports=new Map(crateCorpus.r);const itemParentIdxDecoder=new VlqHexDecoder(crateCorpus.i,noop=>noop);const implDisambiguator=new Map(crateCorpus.b);const paths=crateCorpus.p;const aliases=crateCorpus.a;const lowercasePaths=[];const itemFunctionDecoder=new VlqHexDecoder(crateCorpus.f,buildFunctionSearchTypeCallback(lowercasePaths),);let len=paths.length;let lastPath=itemPaths.get(0);for(let i=0;i2){path=itemPaths.has(elem[2])?itemPaths.get(elem[2]):lastPath;lastPath=path}const exactPath=elem.length>3?itemPaths.get(elem[3]):path;lowercasePaths.push({ty,name:name.toLowerCase(),path,exactPath});paths[i]={ty,name,path,exactPath}}lastPath="";len=itemTypes.length;let lastName="";let lastWord="";for(let i=0;i=descShard.len&&!searchIndexEmptyDesc.get(crate).contains(bitIndex)){descShard={crate,shard:descShard.shard+1,start:descShard.start+descShard.len,len:itemDescShardDecoder.next(),promise:null,resolve:null,};descIndex=0;descShardList.push(descShard)}const name=itemNames[i]===""?lastName:itemNames[i];const word=itemNames[i]===""?lastWord:itemNames[i].toLowerCase();const path=itemPaths.has(i)?itemPaths.get(i):lastPath;const type=itemFunctionDecoder.next();if(type!==null){if(type){const fp=functionTypeFingerprint.subarray(id*4,(id+1)*4);const fps=new Set();for(const t of type.inputs){buildFunctionTypeFingerprint(t,fp,fps)}for(const t of type.output){buildFunctionTypeFingerprint(t,fp,fps)}for(const w of type.where_clause){for(const t of w){buildFunctionTypeFingerprint(t,fp,fps)}}}}const itemParentIdx=itemParentIdxDecoder.next();const row={crate,ty:itemTypes.charCodeAt(i)-65,name,path,descShard,descIndex,exactPath:itemReexports.has(i)?itemPaths.get(itemReexports.get(i)):path,parent:itemParentIdx>0?paths[itemParentIdx-1]:undefined,type,id,word,normalizedName:word.indexOf("_")===-1?word:word.replace(/_/g,""),bitIndex,implDisambiguator:implDisambiguator.has(i)?implDisambiguator.get(i):null,};id+=1;searchIndex.push(row);lastPath=row.path;if(!searchIndexEmptyDesc.get(crate).contains(bitIndex)){descIndex+=1}lastName=name;lastWord=word}if(aliases){const currentCrateAliases=new Map();ALIASES.set(crate,currentCrateAliases);for(const alias_name in aliases){if(!Object.prototype.hasOwnProperty.call(aliases,alias_name)){continue}let currentNameAliases;if(currentCrateAliases.has(alias_name)){currentNameAliases=currentCrateAliases.get(alias_name)}else{currentNameAliases=[];currentCrateAliases.set(alias_name,currentNameAliases)}for(const local_alias of aliases[alias_name]){currentNameAliases.push(local_alias+currentIndex)}}}currentIndex+=itemTypes.length;searchState.descShards.set(crate,descShardList)}TYPES_POOL=new Map()}function onSearchSubmit(e){e.preventDefault();searchState.clearInputTimeout();search()}function putBackSearch(){const search_input=searchState.input;if(!searchState.input){return}if(search_input.value!==""&&!searchState.isDisplayed()){searchState.showResults();if(browserSupportsHistoryApi()){history.replaceState(null,"",buildUrl(search_input.value,getFilterCrates()))}document.title=searchState.title}}function registerSearchEvents(){const params=searchState.getQueryStringParams();if(searchState.input.value===""){searchState.input.value=params.search||""}const searchAfter500ms=()=>{searchState.clearInputTimeout();if(searchState.input.value.length===0){searchState.hideResults()}else{searchState.timeout=setTimeout(search,500)}};searchState.input.onkeyup=searchAfter500ms;searchState.input.oninput=searchAfter500ms;document.getElementsByClassName("search-form")[0].onsubmit=onSearchSubmit;searchState.input.onchange=e=>{if(e.target!==document.activeElement){return}searchState.clearInputTimeout();setTimeout(search,0)};searchState.input.onpaste=searchState.input.onchange;searchState.outputElement().addEventListener("keydown",e=>{if(e.altKey||e.ctrlKey||e.shiftKey||e.metaKey){return}if(e.which===38){const previous=document.activeElement.previousElementSibling;if(previous){previous.focus()}else{searchState.focus()}e.preventDefault()}else if(e.which===40){const next=document.activeElement.nextElementSibling;if(next){next.focus()}const rect=document.activeElement.getBoundingClientRect();if(window.innerHeight-rect.bottom{if(e.which===40){focusSearchResult();e.preventDefault()}});searchState.input.addEventListener("focus",()=>{putBackSearch()});searchState.input.addEventListener("blur",()=>{searchState.input.placeholder=searchState.input.origPlaceholder});if(browserSupportsHistoryApi()){const previousTitle=document.title;window.addEventListener("popstate",e=>{const params=searchState.getQueryStringParams();document.title=previousTitle;currentResults=null;if(params.search&¶ms.search.length>0){searchState.input.value=params.search;e.preventDefault();search()}else{searchState.input.value="";searchState.hideResults()}})}window.onpageshow=()=>{const qSearch=searchState.getQueryStringParams().search;if(searchState.input.value===""&&qSearch){searchState.input.value=qSearch}search()}}function updateCrate(ev){if(ev.target.value==="all crates"){const query=searchState.input.value.trim();updateSearchHistory(buildUrl(query,null))}currentResults=null;search(true)}buildIndex(rawSearchIndex);if(typeof window!=="undefined"){registerSearchEvents();if(window.searchState.getQueryStringParams().search){search()}}if(typeof exports!=="undefined"){exports.initSearch=initSearch;exports.execQuery=execQuery;exports.parseQuery=parseQuery}}if(typeof window!=="undefined"){window.initSearch=initSearch;if(window.searchIndex!==undefined){initSearch(window.searchIndex)}}else{initSearch(new Map())}})() \ No newline at end of file diff --git a/doc/static.files/settings-4313503d2e1961c2.js b/doc/static.files/settings-4313503d2e1961c2.js new file mode 100644 index 00000000..ab425fe4 --- /dev/null +++ b/doc/static.files/settings-4313503d2e1961c2.js @@ -0,0 +1,17 @@ +"use strict";(function(){const isSettingsPage=window.location.pathname.endsWith("/settings.html");function changeSetting(settingName,value){if(settingName==="theme"){const useSystem=value==="system preference"?"true":"false";updateLocalStorage("use-system-theme",useSystem)}updateLocalStorage(settingName,value);switch(settingName){case"theme":case"preferred-dark-theme":case"preferred-light-theme":updateTheme();updateLightAndDark();break;case"line-numbers":if(value===true){window.rustdoc_add_line_numbers_to_examples()}else{window.rustdoc_remove_line_numbers_from_examples()}break;case"hide-sidebar":if(value===true){addClass(document.documentElement,"hide-sidebar")}else{removeClass(document.documentElement,"hide-sidebar")}break}}function showLightAndDark(){removeClass(document.getElementById("preferred-light-theme"),"hidden");removeClass(document.getElementById("preferred-dark-theme"),"hidden")}function hideLightAndDark(){addClass(document.getElementById("preferred-light-theme"),"hidden");addClass(document.getElementById("preferred-dark-theme"),"hidden")}function updateLightAndDark(){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||(useSystem===null&&getSettingValue("theme")===null)){showLightAndDark()}else{hideLightAndDark()}}function setEvents(settingsElement){updateLightAndDark();onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"),toggle=>{const settingId=toggle.id;const settingValue=getSettingValue(settingId);if(settingValue!==null){toggle.checked=settingValue==="true"}toggle.onchange=()=>{changeSetting(toggle.id,toggle.checked)}});onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"),elem=>{const settingId=elem.name;let settingValue=getSettingValue(settingId);if(settingId==="theme"){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||settingValue===null){settingValue=useSystem==="false"?"light":"system preference"}}if(settingValue!==null&&settingValue!=="null"){elem.checked=settingValue===elem.value}elem.addEventListener("change",ev=>{changeSetting(ev.target.name,ev.target.value)})})}function buildSettingsPageSections(settings){let output="";for(const setting of settings){const js_data_name=setting["js_name"];const setting_name=setting["name"];if(setting["options"]!==undefined){output+=`\ +
+
${setting_name}
+
`;onEach(setting["options"],option=>{const checked=option===setting["default"]?" checked":"";const full=`${js_data_name}-${option.replace(/ /g,"-")}`;output+=`\ + `});output+=`\ +
+
`}else{const checked=setting["default"]===true?" checked":"";output+=`\ +
\ + \ +
`}}return output}function buildSettingsPage(){const theme_names=getVar("themes").split(",").filter(t=>t);theme_names.push("light","dark","ayu");const settings=[{"name":"Theme","js_name":"theme","default":"system preference","options":theme_names.concat("system preference"),},{"name":"Preferred light theme","js_name":"preferred-light-theme","default":"light","options":theme_names,},{"name":"Preferred dark theme","js_name":"preferred-dark-theme","default":"dark","options":theme_names,},{"name":"Auto-hide item contents for large items","js_name":"auto-hide-large-items","default":true,},{"name":"Auto-hide item methods' documentation","js_name":"auto-hide-method-docs","default":false,},{"name":"Auto-hide trait implementation documentation","js_name":"auto-hide-trait-implementations","default":false,},{"name":"Directly go to item in search if there is only one result","js_name":"go-to-only-result","default":false,},{"name":"Show line numbers on code examples","js_name":"line-numbers","default":false,},{"name":"Hide persistent navigation bar","js_name":"hide-sidebar","default":false,},{"name":"Disable keyboard shortcuts","js_name":"disable-shortcuts","default":false,},];const elementKind=isSettingsPage?"section":"div";const innerHTML=`
${buildSettingsPageSections(settings)}
`;const el=document.createElement(elementKind);el.id="settings";if(!isSettingsPage){el.className="popover"}el.innerHTML=innerHTML;if(isSettingsPage){document.getElementById(MAIN_ID).appendChild(el)}else{el.setAttribute("tabindex","-1");getSettingsButton().appendChild(el)}return el}const settingsMenu=buildSettingsPage();function displaySettings(){settingsMenu.style.display="";onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"),el=>{const val=getSettingValue(el.id);const checked=val==="true";if(checked!==el.checked&&val!==null){el.checked=checked}})}function settingsBlurHandler(event){blurHandler(event,getSettingsButton(),window.hidePopoverMenus)}if(isSettingsPage){getSettingsButton().onclick=event=>{event.preventDefault()}}else{const settingsButton=getSettingsButton();const settingsMenu=document.getElementById("settings");settingsButton.onclick=event=>{if(settingsMenu.contains(event.target)){return}event.preventDefault();const shouldDisplaySettings=settingsMenu.style.display==="none";window.hideAllModals();if(shouldDisplaySettings){displaySettings()}};settingsButton.onblur=settingsBlurHandler;settingsButton.querySelector("a").onblur=settingsBlurHandler;onEachLazy(settingsMenu.querySelectorAll("input"),el=>{el.onblur=settingsBlurHandler});settingsMenu.onblur=settingsBlurHandler}setTimeout(()=>{setEvents(settingsMenu);if(!isSettingsPage){displaySettings()}removeClass(getSettingsButton(),"rotate")},0)})() \ No newline at end of file diff --git a/doc/static.files/src-script-e66d777a5a92e9b2.js b/doc/static.files/src-script-e66d777a5a92e9b2.js new file mode 100644 index 00000000..d0aebb85 --- /dev/null +++ b/doc/static.files/src-script-e66d777a5a92e9b2.js @@ -0,0 +1 @@ +"use strict";(function(){const rootPath=getVar("root-path");const NAME_OFFSET=0;const DIRS_OFFSET=1;const FILES_OFFSET=2;const RUSTDOC_MOBILE_BREAKPOINT=700;function closeSidebarIfMobile(){if(window.innerWidth{removeClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","false")};window.rustdocShowSourceSidebar=()=>{addClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","true")};window.rustdocToggleSrcSidebar=()=>{if(document.documentElement.classList.contains("src-sidebar-expanded")){window.rustdocCloseSourceSidebar()}else{window.rustdocShowSourceSidebar()}};function createSrcSidebar(){const container=document.querySelector("nav.sidebar");const sidebar=document.createElement("div");sidebar.id="src-sidebar";let hasFoundFile=false;for(const[key,source]of srcIndex){source[NAME_OFFSET]=key;hasFoundFile=createDirEntry(source,sidebar,"",hasFoundFile)}container.appendChild(sidebar);const selected_elem=sidebar.getElementsByClassName("selected")[0];if(typeof selected_elem!=="undefined"){selected_elem.focus()}}function highlightSrcLines(){const match=window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);if(!match){return}let from=parseInt(match[1],10);let to=from;if(typeof match[2]!=="undefined"){to=parseInt(match[2],10)}if(to{onEachLazy(e.getElementsByTagName("a"),i_e=>{removeClass(i_e,"line-highlighted")})});for(let i=from;i<=to;++i){elem=document.getElementById(i);if(!elem){break}addClass(elem,"line-highlighted")}}const handleSrcHighlight=(function(){let prev_line_id=0;const set_fragment=name=>{const x=window.scrollX,y=window.scrollY;if(browserSupportsHistoryApi()){history.replaceState(null,null,"#"+name);highlightSrcLines()}else{location.replace("#"+name)}window.scrollTo(x,y)};return ev=>{let cur_line_id=parseInt(ev.target.id,10);if(isNaN(cur_line_id)||ev.ctrlKey||ev.altKey||ev.metaKey){return}ev.preventDefault();if(ev.shiftKey&&prev_line_id){if(prev_line_id>cur_line_id){const tmp=prev_line_id;prev_line_id=cur_line_id;cur_line_id=tmp}set_fragment(prev_line_id+"-"+cur_line_id)}else{prev_line_id=cur_line_id;set_fragment(cur_line_id)}}}());window.addEventListener("hashchange",highlightSrcLines);onEachLazy(document.getElementsByClassName("src-line-numbers"),el=>{el.addEventListener("click",handleSrcHighlight)});highlightSrcLines();window.createSrcSidebar=createSrcSidebar})() \ No newline at end of file diff --git a/doc/static.files/storage-118b08c4c78b968e.js b/doc/static.files/storage-118b08c4c78b968e.js new file mode 100644 index 00000000..98189467 --- /dev/null +++ b/doc/static.files/storage-118b08c4c78b968e.js @@ -0,0 +1,24 @@ +"use strict";const builtinThemes=["light","dark","ayu"];const darkThemes=["dark","ayu"];window.currentTheme=document.getElementById("themeStyle");const settingsDataset=(function(){const settingsElement=document.getElementById("default-settings");return settingsElement&&settingsElement.dataset?settingsElement.dataset:null})();function getSettingValue(settingName){const current=getCurrentValue(settingName);if(current===null&&settingsDataset!==null){const def=settingsDataset[settingName.replace(/-/g,"_")];if(def!==undefined){return def}}return current}const localStoredTheme=getSettingValue("theme");function hasClass(elem,className){return elem&&elem.classList&&elem.classList.contains(className)}function addClass(elem,className){if(elem&&elem.classList){elem.classList.add(className)}}function removeClass(elem,className){if(elem&&elem.classList){elem.classList.remove(className)}}function onEach(arr,func){for(const elem of arr){if(func(elem)){return true}}return false}function onEachLazy(lazyArray,func){return onEach(Array.prototype.slice.call(lazyArray),func)}function updateLocalStorage(name,value){try{window.localStorage.setItem("rustdoc-"+name,value)}catch(e){}}function getCurrentValue(name){try{return window.localStorage.getItem("rustdoc-"+name)}catch(e){return null}}const getVar=(function getVar(name){const el=document.querySelector("head > meta[name='rustdoc-vars']");return el?el.attributes["data-"+name].value:null});function switchTheme(newThemeName,saveTheme){const themeNames=getVar("themes").split(",").filter(t=>t);themeNames.push(...builtinThemes);if(themeNames.indexOf(newThemeName)===-1){return}if(saveTheme){updateLocalStorage("theme",newThemeName)}document.documentElement.setAttribute("data-theme",newThemeName);if(builtinThemes.indexOf(newThemeName)!==-1){if(window.currentTheme){window.currentTheme.parentNode.removeChild(window.currentTheme);window.currentTheme=null}}else{const newHref=getVar("root-path")+encodeURIComponent(newThemeName)+getVar("resource-suffix")+".css";if(!window.currentTheme){if(document.readyState==="loading"){document.write(``);window.currentTheme=document.getElementById("themeStyle")}else{window.currentTheme=document.createElement("link");window.currentTheme.rel="stylesheet";window.currentTheme.id="themeStyle";window.currentTheme.href=newHref;document.documentElement.appendChild(window.currentTheme)}}else if(newHref!==window.currentTheme.href){window.currentTheme.href=newHref}}}const updateTheme=(function(){const mql=window.matchMedia("(prefers-color-scheme: dark)");function updateTheme(){if(getSettingValue("use-system-theme")!=="false"){const lightTheme=getSettingValue("preferred-light-theme")||"light";const darkTheme=getSettingValue("preferred-dark-theme")||"dark";updateLocalStorage("use-system-theme","true");switchTheme(mql.matches?darkTheme:lightTheme,true)}else{switchTheme(getSettingValue("theme"),false)}}mql.addEventListener("change",updateTheme);return updateTheme})();if(getSettingValue("use-system-theme")!=="false"&&window.matchMedia){if(getSettingValue("use-system-theme")===null&&getSettingValue("preferred-dark-theme")===null&&darkThemes.indexOf(localStoredTheme)>=0){updateLocalStorage("preferred-dark-theme",localStoredTheme)}}updateTheme();if(getSettingValue("source-sidebar-show")==="true"){addClass(document.documentElement,"src-sidebar-expanded")}if(getSettingValue("hide-sidebar")==="true"){addClass(document.documentElement,"hide-sidebar")}function updateSidebarWidth(){const desktopSidebarWidth=getSettingValue("desktop-sidebar-width");if(desktopSidebarWidth&&desktopSidebarWidth!=="null"){document.documentElement.style.setProperty("--desktop-sidebar-width",desktopSidebarWidth+"px",)}const srcSidebarWidth=getSettingValue("src-sidebar-width");if(srcSidebarWidth&&srcSidebarWidth!=="null"){document.documentElement.style.setProperty("--src-sidebar-width",srcSidebarWidth+"px",)}}updateSidebarWidth();window.addEventListener("pageshow",ev=>{if(ev.persisted){setTimeout(updateTheme,0);setTimeout(updateSidebarWidth,0)}});class RustdocSearchElement extends HTMLElement{constructor(){super()}connectedCallback(){const rootPath=getVar("root-path");const currentCrate=getVar("current-crate");this.innerHTML=``}}window.customElements.define("rustdoc-search",RustdocSearchElement) \ No newline at end of file diff --git a/doc/trait.impl/core/clone/trait.Clone.js b/doc/trait.impl/core/clone/trait.Clone.js new file mode 100644 index 00000000..eabbf7e2 --- /dev/null +++ b/doc/trait.impl/core/clone/trait.Clone.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl Clone for RedisErrorKind"],["impl Clone for AggregateOperation"],["impl Clone for Aggregator"],["impl Clone for BackpressurePolicy"],["impl Clone for Blocking"],["impl Clone for BucketTimestamp"],["impl Clone for ClientKillFilter"],["impl Clone for ClientKillType"],["impl Clone for ClientPauseKind"],["impl Clone for ClientReplyFlag"],["impl Clone for ClientState"],["impl Clone for ClientUnblockFlag"],["impl Clone for ClusterDiscoveryPolicy"],["impl Clone for ClusterFailoverFlag"],["impl Clone for ClusterHash"],["impl Clone for ClusterResetFlag"],["impl Clone for ClusterSetSlotState"],["impl Clone for ClusterState"],["impl Clone for ClusterStateChange"],["impl Clone for DuplicatePolicy"],["impl Clone for Encoding"],["impl Clone for Expiration"],["impl Clone for ExpireOptions"],["impl Clone for FnPolicy"],["impl Clone for FunctionFlag"],["impl Clone for GeoUnit"],["impl Clone for GetLabels"],["impl Clone for GetTimestamp"],["impl Clone for IndexKind"],["impl Clone for InfoKind"],["impl Clone for LMoveDirection"],["impl Clone for ListLocation"],["impl Clone for Load"],["impl Clone for MessageKind"],["impl Clone for Ordering"],["impl Clone for ReconnectError"],["impl Clone for ReconnectPolicy"],["impl Clone for RedisValue"],["impl Clone for RedisValueKind"],["impl Clone for Reducer"],["impl Clone for ReducerFunc"],["impl Clone for ScanType"],["impl Clone for ScriptDebugFlag"],["impl Clone for SearchSchemaKind"],["impl Clone for SentinelFailureKind"],["impl Clone for ServerConfig"],["impl Clone for SetOptions"],["impl Clone for ShutdownFlags"],["impl Clone for SortOrder"],["impl Clone for SpellcheckTerms"],["impl Clone for StringOrNumber"],["impl Clone for Timestamp"],["impl Clone for TlsConnector"],["impl Clone for TlsHostMapping"],["impl Clone for Toggle"],["impl Clone for XCapKind"],["impl Clone for XCapTrim"],["impl Clone for XID"],["impl Clone for ZCmp"],["impl Clone for ZRangeBound"],["impl Clone for ZRangeKind"],["impl Clone for ZSort"],["impl Clone for RedisClient"],["impl Clone for RedisPool"],["impl Clone for Replicas"],["impl Clone for SentinelClient"],["impl Clone for SubscriberClient"],["impl Clone for Transaction"],["impl Clone for RedisError"],["impl Clone for Command"],["impl Clone for BackpressureConfig"],["impl Clone for Builder"],["impl Clone for ClusterInfo"],["impl Clone for ClusterRouting"],["impl Clone for ConnectionConfig"],["impl Clone for CustomCommand"],["impl Clone for DatabaseMemoryStats"],["impl Clone for FtAggregateOptions"],["impl Clone for FtAlterOptions"],["impl Clone for FtCreateOptions"],["impl Clone for FtSearchOptions"],["impl Clone for Function"],["impl Clone for GeoPosition"],["impl Clone for GeoRadiusInfo"],["impl Clone for GeoValue"],["impl Clone for GroupBy"],["impl Clone for Invalidation"],["impl Clone for KeyspaceEvent"],["impl Clone for Library"],["impl Clone for MemoryStats"],["impl Clone for Message"],["impl Clone for MultipleGeoValues"],["impl Clone for MultipleIDs"],["impl Clone for MultipleKeys"],["impl Clone for MultipleOrderedPairs"],["impl Clone for Options"],["impl Clone for PerformanceConfig"],["impl Clone for RangeAggregation"],["impl Clone for RedisConfig"],["impl Clone for RedisKey"],["impl Clone for RedisMap"],["impl Clone for ReplicaConfig"],["impl Clone for Script"],["impl Clone for SearchField"],["impl Clone for SearchFilter"],["impl Clone for SearchGeoFilter"],["impl Clone for SearchHighlight"],["impl Clone for SearchParameter"],["impl Clone for SearchReducer"],["impl Clone for SearchSchema"],["impl Clone for SearchSortBy"],["impl Clone for SearchSummarize"],["impl Clone for SentinelConfig"],["impl Clone for Server"],["impl Clone for SlotRange"],["impl Clone for SlowlogEntry"],["impl Clone for TcpConfig"],["impl Clone for TlsConfig"],["impl Clone for TracingConfig"],["impl Clone for UnresponsiveConfig"],["impl Clone for WithCursor"],["impl Clone for XCap"],["impl Clone for XPendingArgs"],["impl Clone for ZRange"],["impl<C: Clone + ClientLike> Clone for WithOptions<C>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[34023]} \ No newline at end of file diff --git a/doc/trait.impl/core/cmp/trait.Eq.js b/doc/trait.impl/core/cmp/trait.Eq.js new file mode 100644 index 00000000..d4ea1ee1 --- /dev/null +++ b/doc/trait.impl/core/cmp/trait.Eq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl Eq for RedisErrorKind"],["impl Eq for AggregateOperation"],["impl Eq for Aggregator"],["impl Eq for BackpressurePolicy"],["impl Eq for Blocking"],["impl Eq for BucketTimestamp"],["impl Eq for ClientKillFilter"],["impl Eq for ClientKillType"],["impl Eq for ClientPauseKind"],["impl Eq for ClientReplyFlag"],["impl Eq for ClientState"],["impl Eq for ClientUnblockFlag"],["impl Eq for ClusterDiscoveryPolicy"],["impl Eq for ClusterFailoverFlag"],["impl Eq for ClusterHash"],["impl Eq for ClusterResetFlag"],["impl Eq for ClusterSetSlotState"],["impl Eq for ClusterState"],["impl Eq for ClusterStateChange"],["impl Eq for DuplicatePolicy"],["impl Eq for Encoding"],["impl Eq for Expiration"],["impl Eq for ExpireOptions"],["impl Eq for FnPolicy"],["impl Eq for FunctionFlag"],["impl Eq for GeoUnit"],["impl Eq for GetLabels"],["impl Eq for GetTimestamp"],["impl Eq for IndexKind"],["impl Eq for InfoKind"],["impl Eq for LMoveDirection"],["impl Eq for ListLocation"],["impl Eq for Load"],["impl Eq for MessageKind"],["impl Eq for Ordering"],["impl Eq for ReconnectError"],["impl Eq for ReconnectPolicy"],["impl Eq for RedisValue"],["impl Eq for RedisValueKind"],["impl Eq for Reducer"],["impl Eq for ReducerFunc"],["impl Eq for ScanType"],["impl Eq for ScriptDebugFlag"],["impl Eq for SentinelFailureKind"],["impl Eq for ServerConfig"],["impl Eq for SetOptions"],["impl Eq for ShutdownFlags"],["impl Eq for SortOrder"],["impl Eq for StringOrNumber"],["impl Eq for Timestamp"],["impl Eq for TlsConnector"],["impl Eq for TlsHostMapping"],["impl Eq for Toggle"],["impl Eq for XCapKind"],["impl Eq for XCapTrim"],["impl Eq for XID"],["impl Eq for ZCmp"],["impl Eq for ZRangeKind"],["impl Eq for ZSort"],["impl Eq for Transaction"],["impl Eq for RedisError"],["impl Eq for Command"],["impl Eq for BackpressureConfig"],["impl Eq for ClusterInfo"],["impl Eq for ConnectionConfig"],["impl Eq for CustomCommand"],["impl Eq for DatabaseMemoryStats"],["impl Eq for Function"],["impl Eq for GeoPosition"],["impl Eq for GeoRadiusInfo"],["impl Eq for GeoValue"],["impl Eq for GroupBy"],["impl Eq for Invalidation"],["impl Eq for KeyspaceEvent"],["impl Eq for Library"],["impl Eq for MemoryStats"],["impl Eq for Message"],["impl Eq for MultipleGeoValues"],["impl Eq for MultipleIDs"],["impl Eq for MultipleKeys"],["impl Eq for MultipleOrderedPairs"],["impl Eq for Options"],["impl Eq for PerformanceConfig"],["impl Eq for RangeAggregation"],["impl Eq for RedisConfig"],["impl Eq for RedisKey"],["impl Eq for RedisMap"],["impl Eq for ReplicaConfig"],["impl Eq for Script"],["impl Eq for SearchField"],["impl Eq for SearchHighlight"],["impl Eq for SearchParameter"],["impl Eq for SearchReducer"],["impl Eq for SearchSortBy"],["impl Eq for SearchSummarize"],["impl Eq for Server"],["impl Eq for SlotRange"],["impl Eq for SlowlogEntry"],["impl Eq for TcpConfig"],["impl Eq for TlsConfig"],["impl Eq for UnresponsiveConfig"],["impl Eq for WithCursor"],["impl Eq for XCap"],["impl Eq for XPendingArgs"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[26608]} \ No newline at end of file diff --git a/doc/trait.impl/core/cmp/trait.Ord.js b/doc/trait.impl/core/cmp/trait.Ord.js new file mode 100644 index 00000000..d65ec2f0 --- /dev/null +++ b/doc/trait.impl/core/cmp/trait.Ord.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl Ord for FunctionFlag"],["impl Ord for Function"],["impl Ord for KeyspaceEvent"],["impl Ord for Library"],["impl Ord for RedisKey"],["impl Ord for Script"],["impl Ord for Server"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1773]} \ No newline at end of file diff --git a/doc/trait.impl/core/cmp/trait.PartialEq.js b/doc/trait.impl/core/cmp/trait.PartialEq.js new file mode 100644 index 00000000..3562d98e --- /dev/null +++ b/doc/trait.impl/core/cmp/trait.PartialEq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl PartialEq for RedisErrorKind"],["impl PartialEq for AggregateOperation"],["impl PartialEq for Aggregator"],["impl PartialEq for BackpressurePolicy"],["impl PartialEq for Blocking"],["impl PartialEq for BucketTimestamp"],["impl PartialEq for ClientKillFilter"],["impl PartialEq for ClientKillType"],["impl PartialEq for ClientPauseKind"],["impl PartialEq for ClientReplyFlag"],["impl PartialEq for ClientState"],["impl PartialEq for ClientUnblockFlag"],["impl PartialEq for ClusterDiscoveryPolicy"],["impl PartialEq for ClusterFailoverFlag"],["impl PartialEq for ClusterHash"],["impl PartialEq for ClusterResetFlag"],["impl PartialEq for ClusterSetSlotState"],["impl PartialEq for ClusterState"],["impl PartialEq for ClusterStateChange"],["impl PartialEq for DuplicatePolicy"],["impl PartialEq for Encoding"],["impl PartialEq for Expiration"],["impl PartialEq for ExpireOptions"],["impl PartialEq for FnPolicy"],["impl PartialEq for FunctionFlag"],["impl PartialEq for GeoUnit"],["impl PartialEq for GetLabels"],["impl PartialEq for GetTimestamp"],["impl PartialEq for IndexKind"],["impl PartialEq for InfoKind"],["impl PartialEq for LMoveDirection"],["impl PartialEq for ListLocation"],["impl PartialEq for Load"],["impl PartialEq for MessageKind"],["impl PartialEq for Ordering"],["impl PartialEq for ReconnectError"],["impl PartialEq for ReconnectPolicy"],["impl PartialEq for RedisValue"],["impl PartialEq for RedisValueKind"],["impl PartialEq for Reducer"],["impl PartialEq for ReducerFunc"],["impl PartialEq for ScanType"],["impl PartialEq for ScriptDebugFlag"],["impl PartialEq for SentinelFailureKind"],["impl PartialEq for ServerConfig"],["impl PartialEq for SetOptions"],["impl PartialEq for ShutdownFlags"],["impl PartialEq for SortOrder"],["impl PartialEq for StringOrNumber"],["impl PartialEq for Timestamp"],["impl PartialEq for TlsConnector"],["impl PartialEq for TlsHostMapping"],["impl PartialEq for Toggle"],["impl PartialEq for XCapKind"],["impl PartialEq for XCapTrim"],["impl PartialEq for XID"],["impl PartialEq for ZCmp"],["impl PartialEq for ZRangeKind"],["impl PartialEq for ZSort"],["impl PartialEq for Transaction"],["impl PartialEq for RedisError"],["impl PartialEq for Command"],["impl PartialEq for BackpressureConfig"],["impl PartialEq for ClusterInfo"],["impl PartialEq for ConnectionConfig"],["impl PartialEq for CustomCommand"],["impl PartialEq for DatabaseMemoryStats"],["impl PartialEq for Function"],["impl PartialEq for GeoPosition"],["impl PartialEq for GeoRadiusInfo"],["impl PartialEq for GeoValue"],["impl PartialEq for GroupBy"],["impl PartialEq for Invalidation"],["impl PartialEq for KeyspaceEvent"],["impl PartialEq for Library"],["impl PartialEq for MemoryStats"],["impl PartialEq for Message"],["impl PartialEq for MultipleGeoValues"],["impl PartialEq for MultipleIDs"],["impl PartialEq for MultipleKeys"],["impl PartialEq for MultipleOrderedPairs"],["impl PartialEq for Options"],["impl PartialEq for PerformanceConfig"],["impl PartialEq for RangeAggregation"],["impl PartialEq for RedisConfig"],["impl PartialEq for RedisKey"],["impl PartialEq for RedisMap"],["impl PartialEq for ReplicaConfig"],["impl PartialEq for Script"],["impl PartialEq for SearchField"],["impl PartialEq for SearchHighlight"],["impl PartialEq for SearchParameter"],["impl PartialEq for SearchReducer"],["impl PartialEq for SearchSortBy"],["impl PartialEq for SearchSummarize"],["impl PartialEq for Server"],["impl PartialEq for SlotRange"],["impl PartialEq for SlowlogEntry"],["impl PartialEq for TcpConfig"],["impl PartialEq for TlsConfig"],["impl PartialEq for UnresponsiveConfig"],["impl PartialEq for WithCursor"],["impl PartialEq for XCap"],["impl PartialEq for XPendingArgs"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[28792]} \ No newline at end of file diff --git a/doc/trait.impl/core/cmp/trait.PartialOrd.js b/doc/trait.impl/core/cmp/trait.PartialOrd.js new file mode 100644 index 00000000..4ddee91a --- /dev/null +++ b/doc/trait.impl/core/cmp/trait.PartialOrd.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl PartialOrd for FunctionFlag"],["impl PartialOrd for Function"],["impl PartialOrd for KeyspaceEvent"],["impl PartialOrd for Library"],["impl PartialOrd for RedisKey"],["impl PartialOrd for Script"],["impl PartialOrd for Server"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1920]} \ No newline at end of file diff --git a/doc/trait.impl/core/convert/trait.From.js b/doc/trait.impl/core/convert/trait.From.js new file mode 100644 index 00000000..8a9644ce --- /dev/null +++ b/doc/trait.impl/core/convert/trait.From.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl From<&str> for ClusterHash"],["impl From<&str> for RedisKey"],["impl From<&RedisKey> for RedisKey"],["impl From<&Server> for Server"],["impl From<&String> for RedisKey"],["impl From<&StrInner<Bytes>> for RedisKey"],["impl From<&[u8]> for ClusterHash"],["impl From<(&str, u16)> for Server"],["impl From<(Aggregator, u64)> for RangeAggregation"],["impl From<(f64, f64)> for GeoPosition"],["impl From<(String, u16)> for Server"],["impl From<Option<f64>> for MultipleWeights"],["impl From<Option<u16>> for ClusterHash"],["impl From<Option<()>> for XCap"],["impl From<Option<RedisKey>> for MultipleKeys"],["impl From<bool> for RedisValue"],["impl From<bool> for Toggle"],["impl From<bool> for RedisKey"],["impl From<f32> for RedisValue"],["impl From<f32> for StringOrNumber"],["impl From<f32> for RedisKey"],["impl From<f64> for RedisValue"],["impl From<f64> for StringOrNumber"],["impl From<f64> for MultipleWeights"],["impl From<f64> for RedisKey"],["impl From<i128> for RedisKey"],["impl From<i16> for RedisValue"],["impl From<i16> for StringOrNumber"],["impl From<i16> for RedisKey"],["impl From<i32> for RedisValue"],["impl From<i32> for StringOrNumber"],["impl From<i32> for RedisKey"],["impl From<i64> for GetTimestamp"],["impl From<i64> for RedisValue"],["impl From<i64> for StringOrNumber"],["impl From<i64> for Timestamp"],["impl From<i64> for ZRangeBound"],["impl From<i64> for RedisKey"],["impl From<i64> for ZRange"],["impl From<i8> for RedisValue"],["impl From<i8> for StringOrNumber"],["impl From<i8> for RedisKey"],["impl From<isize> for StringOrNumber"],["impl From<isize> for RedisKey"],["impl From<u128> for RedisKey"],["impl From<u16> for ClusterHash"],["impl From<u16> for RedisValue"],["impl From<u16> for StringOrNumber"],["impl From<u16> for MultipleHashSlots"],["impl From<u16> for RedisKey"],["impl From<u32> for RedisValue"],["impl From<u32> for StringOrNumber"],["impl From<u32> for RedisKey"],["impl From<u64> for StringOrNumber"],["impl From<u64> for RedisKey"],["impl From<u8> for RedisValue"],["impl From<u8> for StringOrNumber"],["impl From<u8> for RedisKey"],["impl From<()> for RedisValue"],["impl From<()> for MultipleKeys"],["impl From<()> for MultipleOrderedPairs"],["impl From<()> for RedisMap"],["impl From<()> for XPendingArgs"],["impl From<usize> for StringOrNumber"],["impl From<usize> for RedisKey"],["impl From<GeoValue> for MultipleGeoValues"],["impl From<RedisKey> for RedisValue"],["impl From<RedisMap> for RedisValue"],["impl From<Box<[u8]>> for RedisValue"],["impl From<Box<[u8]>> for RedisKey"],["impl From<VecDeque<f64>> for MultipleWeights"],["impl From<VecDeque<u16>> for MultipleHashSlots"],["impl From<VecDeque<GeoValue>> for MultipleGeoValues"],["impl From<String> for RedisValue"],["impl From<String> for StringOrNumber"],["impl From<String> for XID"],["impl From<String> for ZRangeBound"],["impl From<String> for RedisKey"],["impl From<String> for ZRange"],["impl From<Vec<f64>> for MultipleWeights"],["impl From<Vec<u16>> for MultipleHashSlots"],["impl From<Vec<GeoValue>> for MultipleGeoValues"],["impl From<Error> for RedisError"],["impl From<TlsConnector> for TlsConnector"],["impl From<Bytes> for RedisValue"],["impl From<Bytes> for RedisKey"],["impl From<ClientConfig> for TlsConnector"],["impl From<StrInner<Bytes>> for RedisValue"],["impl From<StrInner<Bytes>> for StringOrNumber"],["impl From<StrInner<Bytes>> for XID"],["impl From<StrInner<Bytes>> for RedisKey"],["impl From<TlsConnector> for TlsConnector"],["impl From<TlsConnector> for TlsConnector"],["impl<'a> From<&'a str> for RedisValue"],["impl<'a> From<&'a str> for StringOrNumber"],["impl<'a> From<&'a str> for XID"],["impl<'a> From<&'a str> for ZRangeBound"],["impl<'a> From<&'a str> for ZRange"],["impl<'a> From<&'a RedisMap> for RedisMap"],["impl<'a> From<&'a ZRange> for ZRange"],["impl<'a> From<&'a String> for RedisValue"],["impl<'a> From<&'a String> for XID"],["impl<'a> From<&'a String> for ZRangeBound"],["impl<'a> From<&'a String> for ZRange"],["impl<'a> From<&'a [u16]> for MultipleHashSlots"],["impl<'a> From<&'a [u8]> for RedisValue"],["impl<'a> From<&'a [u8]> for RedisKey"],["impl<'a, K, const N: usize> From<&'a [K; N]> for MultipleKeys
where\n K: Into<RedisKey> + Clone,
"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>> From<(A0, A1)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>> From<(A0, A1, A2)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>> From<(A0, A1, A2, A3)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>> From<(A0, A1, A2, A3, A4)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>, A13: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>, A13: Into<RedisValue>, A14: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>, A13: Into<RedisValue>, A14: Into<RedisValue>, A15: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)> for RedisValue"],["impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>, A13: Into<RedisValue>, A14: Into<RedisValue>, A15: Into<RedisValue>, A16: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16)> for RedisValue"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>> From<(A0, A1)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>> From<(A0, A1, A2)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>> From<(A0, A1, A2, A3)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>> From<(A0, A1, A2, A3, A4)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>, A13: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>, A13: Into<RedisKey>, A14: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>, A13: Into<RedisKey>, A14: Into<RedisKey>, A15: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)> for MultipleKeys"],["impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>, A13: Into<RedisKey>, A14: Into<RedisKey>, A15: Into<RedisKey>, A16: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16)> for MultipleKeys"],["impl<C: Into<TlsConnector>> From<C> for TlsConfig"],["impl<S> From<Vec<S>> for GetLabels
where\n S: Into<Str>,
"],["impl<S, E> From<(u64, S, E, u64)> for XPendingArgs
where\n S: Into<XID>,\n E: Into<XID>,
"],["impl<S, E> From<(S, E, u64)> for XPendingArgs
where\n S: Into<XID>,\n E: Into<XID>,
"],["impl<S, E, C> From<(u64, S, E, u64, C)> for XPendingArgs
where\n S: Into<XID>,\n E: Into<XID>,\n C: Into<Str>,
"],["impl<S, E, C> From<(S, E, u64, C)> for XPendingArgs
where\n S: Into<XID>,\n E: Into<XID>,\n C: Into<Str>,
"],["impl<S, const N: usize> From<[S; N]> for GetLabels
where\n S: Into<Str>,
"],["impl<S: Into<Str>> From<(S, Reducer)> for GroupBy"],["impl<T> From<VecDeque<T>> for MultipleIDs
where\n T: Into<XID>,
"],["impl<T> From<VecDeque<T>> for MultipleKeys
where\n T: Into<RedisKey>,
"],["impl<T> From<Vec<T>> for MultipleIDs
where\n T: Into<XID>,
"],["impl<T> From<Vec<T>> for MultipleKeys
where\n T: Into<RedisKey>,
"],["impl<T> From<T> for MultipleIDs
where\n T: Into<XID>,
"],["impl<T> From<T> for MultipleKeys
where\n T: Into<RedisKey>,
"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[146585]} \ No newline at end of file diff --git a/doc/trait.impl/core/convert/trait.TryFrom.js b/doc/trait.impl/core/convert/trait.TryFrom.js new file mode 100644 index 00000000..48debc4b --- /dev/null +++ b/doc/trait.impl/core/convert/trait.TryFrom.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl TryFrom<&str> for BucketTimestamp"],["impl TryFrom<&str> for FnPolicy"],["impl TryFrom<&str> for GetTimestamp"],["impl TryFrom<&str> for Timestamp"],["impl TryFrom<&str> for Toggle"],["impl TryFrom<&str> for Server"],["impl TryFrom<&String> for FnPolicy"],["impl TryFrom<&String> for Toggle"],["impl TryFrom<&StrInner<Bytes>> for FnPolicy"],["impl TryFrom<RedisValue> for StringOrNumber"],["impl TryFrom<RedisValue> for ClusterInfo"],["impl TryFrom<RedisValue> for DatabaseMemoryStats"],["impl TryFrom<RedisValue> for GeoPosition"],["impl TryFrom<RedisValue> for MemoryStats"],["impl TryFrom<RedisValue> for RedisKey"],["impl TryFrom<RedisValue> for SlowlogEntry"],["impl TryFrom<BytesFrame> for RedisValue"],["impl TryFrom<f64> for ZRangeBound"],["impl TryFrom<f64> for ZRange"],["impl TryFrom<i128> for RedisValue"],["impl TryFrom<u128> for RedisValue"],["impl TryFrom<u64> for RedisValue"],["impl TryFrom<usize> for RedisValue"],["impl TryFrom<String> for FnPolicy"],["impl TryFrom<String> for Timestamp"],["impl TryFrom<String> for Toggle"],["impl TryFrom<String> for Server"],["impl TryFrom<StrInner<Bytes>> for FnPolicy"],["impl TryFrom<StrInner<Bytes>> for Timestamp"],["impl TryFrom<TlsConnectorBuilder> for TlsConnector"],["impl<'a> TryFrom<&'a str> for XCapKind"],["impl<'a> TryFrom<&'a str> for XCapTrim"],["impl<'a, K, V, const N: usize> TryFrom<&'a [(K, V); N]> for RedisMap"],["impl<'a, T, const N: usize> TryFrom<&'a [T; N]> for RedisValue
where\n T: TryInto<RedisValue> + Clone,\n T::Error: Into<RedisError>,
"],["impl<K, S> TryFrom<(K, S)> for XCap"],["impl<K, T, S> TryFrom<(K, T, S, Option<i64>)> for XCap"],["impl<K, T, S> TryFrom<(K, T, S)> for XCap"],["impl<K, V> TryFrom<(K, V)> for MultipleOrderedPairs
where\n K: Into<RedisKey>,\n V: TryInto<RedisValue>,\n V::Error: Into<RedisError>,
"],["impl<K, V> TryFrom<(K, V)> for RedisMap"],["impl<K, V> TryFrom<BTreeMap<K, V>> for RedisValue"],["impl<K, V> TryFrom<BTreeMap<K, V>> for RedisMap"],["impl<K, V> TryFrom<VecDeque<(K, V)>> for MultipleOrderedPairs
where\n K: Into<RedisKey>,\n V: TryInto<RedisValue>,\n V::Error: Into<RedisError>,
"],["impl<K, V> TryFrom<VecDeque<(K, V)>> for RedisMap"],["impl<K, V> TryFrom<Vec<(K, V)>> for MultipleOrderedPairs
where\n K: Into<RedisKey>,\n V: TryInto<RedisValue>,\n V::Error: Into<RedisError>,
"],["impl<K, V> TryFrom<Vec<(K, V)>> for RedisMap"],["impl<K, V> TryFrom<HashMap<K, V>> for RedisValue"],["impl<K, V> TryFrom<HashMap<K, V>> for MultipleOrderedPairs
where\n K: Into<RedisKey>,\n V: TryInto<RedisValue>,\n V::Error: Into<RedisError>,
"],["impl<K, V> TryFrom<HashMap<K, V>> for RedisMap"],["impl<K, V, const N: usize> TryFrom<[(K, V); N]> for RedisMap"],["impl<T> TryFrom<(f64, f64, T)> for GeoValue
where\n T: TryInto<RedisValue>,\n T::Error: Into<RedisError>,
"],["impl<T> TryFrom<(f64, T)> for MultipleZaddValues
where\n T: TryInto<RedisValue>,\n T::Error: Into<RedisError>,
"],["impl<T> TryFrom<Option<T>> for RedisValue
where\n T: TryInto<RedisValue>,\n T::Error: Into<RedisError>,
"],["impl<T> TryFrom<VecDeque<(f64, T)>> for MultipleZaddValues
where\n T: TryInto<RedisValue>,\n T::Error: Into<RedisError>,
"],["impl<T> TryFrom<VecDeque<T>> for RedisValue
where\n T: TryInto<RedisValue>,\n T::Error: Into<RedisError>,
"],["impl<T> TryFrom<Vec<(f64, T)>> for MultipleZaddValues
where\n T: TryInto<RedisValue>,\n T::Error: Into<RedisError>,
"],["impl<T> TryFrom<Vec<T>> for RedisValue
where\n T: TryInto<RedisValue>,\n T::Error: Into<RedisError>,
"],["impl<T, const N: usize> TryFrom<[T; N]> for RedisValue
where\n T: TryInto<RedisValue> + Clone,\n T::Error: Into<RedisError>,
"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[54013]} \ No newline at end of file diff --git a/doc/trait.impl/core/default/trait.Default.js b/doc/trait.impl/core/default/trait.Default.js new file mode 100644 index 00000000..d3f17dd8 --- /dev/null +++ b/doc/trait.impl/core/default/trait.Default.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl Default for BackpressurePolicy"],["impl Default for Blocking"],["impl Default for ClusterDiscoveryPolicy"],["impl Default for ClusterHash"],["impl Default for ClusterState"],["impl Default for FnPolicy"],["impl Default for ReconnectPolicy"],["impl Default for ServerConfig"],["impl Default for Timestamp"],["impl Default for ZRangeKind"],["impl Default for RedisClient"],["impl Default for BackpressureConfig"],["impl Default for Builder"],["impl Default for ClusterInfo"],["impl Default for ConnectionConfig"],["impl Default for DatabaseMemoryStats"],["impl Default for FtAggregateOptions"],["impl Default for FtCreateOptions"],["impl Default for FtSearchOptions"],["impl Default for GeoRadiusInfo"],["impl Default for MemoryStats"],["impl Default for Options"],["impl Default for PerformanceConfig"],["impl Default for RedisConfig"],["impl Default for ReplicaConfig"],["impl Default for SearchHighlight"],["impl Default for SearchSummarize"],["impl Default for SentinelConfig"],["impl Default for TcpConfig"],["impl Default for TracingConfig"],["impl Default for UnresponsiveConfig"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[8844]} \ No newline at end of file diff --git a/doc/trait.impl/core/error/trait.Error.js b/doc/trait.impl/core/error/trait.Error.js new file mode 100644 index 00000000..855480d1 --- /dev/null +++ b/doc/trait.impl/core/error/trait.Error.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl Error for RedisError"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[277]} \ No newline at end of file diff --git a/doc/trait.impl/core/fmt/trait.Debug.js b/doc/trait.impl/core/fmt/trait.Debug.js new file mode 100644 index 00000000..ce0f74b6 --- /dev/null +++ b/doc/trait.impl/core/fmt/trait.Debug.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl Debug for RedisErrorKind"],["impl Debug for AggregateOperation"],["impl Debug for Aggregator"],["impl Debug for BackpressurePolicy"],["impl Debug for Blocking"],["impl Debug for BucketTimestamp"],["impl Debug for ClientKillFilter"],["impl Debug for ClientKillType"],["impl Debug for ClientPauseKind"],["impl Debug for ClientReplyFlag"],["impl Debug for ClientState"],["impl Debug for ClientUnblockFlag"],["impl Debug for ClusterDiscoveryPolicy"],["impl Debug for ClusterFailoverFlag"],["impl Debug for ClusterHash"],["impl Debug for ClusterResetFlag"],["impl Debug for ClusterSetSlotState"],["impl Debug for ClusterState"],["impl Debug for ClusterStateChange"],["impl Debug for DuplicatePolicy"],["impl Debug for Encoding"],["impl Debug for Expiration"],["impl Debug for ExpireOptions"],["impl Debug for FnPolicy"],["impl Debug for FunctionFlag"],["impl Debug for GeoUnit"],["impl Debug for GetLabels"],["impl Debug for GetTimestamp"],["impl Debug for IndexKind"],["impl Debug for InfoKind"],["impl Debug for LMoveDirection"],["impl Debug for ListLocation"],["impl Debug for Load"],["impl Debug for MessageKind"],["impl Debug for Ordering"],["impl Debug for ReconnectError"],["impl Debug for ReconnectPolicy"],["impl Debug for RedisValue"],["impl Debug for RedisValueKind"],["impl Debug for Reducer"],["impl Debug for ReducerFunc"],["impl Debug for ScanType"],["impl Debug for ScriptDebugFlag"],["impl Debug for SearchSchemaKind"],["impl Debug for SentinelFailureKind"],["impl Debug for ServerConfig"],["impl Debug for SetOptions"],["impl Debug for ShutdownFlags"],["impl Debug for SortOrder"],["impl Debug for SpellcheckTerms"],["impl Debug for StringOrNumber"],["impl Debug for Timestamp"],["impl Debug for TlsConnector"],["impl Debug for TlsHostMapping"],["impl Debug for Toggle"],["impl Debug for XCapKind"],["impl Debug for XCapTrim"],["impl Debug for XID"],["impl Debug for ZCmp"],["impl Debug for ZRangeBound"],["impl Debug for ZRangeKind"],["impl Debug for ZSort"],["impl Debug for RedisClient"],["impl Debug for RedisPool"],["impl Debug for Replicas"],["impl Debug for SentinelClient"],["impl Debug for SubscriberClient"],["impl Debug for Transaction"],["impl Debug for RedisError"],["impl Debug for Command"],["impl Debug for BackpressureConfig"],["impl Debug for Builder"],["impl Debug for ClusterInfo"],["impl Debug for ClusterRouting"],["impl Debug for ConnectionConfig"],["impl Debug for CustomCommand"],["impl Debug for DatabaseMemoryStats"],["impl Debug for FtAggregateOptions"],["impl Debug for FtAlterOptions"],["impl Debug for FtCreateOptions"],["impl Debug for FtSearchOptions"],["impl Debug for Function"],["impl Debug for GeoPosition"],["impl Debug for GeoRadiusInfo"],["impl Debug for GeoValue"],["impl Debug for GroupBy"],["impl Debug for Invalidation"],["impl Debug for KeyspaceEvent"],["impl Debug for Library"],["impl Debug for MemoryStats"],["impl Debug for Message"],["impl Debug for MultipleGeoValues"],["impl Debug for MultipleIDs"],["impl Debug for MultipleKeys"],["impl Debug for MultipleOrderedPairs"],["impl Debug for Options"],["impl Debug for PerformanceConfig"],["impl Debug for RangeAggregation"],["impl Debug for RedisConfig"],["impl Debug for RedisKey"],["impl Debug for RedisMap"],["impl Debug for ReplicaConfig"],["impl Debug for Script"],["impl Debug for SearchField"],["impl Debug for SearchFilter"],["impl Debug for SearchGeoFilter"],["impl Debug for SearchHighlight"],["impl Debug for SearchParameter"],["impl Debug for SearchReducer"],["impl Debug for SearchSchema"],["impl Debug for SearchSortBy"],["impl Debug for SearchSummarize"],["impl Debug for SentinelConfig"],["impl Debug for Server"],["impl Debug for SlotRange"],["impl Debug for SlowlogEntry"],["impl Debug for TcpConfig"],["impl Debug for TlsConfig"],["impl Debug for TracingConfig"],["impl Debug for UnresponsiveConfig"],["impl Debug for WithCursor"],["impl Debug for XCap"],["impl Debug for XPendingArgs"],["impl Debug for ZRange"],["impl<C: ClientLike> Debug for Pipeline<C>"],["impl<C: ClientLike> Debug for WithOptions<C>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[33792]} \ No newline at end of file diff --git a/doc/trait.impl/core/fmt/trait.Display.js b/doc/trait.impl/core/fmt/trait.Display.js new file mode 100644 index 00000000..e4692a35 --- /dev/null +++ b/doc/trait.impl/core/fmt/trait.Display.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl Display for ClientState"],["impl Display for RedisValueKind"],["impl Display for RedisClient"],["impl Display for RedisError"],["impl Display for Command"],["impl Display for Function"],["impl Display for Library"],["impl Display for Script"],["impl Display for Server"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[2397]} \ No newline at end of file diff --git a/doc/trait.impl/core/hash/trait.Hash.js b/doc/trait.impl/core/hash/trait.Hash.js new file mode 100644 index 00000000..d1351687 --- /dev/null +++ b/doc/trait.impl/core/hash/trait.Hash.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl Hash for FunctionFlag"],["impl Hash for RedisValue"],["impl Hash for Function"],["impl Hash for KeyspaceEvent"],["impl Hash for Library"],["impl Hash for RedisKey"],["impl Hash for Script"],["impl Hash for Server"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[2064]} \ No newline at end of file diff --git a/doc/trait.impl/core/iter/traits/collect/trait.FromIterator.js b/doc/trait.impl/core/iter/traits/collect/trait.FromIterator.js new file mode 100644 index 00000000..f3f037da --- /dev/null +++ b/doc/trait.impl/core/iter/traits/collect/trait.FromIterator.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl FromIterator<f64> for MultipleWeights"],["impl FromIterator<u16> for MultipleHashSlots"],["impl<K, V> FromIterator<(K, V)> for RedisMap
where\n K: Into<RedisKey>,\n V: Into<RedisValue>,
"],["impl<S> FromIterator<S> for GetLabels
where\n S: Into<Str>,
"],["impl<T> FromIterator<(f64, T)> for MultipleZaddValues
where\n T: Into<RedisValue>,
"],["impl<T> FromIterator<T> for MultipleKeys
where\n T: Into<RedisKey>,
"],["impl<V> FromIterator<V> for RedisValue
where\n V: Into<RedisValue>,
"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[4423]} \ No newline at end of file diff --git a/doc/trait.impl/core/marker/trait.Freeze.js b/doc/trait.impl/core/marker/trait.Freeze.js new file mode 100644 index 00000000..ed0984a6 --- /dev/null +++ b/doc/trait.impl/core/marker/trait.Freeze.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl !Freeze for AggregateOperation",1,["fred::types::redisearch::AggregateOperation"]],["impl !Freeze for ClusterStateChange",1,["fred::types::misc::ClusterStateChange"]],["impl !Freeze for RedisValue",1,["fred::types::args::RedisValue"]],["impl !Freeze for SearchSchemaKind",1,["fred::types::redisearch::SearchSchemaKind"]],["impl !Freeze for ServerConfig",1,["fred::types::config::ServerConfig"]],["impl !Freeze for SpellcheckTerms",1,["fred::types::redisearch::SpellcheckTerms"]],["impl !Freeze for StringOrNumber",1,["fred::types::args::StringOrNumber"]],["impl !Freeze for XID",1,["fred::types::streams::XID"]],["impl !Freeze for Builder",1,["fred::types::builder::Builder"]],["impl !Freeze for CustomCommand",1,["fred::types::misc::CustomCommand"]],["impl !Freeze for FtAlterOptions",1,["fred::types::redisearch::FtAlterOptions"]],["impl !Freeze for FtCreateOptions",1,["fred::types::redisearch::FtCreateOptions"]],["impl !Freeze for FtSearchOptions",1,["fred::types::redisearch::FtSearchOptions"]],["impl !Freeze for Function",1,["fred::types::scripts::Function"]],["impl !Freeze for GeoRadiusInfo",1,["fred::types::geo::GeoRadiusInfo"]],["impl !Freeze for GeoValue",1,["fred::types::geo::GeoValue"]],["impl !Freeze for GroupBy",1,["fred::types::timeseries::GroupBy"]],["impl !Freeze for Invalidation",1,["fred::types::client::Invalidation"]],["impl !Freeze for KeyspaceEvent",1,["fred::types::misc::KeyspaceEvent"]],["impl !Freeze for Library",1,["fred::types::scripts::Library"]],["impl !Freeze for Message",1,["fred::protocol::types::Message"]],["impl !Freeze for Options",1,["fred::types::config::Options"]],["impl !Freeze for RedisConfig",1,["fred::types::config::RedisConfig"]],["impl !Freeze for RedisKey",1,["fred::types::args::RedisKey"]],["impl !Freeze for ScanResult",1,["fred::types::scan::ScanResult"]],["impl !Freeze for Script",1,["fred::types::scripts::Script"]],["impl !Freeze for SearchField",1,["fred::types::redisearch::SearchField"]],["impl !Freeze for SearchFilter",1,["fred::types::redisearch::SearchFilter"]],["impl !Freeze for SearchGeoFilter",1,["fred::types::redisearch::SearchGeoFilter"]],["impl !Freeze for SearchHighlight",1,["fred::types::redisearch::SearchHighlight"]],["impl !Freeze for SearchParameter",1,["fred::types::redisearch::SearchParameter"]],["impl !Freeze for SearchReducer",1,["fred::types::redisearch::SearchReducer"]],["impl !Freeze for SearchSchema",1,["fred::types::redisearch::SearchSchema"]],["impl !Freeze for SearchSortBy",1,["fred::types::redisearch::SearchSortBy"]],["impl !Freeze for SearchSummarize",1,["fred::types::redisearch::SearchSummarize"]],["impl !Freeze for Server",1,["fred::protocol::types::Server"]],["impl !Freeze for SlotRange",1,["fred::protocol::types::SlotRange"]],["impl !Freeze for SlowlogEntry",1,["fred::types::misc::SlowlogEntry"]],["impl !Freeze for XCap",1,["fred::types::streams::XCap"]],["impl !Freeze for XPendingArgs",1,["fred::types::streams::XPendingArgs"]],["impl Freeze for RedisErrorKind",1,["fred::error::RedisErrorKind"]],["impl Freeze for AggregateOptions",1,["fred::types::misc::AggregateOptions"]],["impl Freeze for Aggregator",1,["fred::types::timeseries::Aggregator"]],["impl Freeze for BackpressurePolicy",1,["fred::types::config::BackpressurePolicy"]],["impl Freeze for Blocking",1,["fred::types::config::Blocking"]],["impl Freeze for BucketTimestamp",1,["fred::types::timeseries::BucketTimestamp"]],["impl Freeze for ClientKillFilter",1,["fred::types::client::ClientKillFilter"]],["impl Freeze for ClientKillType",1,["fred::types::client::ClientKillType"]],["impl Freeze for ClientPauseKind",1,["fred::types::client::ClientPauseKind"]],["impl Freeze for ClientReplyFlag",1,["fred::types::client::ClientReplyFlag"]],["impl Freeze for ClientState",1,["fred::types::misc::ClientState"]],["impl Freeze for ClientUnblockFlag",1,["fred::types::misc::ClientUnblockFlag"]],["impl Freeze for ClusterDiscoveryPolicy",1,["fred::types::config::ClusterDiscoveryPolicy"]],["impl Freeze for ClusterFailoverFlag",1,["fred::types::cluster::ClusterFailoverFlag"]],["impl Freeze for ClusterHash",1,["fred::protocol::hashers::ClusterHash"]],["impl Freeze for ClusterResetFlag",1,["fred::types::cluster::ClusterResetFlag"]],["impl Freeze for ClusterSetSlotState",1,["fred::types::cluster::ClusterSetSlotState"]],["impl Freeze for ClusterState",1,["fred::types::cluster::ClusterState"]],["impl Freeze for DuplicatePolicy",1,["fred::types::timeseries::DuplicatePolicy"]],["impl Freeze for Encoding",1,["fred::types::timeseries::Encoding"]],["impl Freeze for Expiration",1,["fred::types::misc::Expiration"]],["impl Freeze for ExpireOptions",1,["fred::types::misc::ExpireOptions"]],["impl Freeze for FnPolicy",1,["fred::types::misc::FnPolicy"]],["impl Freeze for FunctionFlag",1,["fred::types::scripts::FunctionFlag"]],["impl Freeze for GeoUnit",1,["fred::types::geo::GeoUnit"]],["impl Freeze for GetLabels",1,["fred::types::timeseries::GetLabels"]],["impl Freeze for GetTimestamp",1,["fred::types::timeseries::GetTimestamp"]],["impl Freeze for IndexKind",1,["fred::types::redisearch::IndexKind"]],["impl Freeze for InfoKind",1,["fred::types::misc::InfoKind"]],["impl Freeze for LMoveDirection",1,["fred::types::lists::LMoveDirection"]],["impl Freeze for ListLocation",1,["fred::types::lists::ListLocation"]],["impl Freeze for Load",1,["fred::types::redisearch::Load"]],["impl Freeze for MessageKind",1,["fred::protocol::types::MessageKind"]],["impl Freeze for Ordering",1,["fred::types::sorted_sets::Ordering"]],["impl Freeze for ReconnectError",1,["fred::types::config::ReconnectError"]],["impl Freeze for ReconnectPolicy",1,["fred::types::config::ReconnectPolicy"]],["impl Freeze for RedisValueKind",1,["fred::types::args::RedisValueKind"]],["impl Freeze for Reducer",1,["fred::types::timeseries::Reducer"]],["impl Freeze for ReducerFunc",1,["fred::types::redisearch::ReducerFunc"]],["impl Freeze for ScanType",1,["fred::types::scan::ScanType"]],["impl Freeze for ScriptDebugFlag",1,["fred::types::misc::ScriptDebugFlag"]],["impl Freeze for SentinelFailureKind",1,["fred::types::misc::SentinelFailureKind"]],["impl Freeze for SetOptions",1,["fred::types::misc::SetOptions"]],["impl Freeze for ShutdownFlags",1,["fred::types::misc::ShutdownFlags"]],["impl Freeze for SortOrder",1,["fred::types::misc::SortOrder"]],["impl Freeze for Timestamp",1,["fred::types::timeseries::Timestamp"]],["impl Freeze for TlsConnector",1,["fred::protocol::tls::TlsConnector"]],["impl Freeze for TlsHostMapping",1,["fred::protocol::tls::TlsHostMapping"]],["impl Freeze for Toggle",1,["fred::types::client::Toggle"]],["impl Freeze for XCapKind",1,["fred::types::streams::XCapKind"]],["impl Freeze for XCapTrim",1,["fred::types::streams::XCapTrim"]],["impl Freeze for ZCmp",1,["fred::types::sorted_sets::ZCmp"]],["impl Freeze for ZRangeBound",1,["fred::types::sorted_sets::ZRangeBound"]],["impl Freeze for ZRangeKind",1,["fred::types::sorted_sets::ZRangeKind"]],["impl Freeze for ZSort",1,["fred::types::sorted_sets::ZSort"]],["impl Freeze for RedisClient",1,["fred::clients::redis::RedisClient"]],["impl Freeze for RedisPool",1,["fred::clients::pool::RedisPool"]],["impl Freeze for Replicas",1,["fred::clients::replica::Replicas"]],["impl Freeze for SentinelClient",1,["fred::clients::sentinel::SentinelClient"]],["impl Freeze for SubscriberClient",1,["fred::clients::pubsub::SubscriberClient"]],["impl Freeze for Transaction",1,["fred::clients::transaction::Transaction"]],["impl Freeze for RedisError",1,["fred::error::RedisError"]],["impl Freeze for Command",1,["fred::monitor::Command"]],["impl Freeze for BackpressureConfig",1,["fred::types::config::BackpressureConfig"]],["impl Freeze for ClusterInfo",1,["fred::types::cluster::ClusterInfo"]],["impl Freeze for ClusterRouting",1,["fred::protocol::types::ClusterRouting"]],["impl Freeze for ConnectionConfig",1,["fred::types::config::ConnectionConfig"]],["impl Freeze for DatabaseMemoryStats",1,["fred::types::misc::DatabaseMemoryStats"]],["impl Freeze for FtAggregateOptions",1,["fred::types::redisearch::FtAggregateOptions"]],["impl Freeze for GeoPosition",1,["fred::types::geo::GeoPosition"]],["impl Freeze for HScanResult",1,["fred::types::scan::HScanResult"]],["impl Freeze for MemoryStats",1,["fred::types::misc::MemoryStats"]],["impl Freeze for MultipleGeoValues",1,["fred::types::geo::MultipleGeoValues"]],["impl Freeze for MultipleHashSlots",1,["fred::types::multiple::MultipleHashSlots"]],["impl Freeze for MultipleIDs",1,["fred::types::streams::MultipleIDs"]],["impl Freeze for MultipleKeys",1,["fred::types::multiple::MultipleKeys"]],["impl Freeze for MultipleOrderedPairs",1,["fred::types::streams::MultipleOrderedPairs"]],["impl Freeze for MultipleWeights",1,["fred::types::sorted_sets::MultipleWeights"]],["impl Freeze for MultipleZaddValues",1,["fred::types::sorted_sets::MultipleZaddValues"]],["impl Freeze for PerformanceConfig",1,["fred::types::config::PerformanceConfig"]],["impl Freeze for RangeAggregation",1,["fred::types::timeseries::RangeAggregation"]],["impl Freeze for RedisMap",1,["fred::types::args::RedisMap"]],["impl Freeze for ReplicaConfig",1,["fred::router::replicas::ReplicaConfig"]],["impl Freeze for SScanResult",1,["fred::types::scan::SScanResult"]],["impl Freeze for SentinelConfig",1,["fred::types::config::SentinelConfig"]],["impl Freeze for Stats",1,["fred::modules::metrics::Stats"]],["impl Freeze for TcpConfig",1,["fred::types::config::TcpConfig"]],["impl Freeze for TlsConfig",1,["fred::protocol::tls::TlsConfig"]],["impl Freeze for TracingConfig",1,["fred::types::config::TracingConfig"]],["impl Freeze for UnresponsiveConfig",1,["fred::types::config::UnresponsiveConfig"]],["impl Freeze for WithCursor",1,["fred::types::redisearch::WithCursor"]],["impl Freeze for ZRange",1,["fred::types::sorted_sets::ZRange"]],["impl Freeze for ZScanResult",1,["fred::types::scan::ZScanResult"]],["impl<C> !Freeze for WithOptions<C>",1,["fred::clients::options::WithOptions"]],["impl<C> Freeze for Pipeline<C>
where\n C: Freeze,
",1,["fred::clients::pipeline::Pipeline"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[42922]} \ No newline at end of file diff --git a/doc/trait.impl/core/marker/trait.Send.js b/doc/trait.impl/core/marker/trait.Send.js new file mode 100644 index 00000000..cf05467f --- /dev/null +++ b/doc/trait.impl/core/marker/trait.Send.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl !Send for RedisClient",1,["fred::clients::redis::RedisClient"]],["impl !Send for RedisPool",1,["fred::clients::pool::RedisPool"]],["impl !Send for Replicas",1,["fred::clients::replica::Replicas"]],["impl !Send for SentinelClient",1,["fred::clients::sentinel::SentinelClient"]],["impl !Send for SubscriberClient",1,["fred::clients::pubsub::SubscriberClient"]],["impl !Send for Transaction",1,["fred::clients::transaction::Transaction"]],["impl !Send for Builder",1,["fred::types::builder::Builder"]],["impl !Send for ConnectionConfig",1,["fred::types::config::ConnectionConfig"]],["impl !Send for HScanResult",1,["fred::types::scan::HScanResult"]],["impl !Send for ReplicaConfig",1,["fred::router::replicas::ReplicaConfig"]],["impl !Send for SScanResult",1,["fred::types::scan::SScanResult"]],["impl !Send for ScanResult",1,["fred::types::scan::ScanResult"]],["impl !Send for ZScanResult",1,["fred::types::scan::ZScanResult"]],["impl Send for RedisErrorKind",1,["fred::error::RedisErrorKind"]],["impl Send for AggregateOperation",1,["fred::types::redisearch::AggregateOperation"]],["impl Send for AggregateOptions",1,["fred::types::misc::AggregateOptions"]],["impl Send for Aggregator",1,["fred::types::timeseries::Aggregator"]],["impl Send for BackpressurePolicy",1,["fred::types::config::BackpressurePolicy"]],["impl Send for Blocking",1,["fred::types::config::Blocking"]],["impl Send for BucketTimestamp",1,["fred::types::timeseries::BucketTimestamp"]],["impl Send for ClientKillFilter",1,["fred::types::client::ClientKillFilter"]],["impl Send for ClientKillType",1,["fred::types::client::ClientKillType"]],["impl Send for ClientPauseKind",1,["fred::types::client::ClientPauseKind"]],["impl Send for ClientReplyFlag",1,["fred::types::client::ClientReplyFlag"]],["impl Send for ClientState",1,["fred::types::misc::ClientState"]],["impl Send for ClientUnblockFlag",1,["fred::types::misc::ClientUnblockFlag"]],["impl Send for ClusterDiscoveryPolicy",1,["fred::types::config::ClusterDiscoveryPolicy"]],["impl Send for ClusterFailoverFlag",1,["fred::types::cluster::ClusterFailoverFlag"]],["impl Send for ClusterHash",1,["fred::protocol::hashers::ClusterHash"]],["impl Send for ClusterResetFlag",1,["fred::types::cluster::ClusterResetFlag"]],["impl Send for ClusterSetSlotState",1,["fred::types::cluster::ClusterSetSlotState"]],["impl Send for ClusterState",1,["fred::types::cluster::ClusterState"]],["impl Send for ClusterStateChange",1,["fred::types::misc::ClusterStateChange"]],["impl Send for DuplicatePolicy",1,["fred::types::timeseries::DuplicatePolicy"]],["impl Send for Encoding",1,["fred::types::timeseries::Encoding"]],["impl Send for Expiration",1,["fred::types::misc::Expiration"]],["impl Send for ExpireOptions",1,["fred::types::misc::ExpireOptions"]],["impl Send for FnPolicy",1,["fred::types::misc::FnPolicy"]],["impl Send for FunctionFlag",1,["fred::types::scripts::FunctionFlag"]],["impl Send for GeoUnit",1,["fred::types::geo::GeoUnit"]],["impl Send for GetLabels",1,["fred::types::timeseries::GetLabels"]],["impl Send for GetTimestamp",1,["fred::types::timeseries::GetTimestamp"]],["impl Send for IndexKind",1,["fred::types::redisearch::IndexKind"]],["impl Send for InfoKind",1,["fred::types::misc::InfoKind"]],["impl Send for LMoveDirection",1,["fred::types::lists::LMoveDirection"]],["impl Send for ListLocation",1,["fred::types::lists::ListLocation"]],["impl Send for Load",1,["fred::types::redisearch::Load"]],["impl Send for MessageKind",1,["fred::protocol::types::MessageKind"]],["impl Send for Ordering",1,["fred::types::sorted_sets::Ordering"]],["impl Send for ReconnectError",1,["fred::types::config::ReconnectError"]],["impl Send for ReconnectPolicy",1,["fred::types::config::ReconnectPolicy"]],["impl Send for RedisValue",1,["fred::types::args::RedisValue"]],["impl Send for RedisValueKind",1,["fred::types::args::RedisValueKind"]],["impl Send for Reducer",1,["fred::types::timeseries::Reducer"]],["impl Send for ReducerFunc",1,["fred::types::redisearch::ReducerFunc"]],["impl Send for ScanType",1,["fred::types::scan::ScanType"]],["impl Send for ScriptDebugFlag",1,["fred::types::misc::ScriptDebugFlag"]],["impl Send for SearchSchemaKind",1,["fred::types::redisearch::SearchSchemaKind"]],["impl Send for SentinelFailureKind",1,["fred::types::misc::SentinelFailureKind"]],["impl Send for ServerConfig",1,["fred::types::config::ServerConfig"]],["impl Send for SetOptions",1,["fred::types::misc::SetOptions"]],["impl Send for ShutdownFlags",1,["fred::types::misc::ShutdownFlags"]],["impl Send for SortOrder",1,["fred::types::misc::SortOrder"]],["impl Send for SpellcheckTerms",1,["fred::types::redisearch::SpellcheckTerms"]],["impl Send for StringOrNumber",1,["fred::types::args::StringOrNumber"]],["impl Send for Timestamp",1,["fred::types::timeseries::Timestamp"]],["impl Send for TlsConnector",1,["fred::protocol::tls::TlsConnector"]],["impl Send for TlsHostMapping",1,["fred::protocol::tls::TlsHostMapping"]],["impl Send for Toggle",1,["fred::types::client::Toggle"]],["impl Send for XCapKind",1,["fred::types::streams::XCapKind"]],["impl Send for XCapTrim",1,["fred::types::streams::XCapTrim"]],["impl Send for XID",1,["fred::types::streams::XID"]],["impl Send for ZCmp",1,["fred::types::sorted_sets::ZCmp"]],["impl Send for ZRangeBound",1,["fred::types::sorted_sets::ZRangeBound"]],["impl Send for ZRangeKind",1,["fred::types::sorted_sets::ZRangeKind"]],["impl Send for ZSort",1,["fred::types::sorted_sets::ZSort"]],["impl Send for RedisError",1,["fred::error::RedisError"]],["impl Send for Command",1,["fred::monitor::Command"]],["impl Send for BackpressureConfig",1,["fred::types::config::BackpressureConfig"]],["impl Send for ClusterInfo",1,["fred::types::cluster::ClusterInfo"]],["impl Send for ClusterRouting",1,["fred::protocol::types::ClusterRouting"]],["impl Send for CustomCommand",1,["fred::types::misc::CustomCommand"]],["impl Send for DatabaseMemoryStats",1,["fred::types::misc::DatabaseMemoryStats"]],["impl Send for FtAggregateOptions",1,["fred::types::redisearch::FtAggregateOptions"]],["impl Send for FtAlterOptions",1,["fred::types::redisearch::FtAlterOptions"]],["impl Send for FtCreateOptions",1,["fred::types::redisearch::FtCreateOptions"]],["impl Send for FtSearchOptions",1,["fred::types::redisearch::FtSearchOptions"]],["impl Send for Function",1,["fred::types::scripts::Function"]],["impl Send for GeoPosition",1,["fred::types::geo::GeoPosition"]],["impl Send for GeoRadiusInfo",1,["fred::types::geo::GeoRadiusInfo"]],["impl Send for GeoValue",1,["fred::types::geo::GeoValue"]],["impl Send for GroupBy",1,["fred::types::timeseries::GroupBy"]],["impl Send for Invalidation",1,["fred::types::client::Invalidation"]],["impl Send for KeyspaceEvent",1,["fred::types::misc::KeyspaceEvent"]],["impl Send for Library",1,["fred::types::scripts::Library"]],["impl Send for MemoryStats",1,["fred::types::misc::MemoryStats"]],["impl Send for Message",1,["fred::protocol::types::Message"]],["impl Send for MultipleGeoValues",1,["fred::types::geo::MultipleGeoValues"]],["impl Send for MultipleHashSlots",1,["fred::types::multiple::MultipleHashSlots"]],["impl Send for MultipleIDs",1,["fred::types::streams::MultipleIDs"]],["impl Send for MultipleKeys",1,["fred::types::multiple::MultipleKeys"]],["impl Send for MultipleOrderedPairs",1,["fred::types::streams::MultipleOrderedPairs"]],["impl Send for MultipleWeights",1,["fred::types::sorted_sets::MultipleWeights"]],["impl Send for MultipleZaddValues",1,["fred::types::sorted_sets::MultipleZaddValues"]],["impl Send for Options",1,["fred::types::config::Options"]],["impl Send for PerformanceConfig",1,["fred::types::config::PerformanceConfig"]],["impl Send for RangeAggregation",1,["fred::types::timeseries::RangeAggregation"]],["impl Send for RedisConfig",1,["fred::types::config::RedisConfig"]],["impl Send for RedisKey",1,["fred::types::args::RedisKey"]],["impl Send for RedisMap",1,["fred::types::args::RedisMap"]],["impl Send for Script",1,["fred::types::scripts::Script"]],["impl Send for SearchField",1,["fred::types::redisearch::SearchField"]],["impl Send for SearchFilter",1,["fred::types::redisearch::SearchFilter"]],["impl Send for SearchGeoFilter",1,["fred::types::redisearch::SearchGeoFilter"]],["impl Send for SearchHighlight",1,["fred::types::redisearch::SearchHighlight"]],["impl Send for SearchParameter",1,["fred::types::redisearch::SearchParameter"]],["impl Send for SearchReducer",1,["fred::types::redisearch::SearchReducer"]],["impl Send for SearchSchema",1,["fred::types::redisearch::SearchSchema"]],["impl Send for SearchSortBy",1,["fred::types::redisearch::SearchSortBy"]],["impl Send for SearchSummarize",1,["fred::types::redisearch::SearchSummarize"]],["impl Send for SentinelConfig",1,["fred::types::config::SentinelConfig"]],["impl Send for Server",1,["fred::protocol::types::Server"]],["impl Send for SlotRange",1,["fred::protocol::types::SlotRange"]],["impl Send for SlowlogEntry",1,["fred::types::misc::SlowlogEntry"]],["impl Send for Stats",1,["fred::modules::metrics::Stats"]],["impl Send for TcpConfig",1,["fred::types::config::TcpConfig"]],["impl Send for TlsConfig",1,["fred::protocol::tls::TlsConfig"]],["impl Send for TracingConfig",1,["fred::types::config::TracingConfig"]],["impl Send for UnresponsiveConfig",1,["fred::types::config::UnresponsiveConfig"]],["impl Send for WithCursor",1,["fred::types::redisearch::WithCursor"]],["impl Send for XCap",1,["fred::types::streams::XCap"]],["impl Send for XPendingArgs",1,["fred::types::streams::XPendingArgs"]],["impl Send for ZRange",1,["fred::types::sorted_sets::ZRange"]],["impl<C> !Send for Pipeline<C>",1,["fred::clients::pipeline::Pipeline"]],["impl<C> Send for WithOptions<C>
where\n C: Send,
",1,["fred::clients::options::WithOptions"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[42079]} \ No newline at end of file diff --git a/doc/trait.impl/core/marker/trait.StructuralPartialEq.js b/doc/trait.impl/core/marker/trait.StructuralPartialEq.js new file mode 100644 index 00000000..a05d8528 --- /dev/null +++ b/doc/trait.impl/core/marker/trait.StructuralPartialEq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl StructuralPartialEq for RedisErrorKind"],["impl StructuralPartialEq for AggregateOperation"],["impl StructuralPartialEq for Aggregator"],["impl StructuralPartialEq for BackpressurePolicy"],["impl StructuralPartialEq for Blocking"],["impl StructuralPartialEq for BucketTimestamp"],["impl StructuralPartialEq for ClientKillFilter"],["impl StructuralPartialEq for ClientKillType"],["impl StructuralPartialEq for ClientPauseKind"],["impl StructuralPartialEq for ClientReplyFlag"],["impl StructuralPartialEq for ClientState"],["impl StructuralPartialEq for ClientUnblockFlag"],["impl StructuralPartialEq for ClusterDiscoveryPolicy"],["impl StructuralPartialEq for ClusterFailoverFlag"],["impl StructuralPartialEq for ClusterHash"],["impl StructuralPartialEq for ClusterResetFlag"],["impl StructuralPartialEq for ClusterSetSlotState"],["impl StructuralPartialEq for ClusterState"],["impl StructuralPartialEq for ClusterStateChange"],["impl StructuralPartialEq for DuplicatePolicy"],["impl StructuralPartialEq for Encoding"],["impl StructuralPartialEq for Expiration"],["impl StructuralPartialEq for ExpireOptions"],["impl StructuralPartialEq for FnPolicy"],["impl StructuralPartialEq for FunctionFlag"],["impl StructuralPartialEq for GeoUnit"],["impl StructuralPartialEq for GetLabels"],["impl StructuralPartialEq for GetTimestamp"],["impl StructuralPartialEq for IndexKind"],["impl StructuralPartialEq for InfoKind"],["impl StructuralPartialEq for LMoveDirection"],["impl StructuralPartialEq for ListLocation"],["impl StructuralPartialEq for Load"],["impl StructuralPartialEq for MessageKind"],["impl StructuralPartialEq for Ordering"],["impl StructuralPartialEq for ReconnectError"],["impl StructuralPartialEq for ReconnectPolicy"],["impl StructuralPartialEq for RedisValueKind"],["impl StructuralPartialEq for Reducer"],["impl StructuralPartialEq for ReducerFunc"],["impl StructuralPartialEq for ScanType"],["impl StructuralPartialEq for ScriptDebugFlag"],["impl StructuralPartialEq for SentinelFailureKind"],["impl StructuralPartialEq for ServerConfig"],["impl StructuralPartialEq for SetOptions"],["impl StructuralPartialEq for ShutdownFlags"],["impl StructuralPartialEq for SortOrder"],["impl StructuralPartialEq for Timestamp"],["impl StructuralPartialEq for Toggle"],["impl StructuralPartialEq for XCapKind"],["impl StructuralPartialEq for XCapTrim"],["impl StructuralPartialEq for XID"],["impl StructuralPartialEq for ZCmp"],["impl StructuralPartialEq for ZRangeKind"],["impl StructuralPartialEq for ZSort"],["impl StructuralPartialEq for BackpressureConfig"],["impl StructuralPartialEq for ClusterInfo"],["impl StructuralPartialEq for ConnectionConfig"],["impl StructuralPartialEq for CustomCommand"],["impl StructuralPartialEq for DatabaseMemoryStats"],["impl StructuralPartialEq for Function"],["impl StructuralPartialEq for GeoValue"],["impl StructuralPartialEq for GroupBy"],["impl StructuralPartialEq for Invalidation"],["impl StructuralPartialEq for KeyspaceEvent"],["impl StructuralPartialEq for Library"],["impl StructuralPartialEq for Message"],["impl StructuralPartialEq for MultipleGeoValues"],["impl StructuralPartialEq for MultipleIDs"],["impl StructuralPartialEq for MultipleKeys"],["impl StructuralPartialEq for MultipleOrderedPairs"],["impl StructuralPartialEq for Options"],["impl StructuralPartialEq for PerformanceConfig"],["impl StructuralPartialEq for RangeAggregation"],["impl StructuralPartialEq for RedisKey"],["impl StructuralPartialEq for RedisMap"],["impl StructuralPartialEq for Script"],["impl StructuralPartialEq for SearchField"],["impl StructuralPartialEq for SearchHighlight"],["impl StructuralPartialEq for SearchParameter"],["impl StructuralPartialEq for SearchReducer"],["impl StructuralPartialEq for SearchSortBy"],["impl StructuralPartialEq for SearchSummarize"],["impl StructuralPartialEq for SlotRange"],["impl StructuralPartialEq for SlowlogEntry"],["impl StructuralPartialEq for TlsConfig"],["impl StructuralPartialEq for UnresponsiveConfig"],["impl StructuralPartialEq for WithCursor"],["impl StructuralPartialEq for XCap"],["impl StructuralPartialEq for XPendingArgs"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[28162]} \ No newline at end of file diff --git a/doc/trait.impl/core/marker/trait.Sync.js b/doc/trait.impl/core/marker/trait.Sync.js new file mode 100644 index 00000000..a9ec62df --- /dev/null +++ b/doc/trait.impl/core/marker/trait.Sync.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl !Sync for RedisClient",1,["fred::clients::redis::RedisClient"]],["impl !Sync for RedisPool",1,["fred::clients::pool::RedisPool"]],["impl !Sync for Replicas",1,["fred::clients::replica::Replicas"]],["impl !Sync for SentinelClient",1,["fred::clients::sentinel::SentinelClient"]],["impl !Sync for SubscriberClient",1,["fred::clients::pubsub::SubscriberClient"]],["impl !Sync for Transaction",1,["fred::clients::transaction::Transaction"]],["impl !Sync for Builder",1,["fred::types::builder::Builder"]],["impl !Sync for ConnectionConfig",1,["fred::types::config::ConnectionConfig"]],["impl !Sync for HScanResult",1,["fred::types::scan::HScanResult"]],["impl !Sync for ReplicaConfig",1,["fred::router::replicas::ReplicaConfig"]],["impl !Sync for SScanResult",1,["fred::types::scan::SScanResult"]],["impl !Sync for ScanResult",1,["fred::types::scan::ScanResult"]],["impl !Sync for ZScanResult",1,["fred::types::scan::ZScanResult"]],["impl Sync for RedisErrorKind",1,["fred::error::RedisErrorKind"]],["impl Sync for AggregateOperation",1,["fred::types::redisearch::AggregateOperation"]],["impl Sync for AggregateOptions",1,["fred::types::misc::AggregateOptions"]],["impl Sync for Aggregator",1,["fred::types::timeseries::Aggregator"]],["impl Sync for BackpressurePolicy",1,["fred::types::config::BackpressurePolicy"]],["impl Sync for Blocking",1,["fred::types::config::Blocking"]],["impl Sync for BucketTimestamp",1,["fred::types::timeseries::BucketTimestamp"]],["impl Sync for ClientKillFilter",1,["fred::types::client::ClientKillFilter"]],["impl Sync for ClientKillType",1,["fred::types::client::ClientKillType"]],["impl Sync for ClientPauseKind",1,["fred::types::client::ClientPauseKind"]],["impl Sync for ClientReplyFlag",1,["fred::types::client::ClientReplyFlag"]],["impl Sync for ClientState",1,["fred::types::misc::ClientState"]],["impl Sync for ClientUnblockFlag",1,["fred::types::misc::ClientUnblockFlag"]],["impl Sync for ClusterDiscoveryPolicy",1,["fred::types::config::ClusterDiscoveryPolicy"]],["impl Sync for ClusterFailoverFlag",1,["fred::types::cluster::ClusterFailoverFlag"]],["impl Sync for ClusterHash",1,["fred::protocol::hashers::ClusterHash"]],["impl Sync for ClusterResetFlag",1,["fred::types::cluster::ClusterResetFlag"]],["impl Sync for ClusterSetSlotState",1,["fred::types::cluster::ClusterSetSlotState"]],["impl Sync for ClusterState",1,["fred::types::cluster::ClusterState"]],["impl Sync for ClusterStateChange",1,["fred::types::misc::ClusterStateChange"]],["impl Sync for DuplicatePolicy",1,["fred::types::timeseries::DuplicatePolicy"]],["impl Sync for Encoding",1,["fred::types::timeseries::Encoding"]],["impl Sync for Expiration",1,["fred::types::misc::Expiration"]],["impl Sync for ExpireOptions",1,["fred::types::misc::ExpireOptions"]],["impl Sync for FnPolicy",1,["fred::types::misc::FnPolicy"]],["impl Sync for FunctionFlag",1,["fred::types::scripts::FunctionFlag"]],["impl Sync for GeoUnit",1,["fred::types::geo::GeoUnit"]],["impl Sync for GetLabels",1,["fred::types::timeseries::GetLabels"]],["impl Sync for GetTimestamp",1,["fred::types::timeseries::GetTimestamp"]],["impl Sync for IndexKind",1,["fred::types::redisearch::IndexKind"]],["impl Sync for InfoKind",1,["fred::types::misc::InfoKind"]],["impl Sync for LMoveDirection",1,["fred::types::lists::LMoveDirection"]],["impl Sync for ListLocation",1,["fred::types::lists::ListLocation"]],["impl Sync for Load",1,["fred::types::redisearch::Load"]],["impl Sync for MessageKind",1,["fred::protocol::types::MessageKind"]],["impl Sync for Ordering",1,["fred::types::sorted_sets::Ordering"]],["impl Sync for ReconnectError",1,["fred::types::config::ReconnectError"]],["impl Sync for ReconnectPolicy",1,["fred::types::config::ReconnectPolicy"]],["impl Sync for RedisValue",1,["fred::types::args::RedisValue"]],["impl Sync for RedisValueKind",1,["fred::types::args::RedisValueKind"]],["impl Sync for Reducer",1,["fred::types::timeseries::Reducer"]],["impl Sync for ReducerFunc",1,["fred::types::redisearch::ReducerFunc"]],["impl Sync for ScanType",1,["fred::types::scan::ScanType"]],["impl Sync for ScriptDebugFlag",1,["fred::types::misc::ScriptDebugFlag"]],["impl Sync for SearchSchemaKind",1,["fred::types::redisearch::SearchSchemaKind"]],["impl Sync for SentinelFailureKind",1,["fred::types::misc::SentinelFailureKind"]],["impl Sync for ServerConfig",1,["fred::types::config::ServerConfig"]],["impl Sync for SetOptions",1,["fred::types::misc::SetOptions"]],["impl Sync for ShutdownFlags",1,["fred::types::misc::ShutdownFlags"]],["impl Sync for SortOrder",1,["fred::types::misc::SortOrder"]],["impl Sync for SpellcheckTerms",1,["fred::types::redisearch::SpellcheckTerms"]],["impl Sync for StringOrNumber",1,["fred::types::args::StringOrNumber"]],["impl Sync for Timestamp",1,["fred::types::timeseries::Timestamp"]],["impl Sync for TlsConnector",1,["fred::protocol::tls::TlsConnector"]],["impl Sync for TlsHostMapping",1,["fred::protocol::tls::TlsHostMapping"]],["impl Sync for Toggle",1,["fred::types::client::Toggle"]],["impl Sync for XCapKind",1,["fred::types::streams::XCapKind"]],["impl Sync for XCapTrim",1,["fred::types::streams::XCapTrim"]],["impl Sync for XID",1,["fred::types::streams::XID"]],["impl Sync for ZCmp",1,["fred::types::sorted_sets::ZCmp"]],["impl Sync for ZRangeBound",1,["fred::types::sorted_sets::ZRangeBound"]],["impl Sync for ZRangeKind",1,["fred::types::sorted_sets::ZRangeKind"]],["impl Sync for ZSort",1,["fred::types::sorted_sets::ZSort"]],["impl Sync for RedisError",1,["fred::error::RedisError"]],["impl Sync for Command",1,["fred::monitor::Command"]],["impl Sync for BackpressureConfig",1,["fred::types::config::BackpressureConfig"]],["impl Sync for ClusterInfo",1,["fred::types::cluster::ClusterInfo"]],["impl Sync for ClusterRouting",1,["fred::protocol::types::ClusterRouting"]],["impl Sync for CustomCommand",1,["fred::types::misc::CustomCommand"]],["impl Sync for DatabaseMemoryStats",1,["fred::types::misc::DatabaseMemoryStats"]],["impl Sync for FtAggregateOptions",1,["fred::types::redisearch::FtAggregateOptions"]],["impl Sync for FtAlterOptions",1,["fred::types::redisearch::FtAlterOptions"]],["impl Sync for FtCreateOptions",1,["fred::types::redisearch::FtCreateOptions"]],["impl Sync for FtSearchOptions",1,["fred::types::redisearch::FtSearchOptions"]],["impl Sync for Function",1,["fred::types::scripts::Function"]],["impl Sync for GeoPosition",1,["fred::types::geo::GeoPosition"]],["impl Sync for GeoRadiusInfo",1,["fred::types::geo::GeoRadiusInfo"]],["impl Sync for GeoValue",1,["fred::types::geo::GeoValue"]],["impl Sync for GroupBy",1,["fred::types::timeseries::GroupBy"]],["impl Sync for Invalidation",1,["fred::types::client::Invalidation"]],["impl Sync for KeyspaceEvent",1,["fred::types::misc::KeyspaceEvent"]],["impl Sync for Library",1,["fred::types::scripts::Library"]],["impl Sync for MemoryStats",1,["fred::types::misc::MemoryStats"]],["impl Sync for Message",1,["fred::protocol::types::Message"]],["impl Sync for MultipleGeoValues",1,["fred::types::geo::MultipleGeoValues"]],["impl Sync for MultipleHashSlots",1,["fred::types::multiple::MultipleHashSlots"]],["impl Sync for MultipleIDs",1,["fred::types::streams::MultipleIDs"]],["impl Sync for MultipleKeys",1,["fred::types::multiple::MultipleKeys"]],["impl Sync for MultipleOrderedPairs",1,["fred::types::streams::MultipleOrderedPairs"]],["impl Sync for MultipleWeights",1,["fred::types::sorted_sets::MultipleWeights"]],["impl Sync for MultipleZaddValues",1,["fred::types::sorted_sets::MultipleZaddValues"]],["impl Sync for Options",1,["fred::types::config::Options"]],["impl Sync for PerformanceConfig",1,["fred::types::config::PerformanceConfig"]],["impl Sync for RangeAggregation",1,["fred::types::timeseries::RangeAggregation"]],["impl Sync for RedisConfig",1,["fred::types::config::RedisConfig"]],["impl Sync for RedisKey",1,["fred::types::args::RedisKey"]],["impl Sync for RedisMap",1,["fred::types::args::RedisMap"]],["impl Sync for Script",1,["fred::types::scripts::Script"]],["impl Sync for SearchField",1,["fred::types::redisearch::SearchField"]],["impl Sync for SearchFilter",1,["fred::types::redisearch::SearchFilter"]],["impl Sync for SearchGeoFilter",1,["fred::types::redisearch::SearchGeoFilter"]],["impl Sync for SearchHighlight",1,["fred::types::redisearch::SearchHighlight"]],["impl Sync for SearchParameter",1,["fred::types::redisearch::SearchParameter"]],["impl Sync for SearchReducer",1,["fred::types::redisearch::SearchReducer"]],["impl Sync for SearchSchema",1,["fred::types::redisearch::SearchSchema"]],["impl Sync for SearchSortBy",1,["fred::types::redisearch::SearchSortBy"]],["impl Sync for SearchSummarize",1,["fred::types::redisearch::SearchSummarize"]],["impl Sync for SentinelConfig",1,["fred::types::config::SentinelConfig"]],["impl Sync for Server",1,["fred::protocol::types::Server"]],["impl Sync for SlotRange",1,["fred::protocol::types::SlotRange"]],["impl Sync for SlowlogEntry",1,["fred::types::misc::SlowlogEntry"]],["impl Sync for Stats",1,["fred::modules::metrics::Stats"]],["impl Sync for TcpConfig",1,["fred::types::config::TcpConfig"]],["impl Sync for TlsConfig",1,["fred::protocol::tls::TlsConfig"]],["impl Sync for TracingConfig",1,["fred::types::config::TracingConfig"]],["impl Sync for UnresponsiveConfig",1,["fred::types::config::UnresponsiveConfig"]],["impl Sync for WithCursor",1,["fred::types::redisearch::WithCursor"]],["impl Sync for XCap",1,["fred::types::streams::XCap"]],["impl Sync for XPendingArgs",1,["fred::types::streams::XPendingArgs"]],["impl Sync for ZRange",1,["fred::types::sorted_sets::ZRange"]],["impl<C> !Sync for Pipeline<C>",1,["fred::clients::pipeline::Pipeline"]],["impl<C> Sync for WithOptions<C>
where\n C: Sync,
",1,["fred::clients::options::WithOptions"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[42079]} \ No newline at end of file diff --git a/doc/trait.impl/core/marker/trait.Unpin.js b/doc/trait.impl/core/marker/trait.Unpin.js new file mode 100644 index 00000000..4f539dc8 --- /dev/null +++ b/doc/trait.impl/core/marker/trait.Unpin.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl Unpin for RedisErrorKind",1,["fred::error::RedisErrorKind"]],["impl Unpin for AggregateOperation",1,["fred::types::redisearch::AggregateOperation"]],["impl Unpin for AggregateOptions",1,["fred::types::misc::AggregateOptions"]],["impl Unpin for Aggregator",1,["fred::types::timeseries::Aggregator"]],["impl Unpin for BackpressurePolicy",1,["fred::types::config::BackpressurePolicy"]],["impl Unpin for Blocking",1,["fred::types::config::Blocking"]],["impl Unpin for BucketTimestamp",1,["fred::types::timeseries::BucketTimestamp"]],["impl Unpin for ClientKillFilter",1,["fred::types::client::ClientKillFilter"]],["impl Unpin for ClientKillType",1,["fred::types::client::ClientKillType"]],["impl Unpin for ClientPauseKind",1,["fred::types::client::ClientPauseKind"]],["impl Unpin for ClientReplyFlag",1,["fred::types::client::ClientReplyFlag"]],["impl Unpin for ClientState",1,["fred::types::misc::ClientState"]],["impl Unpin for ClientUnblockFlag",1,["fred::types::misc::ClientUnblockFlag"]],["impl Unpin for ClusterDiscoveryPolicy",1,["fred::types::config::ClusterDiscoveryPolicy"]],["impl Unpin for ClusterFailoverFlag",1,["fred::types::cluster::ClusterFailoverFlag"]],["impl Unpin for ClusterHash",1,["fred::protocol::hashers::ClusterHash"]],["impl Unpin for ClusterResetFlag",1,["fred::types::cluster::ClusterResetFlag"]],["impl Unpin for ClusterSetSlotState",1,["fred::types::cluster::ClusterSetSlotState"]],["impl Unpin for ClusterState",1,["fred::types::cluster::ClusterState"]],["impl Unpin for ClusterStateChange",1,["fred::types::misc::ClusterStateChange"]],["impl Unpin for DuplicatePolicy",1,["fred::types::timeseries::DuplicatePolicy"]],["impl Unpin for Encoding",1,["fred::types::timeseries::Encoding"]],["impl Unpin for Expiration",1,["fred::types::misc::Expiration"]],["impl Unpin for ExpireOptions",1,["fred::types::misc::ExpireOptions"]],["impl Unpin for FnPolicy",1,["fred::types::misc::FnPolicy"]],["impl Unpin for FunctionFlag",1,["fred::types::scripts::FunctionFlag"]],["impl Unpin for GeoUnit",1,["fred::types::geo::GeoUnit"]],["impl Unpin for GetLabels",1,["fred::types::timeseries::GetLabels"]],["impl Unpin for GetTimestamp",1,["fred::types::timeseries::GetTimestamp"]],["impl Unpin for IndexKind",1,["fred::types::redisearch::IndexKind"]],["impl Unpin for InfoKind",1,["fred::types::misc::InfoKind"]],["impl Unpin for LMoveDirection",1,["fred::types::lists::LMoveDirection"]],["impl Unpin for ListLocation",1,["fred::types::lists::ListLocation"]],["impl Unpin for Load",1,["fred::types::redisearch::Load"]],["impl Unpin for MessageKind",1,["fred::protocol::types::MessageKind"]],["impl Unpin for Ordering",1,["fred::types::sorted_sets::Ordering"]],["impl Unpin for ReconnectError",1,["fred::types::config::ReconnectError"]],["impl Unpin for ReconnectPolicy",1,["fred::types::config::ReconnectPolicy"]],["impl Unpin for RedisValue",1,["fred::types::args::RedisValue"]],["impl Unpin for RedisValueKind",1,["fred::types::args::RedisValueKind"]],["impl Unpin for Reducer",1,["fred::types::timeseries::Reducer"]],["impl Unpin for ReducerFunc",1,["fred::types::redisearch::ReducerFunc"]],["impl Unpin for ScanType",1,["fred::types::scan::ScanType"]],["impl Unpin for ScriptDebugFlag",1,["fred::types::misc::ScriptDebugFlag"]],["impl Unpin for SearchSchemaKind",1,["fred::types::redisearch::SearchSchemaKind"]],["impl Unpin for SentinelFailureKind",1,["fred::types::misc::SentinelFailureKind"]],["impl Unpin for ServerConfig",1,["fred::types::config::ServerConfig"]],["impl Unpin for SetOptions",1,["fred::types::misc::SetOptions"]],["impl Unpin for ShutdownFlags",1,["fred::types::misc::ShutdownFlags"]],["impl Unpin for SortOrder",1,["fred::types::misc::SortOrder"]],["impl Unpin for SpellcheckTerms",1,["fred::types::redisearch::SpellcheckTerms"]],["impl Unpin for StringOrNumber",1,["fred::types::args::StringOrNumber"]],["impl Unpin for Timestamp",1,["fred::types::timeseries::Timestamp"]],["impl Unpin for TlsConnector",1,["fred::protocol::tls::TlsConnector"]],["impl Unpin for TlsHostMapping",1,["fred::protocol::tls::TlsHostMapping"]],["impl Unpin for Toggle",1,["fred::types::client::Toggle"]],["impl Unpin for XCapKind",1,["fred::types::streams::XCapKind"]],["impl Unpin for XCapTrim",1,["fred::types::streams::XCapTrim"]],["impl Unpin for XID",1,["fred::types::streams::XID"]],["impl Unpin for ZCmp",1,["fred::types::sorted_sets::ZCmp"]],["impl Unpin for ZRangeBound",1,["fred::types::sorted_sets::ZRangeBound"]],["impl Unpin for ZRangeKind",1,["fred::types::sorted_sets::ZRangeKind"]],["impl Unpin for ZSort",1,["fred::types::sorted_sets::ZSort"]],["impl Unpin for RedisClient",1,["fred::clients::redis::RedisClient"]],["impl Unpin for RedisPool",1,["fred::clients::pool::RedisPool"]],["impl Unpin for Replicas",1,["fred::clients::replica::Replicas"]],["impl Unpin for SentinelClient",1,["fred::clients::sentinel::SentinelClient"]],["impl Unpin for SubscriberClient",1,["fred::clients::pubsub::SubscriberClient"]],["impl Unpin for Transaction",1,["fred::clients::transaction::Transaction"]],["impl Unpin for RedisError",1,["fred::error::RedisError"]],["impl Unpin for Command",1,["fred::monitor::Command"]],["impl Unpin for BackpressureConfig",1,["fred::types::config::BackpressureConfig"]],["impl Unpin for Builder",1,["fred::types::builder::Builder"]],["impl Unpin for ClusterInfo",1,["fred::types::cluster::ClusterInfo"]],["impl Unpin for ClusterRouting",1,["fred::protocol::types::ClusterRouting"]],["impl Unpin for ConnectionConfig",1,["fred::types::config::ConnectionConfig"]],["impl Unpin for CustomCommand",1,["fred::types::misc::CustomCommand"]],["impl Unpin for DatabaseMemoryStats",1,["fred::types::misc::DatabaseMemoryStats"]],["impl Unpin for FtAggregateOptions",1,["fred::types::redisearch::FtAggregateOptions"]],["impl Unpin for FtAlterOptions",1,["fred::types::redisearch::FtAlterOptions"]],["impl Unpin for FtCreateOptions",1,["fred::types::redisearch::FtCreateOptions"]],["impl Unpin for FtSearchOptions",1,["fred::types::redisearch::FtSearchOptions"]],["impl Unpin for Function",1,["fred::types::scripts::Function"]],["impl Unpin for GeoPosition",1,["fred::types::geo::GeoPosition"]],["impl Unpin for GeoRadiusInfo",1,["fred::types::geo::GeoRadiusInfo"]],["impl Unpin for GeoValue",1,["fred::types::geo::GeoValue"]],["impl Unpin for GroupBy",1,["fred::types::timeseries::GroupBy"]],["impl Unpin for HScanResult",1,["fred::types::scan::HScanResult"]],["impl Unpin for Invalidation",1,["fred::types::client::Invalidation"]],["impl Unpin for KeyspaceEvent",1,["fred::types::misc::KeyspaceEvent"]],["impl Unpin for Library",1,["fred::types::scripts::Library"]],["impl Unpin for MemoryStats",1,["fred::types::misc::MemoryStats"]],["impl Unpin for Message",1,["fred::protocol::types::Message"]],["impl Unpin for MultipleGeoValues",1,["fred::types::geo::MultipleGeoValues"]],["impl Unpin for MultipleHashSlots",1,["fred::types::multiple::MultipleHashSlots"]],["impl Unpin for MultipleIDs",1,["fred::types::streams::MultipleIDs"]],["impl Unpin for MultipleKeys",1,["fred::types::multiple::MultipleKeys"]],["impl Unpin for MultipleOrderedPairs",1,["fred::types::streams::MultipleOrderedPairs"]],["impl Unpin for MultipleWeights",1,["fred::types::sorted_sets::MultipleWeights"]],["impl Unpin for MultipleZaddValues",1,["fred::types::sorted_sets::MultipleZaddValues"]],["impl Unpin for Options",1,["fred::types::config::Options"]],["impl Unpin for PerformanceConfig",1,["fred::types::config::PerformanceConfig"]],["impl Unpin for RangeAggregation",1,["fred::types::timeseries::RangeAggregation"]],["impl Unpin for RedisConfig",1,["fred::types::config::RedisConfig"]],["impl Unpin for RedisKey",1,["fred::types::args::RedisKey"]],["impl Unpin for RedisMap",1,["fred::types::args::RedisMap"]],["impl Unpin for ReplicaConfig",1,["fred::router::replicas::ReplicaConfig"]],["impl Unpin for SScanResult",1,["fred::types::scan::SScanResult"]],["impl Unpin for ScanResult",1,["fred::types::scan::ScanResult"]],["impl Unpin for Script",1,["fred::types::scripts::Script"]],["impl Unpin for SearchField",1,["fred::types::redisearch::SearchField"]],["impl Unpin for SearchFilter",1,["fred::types::redisearch::SearchFilter"]],["impl Unpin for SearchGeoFilter",1,["fred::types::redisearch::SearchGeoFilter"]],["impl Unpin for SearchHighlight",1,["fred::types::redisearch::SearchHighlight"]],["impl Unpin for SearchParameter",1,["fred::types::redisearch::SearchParameter"]],["impl Unpin for SearchReducer",1,["fred::types::redisearch::SearchReducer"]],["impl Unpin for SearchSchema",1,["fred::types::redisearch::SearchSchema"]],["impl Unpin for SearchSortBy",1,["fred::types::redisearch::SearchSortBy"]],["impl Unpin for SearchSummarize",1,["fred::types::redisearch::SearchSummarize"]],["impl Unpin for SentinelConfig",1,["fred::types::config::SentinelConfig"]],["impl Unpin for Server",1,["fred::protocol::types::Server"]],["impl Unpin for SlotRange",1,["fred::protocol::types::SlotRange"]],["impl Unpin for SlowlogEntry",1,["fred::types::misc::SlowlogEntry"]],["impl Unpin for Stats",1,["fred::modules::metrics::Stats"]],["impl Unpin for TcpConfig",1,["fred::types::config::TcpConfig"]],["impl Unpin for TlsConfig",1,["fred::protocol::tls::TlsConfig"]],["impl Unpin for TracingConfig",1,["fred::types::config::TracingConfig"]],["impl Unpin for UnresponsiveConfig",1,["fred::types::config::UnresponsiveConfig"]],["impl Unpin for WithCursor",1,["fred::types::redisearch::WithCursor"]],["impl Unpin for XCap",1,["fred::types::streams::XCap"]],["impl Unpin for XPendingArgs",1,["fred::types::streams::XPendingArgs"]],["impl Unpin for ZRange",1,["fred::types::sorted_sets::ZRange"]],["impl Unpin for ZScanResult",1,["fred::types::scan::ZScanResult"]],["impl<C> Unpin for Pipeline<C>
where\n C: Unpin,
",1,["fred::clients::pipeline::Pipeline"]],["impl<C> Unpin for WithOptions<C>
where\n C: Unpin,
",1,["fred::clients::options::WithOptions"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[42651]} \ No newline at end of file diff --git a/doc/trait.impl/core/ops/deref/trait.Deref.js b/doc/trait.impl/core/ops/deref/trait.Deref.js new file mode 100644 index 00000000..e21e9aad --- /dev/null +++ b/doc/trait.impl/core/ops/deref/trait.Deref.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl Deref for RedisMap"],["impl<C: ClientLike> Deref for WithOptions<C>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[708]} \ No newline at end of file diff --git a/doc/trait.impl/core/ops/deref/trait.DerefMut.js b/doc/trait.impl/core/ops/deref/trait.DerefMut.js new file mode 100644 index 00000000..1533298a --- /dev/null +++ b/doc/trait.impl/core/ops/deref/trait.DerefMut.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl DerefMut for RedisMap"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[289]} \ No newline at end of file diff --git a/doc/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js b/doc/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js new file mode 100644 index 00000000..77b7e5fd --- /dev/null +++ b/doc/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl !RefUnwindSafe for TlsConnector",1,["fred::protocol::tls::TlsConnector"]],["impl !RefUnwindSafe for TlsHostMapping",1,["fred::protocol::tls::TlsHostMapping"]],["impl !RefUnwindSafe for RedisClient",1,["fred::clients::redis::RedisClient"]],["impl !RefUnwindSafe for RedisPool",1,["fred::clients::pool::RedisPool"]],["impl !RefUnwindSafe for Replicas",1,["fred::clients::replica::Replicas"]],["impl !RefUnwindSafe for SentinelClient",1,["fred::clients::sentinel::SentinelClient"]],["impl !RefUnwindSafe for SubscriberClient",1,["fred::clients::pubsub::SubscriberClient"]],["impl !RefUnwindSafe for Transaction",1,["fred::clients::transaction::Transaction"]],["impl !RefUnwindSafe for Builder",1,["fred::types::builder::Builder"]],["impl !RefUnwindSafe for ConnectionConfig",1,["fred::types::config::ConnectionConfig"]],["impl !RefUnwindSafe for HScanResult",1,["fred::types::scan::HScanResult"]],["impl !RefUnwindSafe for RedisConfig",1,["fred::types::config::RedisConfig"]],["impl !RefUnwindSafe for ReplicaConfig",1,["fred::router::replicas::ReplicaConfig"]],["impl !RefUnwindSafe for SScanResult",1,["fred::types::scan::SScanResult"]],["impl !RefUnwindSafe for ScanResult",1,["fred::types::scan::ScanResult"]],["impl !RefUnwindSafe for SentinelConfig",1,["fred::types::config::SentinelConfig"]],["impl !RefUnwindSafe for TlsConfig",1,["fred::protocol::tls::TlsConfig"]],["impl !RefUnwindSafe for ZScanResult",1,["fred::types::scan::ZScanResult"]],["impl RefUnwindSafe for RedisErrorKind",1,["fred::error::RedisErrorKind"]],["impl RefUnwindSafe for AggregateOperation",1,["fred::types::redisearch::AggregateOperation"]],["impl RefUnwindSafe for AggregateOptions",1,["fred::types::misc::AggregateOptions"]],["impl RefUnwindSafe for Aggregator",1,["fred::types::timeseries::Aggregator"]],["impl RefUnwindSafe for BackpressurePolicy",1,["fred::types::config::BackpressurePolicy"]],["impl RefUnwindSafe for Blocking",1,["fred::types::config::Blocking"]],["impl RefUnwindSafe for BucketTimestamp",1,["fred::types::timeseries::BucketTimestamp"]],["impl RefUnwindSafe for ClientKillFilter",1,["fred::types::client::ClientKillFilter"]],["impl RefUnwindSafe for ClientKillType",1,["fred::types::client::ClientKillType"]],["impl RefUnwindSafe for ClientPauseKind",1,["fred::types::client::ClientPauseKind"]],["impl RefUnwindSafe for ClientReplyFlag",1,["fred::types::client::ClientReplyFlag"]],["impl RefUnwindSafe for ClientState",1,["fred::types::misc::ClientState"]],["impl RefUnwindSafe for ClientUnblockFlag",1,["fred::types::misc::ClientUnblockFlag"]],["impl RefUnwindSafe for ClusterDiscoveryPolicy",1,["fred::types::config::ClusterDiscoveryPolicy"]],["impl RefUnwindSafe for ClusterFailoverFlag",1,["fred::types::cluster::ClusterFailoverFlag"]],["impl RefUnwindSafe for ClusterHash",1,["fred::protocol::hashers::ClusterHash"]],["impl RefUnwindSafe for ClusterResetFlag",1,["fred::types::cluster::ClusterResetFlag"]],["impl RefUnwindSafe for ClusterSetSlotState",1,["fred::types::cluster::ClusterSetSlotState"]],["impl RefUnwindSafe for ClusterState",1,["fred::types::cluster::ClusterState"]],["impl RefUnwindSafe for ClusterStateChange",1,["fred::types::misc::ClusterStateChange"]],["impl RefUnwindSafe for DuplicatePolicy",1,["fred::types::timeseries::DuplicatePolicy"]],["impl RefUnwindSafe for Encoding",1,["fred::types::timeseries::Encoding"]],["impl RefUnwindSafe for Expiration",1,["fred::types::misc::Expiration"]],["impl RefUnwindSafe for ExpireOptions",1,["fred::types::misc::ExpireOptions"]],["impl RefUnwindSafe for FnPolicy",1,["fred::types::misc::FnPolicy"]],["impl RefUnwindSafe for FunctionFlag",1,["fred::types::scripts::FunctionFlag"]],["impl RefUnwindSafe for GeoUnit",1,["fred::types::geo::GeoUnit"]],["impl RefUnwindSafe for GetLabels",1,["fred::types::timeseries::GetLabels"]],["impl RefUnwindSafe for GetTimestamp",1,["fred::types::timeseries::GetTimestamp"]],["impl RefUnwindSafe for IndexKind",1,["fred::types::redisearch::IndexKind"]],["impl RefUnwindSafe for InfoKind",1,["fred::types::misc::InfoKind"]],["impl RefUnwindSafe for LMoveDirection",1,["fred::types::lists::LMoveDirection"]],["impl RefUnwindSafe for ListLocation",1,["fred::types::lists::ListLocation"]],["impl RefUnwindSafe for Load",1,["fred::types::redisearch::Load"]],["impl RefUnwindSafe for MessageKind",1,["fred::protocol::types::MessageKind"]],["impl RefUnwindSafe for Ordering",1,["fred::types::sorted_sets::Ordering"]],["impl RefUnwindSafe for ReconnectError",1,["fred::types::config::ReconnectError"]],["impl RefUnwindSafe for ReconnectPolicy",1,["fred::types::config::ReconnectPolicy"]],["impl RefUnwindSafe for RedisValue",1,["fred::types::args::RedisValue"]],["impl RefUnwindSafe for RedisValueKind",1,["fred::types::args::RedisValueKind"]],["impl RefUnwindSafe for Reducer",1,["fred::types::timeseries::Reducer"]],["impl RefUnwindSafe for ReducerFunc",1,["fred::types::redisearch::ReducerFunc"]],["impl RefUnwindSafe for ScanType",1,["fred::types::scan::ScanType"]],["impl RefUnwindSafe for ScriptDebugFlag",1,["fred::types::misc::ScriptDebugFlag"]],["impl RefUnwindSafe for SearchSchemaKind",1,["fred::types::redisearch::SearchSchemaKind"]],["impl RefUnwindSafe for SentinelFailureKind",1,["fred::types::misc::SentinelFailureKind"]],["impl RefUnwindSafe for ServerConfig",1,["fred::types::config::ServerConfig"]],["impl RefUnwindSafe for SetOptions",1,["fred::types::misc::SetOptions"]],["impl RefUnwindSafe for ShutdownFlags",1,["fred::types::misc::ShutdownFlags"]],["impl RefUnwindSafe for SortOrder",1,["fred::types::misc::SortOrder"]],["impl RefUnwindSafe for SpellcheckTerms",1,["fred::types::redisearch::SpellcheckTerms"]],["impl RefUnwindSafe for StringOrNumber",1,["fred::types::args::StringOrNumber"]],["impl RefUnwindSafe for Timestamp",1,["fred::types::timeseries::Timestamp"]],["impl RefUnwindSafe for Toggle",1,["fred::types::client::Toggle"]],["impl RefUnwindSafe for XCapKind",1,["fred::types::streams::XCapKind"]],["impl RefUnwindSafe for XCapTrim",1,["fred::types::streams::XCapTrim"]],["impl RefUnwindSafe for XID",1,["fred::types::streams::XID"]],["impl RefUnwindSafe for ZCmp",1,["fred::types::sorted_sets::ZCmp"]],["impl RefUnwindSafe for ZRangeBound",1,["fred::types::sorted_sets::ZRangeBound"]],["impl RefUnwindSafe for ZRangeKind",1,["fred::types::sorted_sets::ZRangeKind"]],["impl RefUnwindSafe for ZSort",1,["fred::types::sorted_sets::ZSort"]],["impl RefUnwindSafe for RedisError",1,["fred::error::RedisError"]],["impl RefUnwindSafe for Command",1,["fred::monitor::Command"]],["impl RefUnwindSafe for BackpressureConfig",1,["fred::types::config::BackpressureConfig"]],["impl RefUnwindSafe for ClusterInfo",1,["fred::types::cluster::ClusterInfo"]],["impl RefUnwindSafe for ClusterRouting",1,["fred::protocol::types::ClusterRouting"]],["impl RefUnwindSafe for CustomCommand",1,["fred::types::misc::CustomCommand"]],["impl RefUnwindSafe for DatabaseMemoryStats",1,["fred::types::misc::DatabaseMemoryStats"]],["impl RefUnwindSafe for FtAggregateOptions",1,["fred::types::redisearch::FtAggregateOptions"]],["impl RefUnwindSafe for FtAlterOptions",1,["fred::types::redisearch::FtAlterOptions"]],["impl RefUnwindSafe for FtCreateOptions",1,["fred::types::redisearch::FtCreateOptions"]],["impl RefUnwindSafe for FtSearchOptions",1,["fred::types::redisearch::FtSearchOptions"]],["impl RefUnwindSafe for Function",1,["fred::types::scripts::Function"]],["impl RefUnwindSafe for GeoPosition",1,["fred::types::geo::GeoPosition"]],["impl RefUnwindSafe for GeoRadiusInfo",1,["fred::types::geo::GeoRadiusInfo"]],["impl RefUnwindSafe for GeoValue",1,["fred::types::geo::GeoValue"]],["impl RefUnwindSafe for GroupBy",1,["fred::types::timeseries::GroupBy"]],["impl RefUnwindSafe for Invalidation",1,["fred::types::client::Invalidation"]],["impl RefUnwindSafe for KeyspaceEvent",1,["fred::types::misc::KeyspaceEvent"]],["impl RefUnwindSafe for Library",1,["fred::types::scripts::Library"]],["impl RefUnwindSafe for MemoryStats",1,["fred::types::misc::MemoryStats"]],["impl RefUnwindSafe for Message",1,["fred::protocol::types::Message"]],["impl RefUnwindSafe for MultipleGeoValues",1,["fred::types::geo::MultipleGeoValues"]],["impl RefUnwindSafe for MultipleHashSlots",1,["fred::types::multiple::MultipleHashSlots"]],["impl RefUnwindSafe for MultipleIDs",1,["fred::types::streams::MultipleIDs"]],["impl RefUnwindSafe for MultipleKeys",1,["fred::types::multiple::MultipleKeys"]],["impl RefUnwindSafe for MultipleOrderedPairs",1,["fred::types::streams::MultipleOrderedPairs"]],["impl RefUnwindSafe for MultipleWeights",1,["fred::types::sorted_sets::MultipleWeights"]],["impl RefUnwindSafe for MultipleZaddValues",1,["fred::types::sorted_sets::MultipleZaddValues"]],["impl RefUnwindSafe for Options",1,["fred::types::config::Options"]],["impl RefUnwindSafe for PerformanceConfig",1,["fred::types::config::PerformanceConfig"]],["impl RefUnwindSafe for RangeAggregation",1,["fred::types::timeseries::RangeAggregation"]],["impl RefUnwindSafe for RedisKey",1,["fred::types::args::RedisKey"]],["impl RefUnwindSafe for RedisMap",1,["fred::types::args::RedisMap"]],["impl RefUnwindSafe for Script",1,["fred::types::scripts::Script"]],["impl RefUnwindSafe for SearchField",1,["fred::types::redisearch::SearchField"]],["impl RefUnwindSafe for SearchFilter",1,["fred::types::redisearch::SearchFilter"]],["impl RefUnwindSafe for SearchGeoFilter",1,["fred::types::redisearch::SearchGeoFilter"]],["impl RefUnwindSafe for SearchHighlight",1,["fred::types::redisearch::SearchHighlight"]],["impl RefUnwindSafe for SearchParameter",1,["fred::types::redisearch::SearchParameter"]],["impl RefUnwindSafe for SearchReducer",1,["fred::types::redisearch::SearchReducer"]],["impl RefUnwindSafe for SearchSchema",1,["fred::types::redisearch::SearchSchema"]],["impl RefUnwindSafe for SearchSortBy",1,["fred::types::redisearch::SearchSortBy"]],["impl RefUnwindSafe for SearchSummarize",1,["fred::types::redisearch::SearchSummarize"]],["impl RefUnwindSafe for Server",1,["fred::protocol::types::Server"]],["impl RefUnwindSafe for SlotRange",1,["fred::protocol::types::SlotRange"]],["impl RefUnwindSafe for SlowlogEntry",1,["fred::types::misc::SlowlogEntry"]],["impl RefUnwindSafe for Stats",1,["fred::modules::metrics::Stats"]],["impl RefUnwindSafe for TcpConfig",1,["fred::types::config::TcpConfig"]],["impl RefUnwindSafe for TracingConfig",1,["fred::types::config::TracingConfig"]],["impl RefUnwindSafe for UnresponsiveConfig",1,["fred::types::config::UnresponsiveConfig"]],["impl RefUnwindSafe for WithCursor",1,["fred::types::redisearch::WithCursor"]],["impl RefUnwindSafe for XCap",1,["fred::types::streams::XCap"]],["impl RefUnwindSafe for XPendingArgs",1,["fred::types::streams::XPendingArgs"]],["impl RefUnwindSafe for ZRange",1,["fred::types::sorted_sets::ZRange"]],["impl<C> !RefUnwindSafe for Pipeline<C>",1,["fred::clients::pipeline::Pipeline"]],["impl<C> RefUnwindSafe for WithOptions<C>
where\n C: RefUnwindSafe,
",1,["fred::clients::options::WithOptions"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[48884]} \ No newline at end of file diff --git a/doc/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js b/doc/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js new file mode 100644 index 00000000..cf4b61a3 --- /dev/null +++ b/doc/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[["impl !UnwindSafe for TlsConnector",1,["fred::protocol::tls::TlsConnector"]],["impl !UnwindSafe for TlsHostMapping",1,["fred::protocol::tls::TlsHostMapping"]],["impl !UnwindSafe for RedisClient",1,["fred::clients::redis::RedisClient"]],["impl !UnwindSafe for RedisPool",1,["fred::clients::pool::RedisPool"]],["impl !UnwindSafe for Replicas",1,["fred::clients::replica::Replicas"]],["impl !UnwindSafe for SentinelClient",1,["fred::clients::sentinel::SentinelClient"]],["impl !UnwindSafe for SubscriberClient",1,["fred::clients::pubsub::SubscriberClient"]],["impl !UnwindSafe for Transaction",1,["fred::clients::transaction::Transaction"]],["impl !UnwindSafe for Builder",1,["fred::types::builder::Builder"]],["impl !UnwindSafe for ConnectionConfig",1,["fred::types::config::ConnectionConfig"]],["impl !UnwindSafe for HScanResult",1,["fred::types::scan::HScanResult"]],["impl !UnwindSafe for RedisConfig",1,["fred::types::config::RedisConfig"]],["impl !UnwindSafe for ReplicaConfig",1,["fred::router::replicas::ReplicaConfig"]],["impl !UnwindSafe for SScanResult",1,["fred::types::scan::SScanResult"]],["impl !UnwindSafe for ScanResult",1,["fred::types::scan::ScanResult"]],["impl !UnwindSafe for SentinelConfig",1,["fred::types::config::SentinelConfig"]],["impl !UnwindSafe for TlsConfig",1,["fred::protocol::tls::TlsConfig"]],["impl !UnwindSafe for ZScanResult",1,["fred::types::scan::ZScanResult"]],["impl UnwindSafe for RedisErrorKind",1,["fred::error::RedisErrorKind"]],["impl UnwindSafe for AggregateOperation",1,["fred::types::redisearch::AggregateOperation"]],["impl UnwindSafe for AggregateOptions",1,["fred::types::misc::AggregateOptions"]],["impl UnwindSafe for Aggregator",1,["fred::types::timeseries::Aggregator"]],["impl UnwindSafe for BackpressurePolicy",1,["fred::types::config::BackpressurePolicy"]],["impl UnwindSafe for Blocking",1,["fred::types::config::Blocking"]],["impl UnwindSafe for BucketTimestamp",1,["fred::types::timeseries::BucketTimestamp"]],["impl UnwindSafe for ClientKillFilter",1,["fred::types::client::ClientKillFilter"]],["impl UnwindSafe for ClientKillType",1,["fred::types::client::ClientKillType"]],["impl UnwindSafe for ClientPauseKind",1,["fred::types::client::ClientPauseKind"]],["impl UnwindSafe for ClientReplyFlag",1,["fred::types::client::ClientReplyFlag"]],["impl UnwindSafe for ClientState",1,["fred::types::misc::ClientState"]],["impl UnwindSafe for ClientUnblockFlag",1,["fred::types::misc::ClientUnblockFlag"]],["impl UnwindSafe for ClusterDiscoveryPolicy",1,["fred::types::config::ClusterDiscoveryPolicy"]],["impl UnwindSafe for ClusterFailoverFlag",1,["fred::types::cluster::ClusterFailoverFlag"]],["impl UnwindSafe for ClusterHash",1,["fred::protocol::hashers::ClusterHash"]],["impl UnwindSafe for ClusterResetFlag",1,["fred::types::cluster::ClusterResetFlag"]],["impl UnwindSafe for ClusterSetSlotState",1,["fred::types::cluster::ClusterSetSlotState"]],["impl UnwindSafe for ClusterState",1,["fred::types::cluster::ClusterState"]],["impl UnwindSafe for ClusterStateChange",1,["fred::types::misc::ClusterStateChange"]],["impl UnwindSafe for DuplicatePolicy",1,["fred::types::timeseries::DuplicatePolicy"]],["impl UnwindSafe for Encoding",1,["fred::types::timeseries::Encoding"]],["impl UnwindSafe for Expiration",1,["fred::types::misc::Expiration"]],["impl UnwindSafe for ExpireOptions",1,["fred::types::misc::ExpireOptions"]],["impl UnwindSafe for FnPolicy",1,["fred::types::misc::FnPolicy"]],["impl UnwindSafe for FunctionFlag",1,["fred::types::scripts::FunctionFlag"]],["impl UnwindSafe for GeoUnit",1,["fred::types::geo::GeoUnit"]],["impl UnwindSafe for GetLabels",1,["fred::types::timeseries::GetLabels"]],["impl UnwindSafe for GetTimestamp",1,["fred::types::timeseries::GetTimestamp"]],["impl UnwindSafe for IndexKind",1,["fred::types::redisearch::IndexKind"]],["impl UnwindSafe for InfoKind",1,["fred::types::misc::InfoKind"]],["impl UnwindSafe for LMoveDirection",1,["fred::types::lists::LMoveDirection"]],["impl UnwindSafe for ListLocation",1,["fred::types::lists::ListLocation"]],["impl UnwindSafe for Load",1,["fred::types::redisearch::Load"]],["impl UnwindSafe for MessageKind",1,["fred::protocol::types::MessageKind"]],["impl UnwindSafe for Ordering",1,["fred::types::sorted_sets::Ordering"]],["impl UnwindSafe for ReconnectError",1,["fred::types::config::ReconnectError"]],["impl UnwindSafe for ReconnectPolicy",1,["fred::types::config::ReconnectPolicy"]],["impl UnwindSafe for RedisValue",1,["fred::types::args::RedisValue"]],["impl UnwindSafe for RedisValueKind",1,["fred::types::args::RedisValueKind"]],["impl UnwindSafe for Reducer",1,["fred::types::timeseries::Reducer"]],["impl UnwindSafe for ReducerFunc",1,["fred::types::redisearch::ReducerFunc"]],["impl UnwindSafe for ScanType",1,["fred::types::scan::ScanType"]],["impl UnwindSafe for ScriptDebugFlag",1,["fred::types::misc::ScriptDebugFlag"]],["impl UnwindSafe for SearchSchemaKind",1,["fred::types::redisearch::SearchSchemaKind"]],["impl UnwindSafe for SentinelFailureKind",1,["fred::types::misc::SentinelFailureKind"]],["impl UnwindSafe for ServerConfig",1,["fred::types::config::ServerConfig"]],["impl UnwindSafe for SetOptions",1,["fred::types::misc::SetOptions"]],["impl UnwindSafe for ShutdownFlags",1,["fred::types::misc::ShutdownFlags"]],["impl UnwindSafe for SortOrder",1,["fred::types::misc::SortOrder"]],["impl UnwindSafe for SpellcheckTerms",1,["fred::types::redisearch::SpellcheckTerms"]],["impl UnwindSafe for StringOrNumber",1,["fred::types::args::StringOrNumber"]],["impl UnwindSafe for Timestamp",1,["fred::types::timeseries::Timestamp"]],["impl UnwindSafe for Toggle",1,["fred::types::client::Toggle"]],["impl UnwindSafe for XCapKind",1,["fred::types::streams::XCapKind"]],["impl UnwindSafe for XCapTrim",1,["fred::types::streams::XCapTrim"]],["impl UnwindSafe for XID",1,["fred::types::streams::XID"]],["impl UnwindSafe for ZCmp",1,["fred::types::sorted_sets::ZCmp"]],["impl UnwindSafe for ZRangeBound",1,["fred::types::sorted_sets::ZRangeBound"]],["impl UnwindSafe for ZRangeKind",1,["fred::types::sorted_sets::ZRangeKind"]],["impl UnwindSafe for ZSort",1,["fred::types::sorted_sets::ZSort"]],["impl UnwindSafe for RedisError",1,["fred::error::RedisError"]],["impl UnwindSafe for Command",1,["fred::monitor::Command"]],["impl UnwindSafe for BackpressureConfig",1,["fred::types::config::BackpressureConfig"]],["impl UnwindSafe for ClusterInfo",1,["fred::types::cluster::ClusterInfo"]],["impl UnwindSafe for ClusterRouting",1,["fred::protocol::types::ClusterRouting"]],["impl UnwindSafe for CustomCommand",1,["fred::types::misc::CustomCommand"]],["impl UnwindSafe for DatabaseMemoryStats",1,["fred::types::misc::DatabaseMemoryStats"]],["impl UnwindSafe for FtAggregateOptions",1,["fred::types::redisearch::FtAggregateOptions"]],["impl UnwindSafe for FtAlterOptions",1,["fred::types::redisearch::FtAlterOptions"]],["impl UnwindSafe for FtCreateOptions",1,["fred::types::redisearch::FtCreateOptions"]],["impl UnwindSafe for FtSearchOptions",1,["fred::types::redisearch::FtSearchOptions"]],["impl UnwindSafe for Function",1,["fred::types::scripts::Function"]],["impl UnwindSafe for GeoPosition",1,["fred::types::geo::GeoPosition"]],["impl UnwindSafe for GeoRadiusInfo",1,["fred::types::geo::GeoRadiusInfo"]],["impl UnwindSafe for GeoValue",1,["fred::types::geo::GeoValue"]],["impl UnwindSafe for GroupBy",1,["fred::types::timeseries::GroupBy"]],["impl UnwindSafe for Invalidation",1,["fred::types::client::Invalidation"]],["impl UnwindSafe for KeyspaceEvent",1,["fred::types::misc::KeyspaceEvent"]],["impl UnwindSafe for Library",1,["fred::types::scripts::Library"]],["impl UnwindSafe for MemoryStats",1,["fred::types::misc::MemoryStats"]],["impl UnwindSafe for Message",1,["fred::protocol::types::Message"]],["impl UnwindSafe for MultipleGeoValues",1,["fred::types::geo::MultipleGeoValues"]],["impl UnwindSafe for MultipleHashSlots",1,["fred::types::multiple::MultipleHashSlots"]],["impl UnwindSafe for MultipleIDs",1,["fred::types::streams::MultipleIDs"]],["impl UnwindSafe for MultipleKeys",1,["fred::types::multiple::MultipleKeys"]],["impl UnwindSafe for MultipleOrderedPairs",1,["fred::types::streams::MultipleOrderedPairs"]],["impl UnwindSafe for MultipleWeights",1,["fred::types::sorted_sets::MultipleWeights"]],["impl UnwindSafe for MultipleZaddValues",1,["fred::types::sorted_sets::MultipleZaddValues"]],["impl UnwindSafe for Options",1,["fred::types::config::Options"]],["impl UnwindSafe for PerformanceConfig",1,["fred::types::config::PerformanceConfig"]],["impl UnwindSafe for RangeAggregation",1,["fred::types::timeseries::RangeAggregation"]],["impl UnwindSafe for RedisKey",1,["fred::types::args::RedisKey"]],["impl UnwindSafe for RedisMap",1,["fred::types::args::RedisMap"]],["impl UnwindSafe for Script",1,["fred::types::scripts::Script"]],["impl UnwindSafe for SearchField",1,["fred::types::redisearch::SearchField"]],["impl UnwindSafe for SearchFilter",1,["fred::types::redisearch::SearchFilter"]],["impl UnwindSafe for SearchGeoFilter",1,["fred::types::redisearch::SearchGeoFilter"]],["impl UnwindSafe for SearchHighlight",1,["fred::types::redisearch::SearchHighlight"]],["impl UnwindSafe for SearchParameter",1,["fred::types::redisearch::SearchParameter"]],["impl UnwindSafe for SearchReducer",1,["fred::types::redisearch::SearchReducer"]],["impl UnwindSafe for SearchSchema",1,["fred::types::redisearch::SearchSchema"]],["impl UnwindSafe for SearchSortBy",1,["fred::types::redisearch::SearchSortBy"]],["impl UnwindSafe for SearchSummarize",1,["fred::types::redisearch::SearchSummarize"]],["impl UnwindSafe for Server",1,["fred::protocol::types::Server"]],["impl UnwindSafe for SlotRange",1,["fred::protocol::types::SlotRange"]],["impl UnwindSafe for SlowlogEntry",1,["fred::types::misc::SlowlogEntry"]],["impl UnwindSafe for Stats",1,["fred::modules::metrics::Stats"]],["impl UnwindSafe for TcpConfig",1,["fred::types::config::TcpConfig"]],["impl UnwindSafe for TracingConfig",1,["fred::types::config::TracingConfig"]],["impl UnwindSafe for UnresponsiveConfig",1,["fred::types::config::UnresponsiveConfig"]],["impl UnwindSafe for WithCursor",1,["fred::types::redisearch::WithCursor"]],["impl UnwindSafe for XCap",1,["fred::types::streams::XCap"]],["impl UnwindSafe for XPendingArgs",1,["fred::types::streams::XPendingArgs"]],["impl UnwindSafe for ZRange",1,["fred::types::sorted_sets::ZRange"]],["impl<C> !UnwindSafe for Pipeline<C>",1,["fred::clients::pipeline::Pipeline"]],["impl<C> UnwindSafe for WithOptions<C>
where\n C: UnwindSafe,
",1,["fred::clients::options::WithOptions"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[47660]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/acl/trait.AclInterface.js b/doc/trait.impl/fred/commands/interfaces/acl/trait.AclInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/acl/trait.AclInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/client/trait.ClientInterface.js b/doc/trait.impl/fred/commands/interfaces/client/trait.ClientInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/client/trait.ClientInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/cluster/trait.ClusterInterface.js b/doc/trait.impl/fred/commands/interfaces/cluster/trait.ClusterInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/cluster/trait.ClusterInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/config/trait.ConfigInterface.js b/doc/trait.impl/fred/commands/interfaces/config/trait.ConfigInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/config/trait.ConfigInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/geo/trait.GeoInterface.js b/doc/trait.impl/fred/commands/interfaces/geo/trait.GeoInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/geo/trait.GeoInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/hashes/trait.HashesInterface.js b/doc/trait.impl/fred/commands/interfaces/hashes/trait.HashesInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/hashes/trait.HashesInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/hyperloglog/trait.HyperloglogInterface.js b/doc/trait.impl/fred/commands/interfaces/hyperloglog/trait.HyperloglogInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/hyperloglog/trait.HyperloglogInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/keys/trait.KeysInterface.js b/doc/trait.impl/fred/commands/interfaces/keys/trait.KeysInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/keys/trait.KeysInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/lists/trait.ListInterface.js b/doc/trait.impl/fred/commands/interfaces/lists/trait.ListInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/lists/trait.ListInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/lua/trait.FunctionInterface.js b/doc/trait.impl/fred/commands/interfaces/lua/trait.FunctionInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/lua/trait.FunctionInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/lua/trait.LuaInterface.js b/doc/trait.impl/fred/commands/interfaces/lua/trait.LuaInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/lua/trait.LuaInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/memory/trait.MemoryInterface.js b/doc/trait.impl/fred/commands/interfaces/memory/trait.MemoryInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/memory/trait.MemoryInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/metrics/trait.MetricsInterface.js b/doc/trait.impl/fred/commands/interfaces/metrics/trait.MetricsInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/metrics/trait.MetricsInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/pubsub/trait.PubsubInterface.js b/doc/trait.impl/fred/commands/interfaces/pubsub/trait.PubsubInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/pubsub/trait.PubsubInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/redis_json/trait.RedisJsonInterface.js b/doc/trait.impl/fred/commands/interfaces/redis_json/trait.RedisJsonInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/redis_json/trait.RedisJsonInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/redisearch/trait.RediSearchInterface.js b/doc/trait.impl/fred/commands/interfaces/redisearch/trait.RediSearchInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/redisearch/trait.RediSearchInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/sentinel/trait.SentinelInterface.js b/doc/trait.impl/fred/commands/interfaces/sentinel/trait.SentinelInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/sentinel/trait.SentinelInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/server/trait.ServerInterface.js b/doc/trait.impl/fred/commands/interfaces/server/trait.ServerInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/server/trait.ServerInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/sets/trait.SetsInterface.js b/doc/trait.impl/fred/commands/interfaces/sets/trait.SetsInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/sets/trait.SetsInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/slowlog/trait.SlowlogInterface.js b/doc/trait.impl/fred/commands/interfaces/slowlog/trait.SlowlogInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/slowlog/trait.SlowlogInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/sorted_sets/trait.SortedSetsInterface.js b/doc/trait.impl/fred/commands/interfaces/sorted_sets/trait.SortedSetsInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/sorted_sets/trait.SortedSetsInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/streams/trait.StreamsInterface.js b/doc/trait.impl/fred/commands/interfaces/streams/trait.StreamsInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/streams/trait.StreamsInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/timeseries/trait.TimeSeriesInterface.js b/doc/trait.impl/fred/commands/interfaces/timeseries/trait.TimeSeriesInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/timeseries/trait.TimeSeriesInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/tracking/trait.TrackingInterface.js b/doc/trait.impl/fred/commands/interfaces/tracking/trait.TrackingInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/tracking/trait.TrackingInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/commands/interfaces/transactions/trait.TransactionInterface.js b/doc/trait.impl/fred/commands/interfaces/transactions/trait.TransactionInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/commands/interfaces/transactions/trait.TransactionInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/glommio/interfaces/trait.ClientLike.js b/doc/trait.impl/fred/glommio/interfaces/trait.ClientLike.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/glommio/interfaces/trait.ClientLike.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/interfaces/trait.AuthInterface.js b/doc/trait.impl/fred/interfaces/trait.AuthInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/interfaces/trait.AuthInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/interfaces/trait.EventInterface.js b/doc/trait.impl/fred/interfaces/trait.EventInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/interfaces/trait.EventInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/interfaces/trait.HeartbeatInterface.js b/doc/trait.impl/fred/interfaces/trait.HeartbeatInterface.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/interfaces/trait.HeartbeatInterface.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/modules/response/trait.FromRedis.js b/doc/trait.impl/fred/modules/response/trait.FromRedis.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/modules/response/trait.FromRedis.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/modules/response/trait.FromRedisKey.js b/doc/trait.impl/fred/modules/response/trait.FromRedisKey.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/modules/response/trait.FromRedisKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/trait.impl/fred/types/scan/trait.Scanner.js b/doc/trait.impl/fred/types/scan/trait.Scanner.js new file mode 100644 index 00000000..f760c4b0 --- /dev/null +++ b/doc/trait.impl/fred/types/scan/trait.Scanner.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["fred",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/doc/type.impl/alloc/vec/struct.Vec.js b/doc/type.impl/alloc/vec/struct.Vec.js new file mode 100644 index 00000000..8da6f1ec --- /dev/null +++ b/doc/type.impl/alloc/vec/struct.Vec.js @@ -0,0 +1,9 @@ +(function() { + var type_impls = Object.fromEntries([["fred",[["
§

impl<T> ArrayLike for Vec<T>

§

type Item = T

Type of the elements being stored.
","ArrayLike","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.5.0 · source§

impl<T, A> AsMut<[T]> for Vec<T, A>
where\n A: Allocator,

source§

fn as_mut(&mut self) -> &mut [T]

Converts this type into a mutable reference of the (usually inferred) input type.
","AsMut<[T]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.5.0 · source§

impl<T, A> AsMut<Vec<T, A>> for Vec<T, A>
where\n A: Allocator,

source§

fn as_mut(&mut self) -> &mut Vec<T, A>

Converts this type into a mutable reference of the (usually inferred) input type.
","AsMut>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> AsRef<[T]> for Vec<T, A>
where\n A: Allocator,

source§

fn as_ref(&self) -> &[T]

Converts this type into a shared reference of the (usually inferred) input type.
","AsRef<[T]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> AsRef<Vec<T, A>> for Vec<T, A>
where\n A: Allocator,

source§

fn as_ref(&self) -> &Vec<T, A>

Converts this type into a shared reference of the (usually inferred) input type.
","AsRef>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> Borrow<[T]> for Vec<T, A>
where\n A: Allocator,

source§

fn borrow(&self) -> &[T]

Immutably borrows from an owned value. Read more
","Borrow<[T]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> BorrowMut<[T]> for Vec<T, A>
where\n A: Allocator,

source§

fn borrow_mut(&mut self) -> &mut [T]

Mutably borrows from an owned value. Read more
","BorrowMut<[T]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> Clone for Vec<T, A>
where\n T: Clone,\n A: Allocator + Clone,

source§

fn clone_from(&mut self, source: &Vec<T, A>)

Overwrites the contents of self with a clone of the contents of source.

\n

This method is preferred over simply assigning source.clone() to self,\nas it avoids reallocation if possible. Additionally, if the element type\nT overrides clone_from(), this will reuse the resources of self’s\nelements as well.

\n
§Examples
\n
let x = vec![5, 6, 7];\nlet mut y = vec![8, 9, 10];\nlet yp: *const i32 = y.as_ptr();\n\ny.clone_from(&x);\n\n// The value is the same\nassert_eq!(x, y);\n\n// And no reallocation occurred\nassert_eq!(yp, y.as_ptr());
\n
source§

fn clone(&self) -> Vec<T, A>

Returns a copy of the value. Read more
","Clone","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> Debug for Vec<T, A>
where\n T: Debug,\n A: Allocator,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
","Debug","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T> Default for Vec<T>

source§

fn default() -> Vec<T>

Creates an empty Vec<T>.

\n

The vector will not allocate until elements are pushed onto it.

\n
","Default","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> Deref for Vec<T, A>
where\n A: Allocator,

§

type Target = [T]

The resulting type after dereferencing.
source§

fn deref(&self) -> &[T]

Dereferences the value.
","Deref","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> DerefMut for Vec<T, A>
where\n A: Allocator,

source§

fn deref_mut(&mut self) -> &mut [T]

Mutably dereferences the value.
","DerefMut","fred::types::timeseries::Resp2TimeSeriesValues"],["
source§

impl<'de, T> Deserialize<'de> for Vec<T>
where\n T: Deserialize<'de>,

source§

fn deserialize<D>(\n deserializer: D,\n) -> Result<Vec<T>, <D as Deserializer<'de>>::Error>
where\n D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
","Deserialize<'de>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> Drop for Vec<T, A>
where\n A: Allocator,

source§

fn drop(&mut self)

Executes the destructor for this type. Read more
","Drop","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.2.0 · source§

impl<'a, T, A> Extend<&'a T> for Vec<T, A>
where\n T: Copy + 'a,\n A: Allocator,

Extend implementation that copies elements out of references before pushing them onto the Vec.

\n

This implementation is specialized for slice iterators, where it uses copy_from_slice to\nappend the entire slice at once.

\n
source§

fn extend<I>(&mut self, iter: I)
where\n I: IntoIterator<Item = &'a T>,

Extends a collection with the contents of an iterator. Read more
source§

fn extend_one(&mut self, _: &'a T)

🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
source§

fn extend_reserve(&mut self, additional: usize)

🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
","Extend<&'a T>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> Extend<T> for Vec<T, A>
where\n A: Allocator,

source§

fn extend<I>(&mut self, iter: I)
where\n I: IntoIterator<Item = T>,

Extends a collection with the contents of an iterator. Read more
source§

fn extend_one(&mut self, item: T)

🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
source§

fn extend_reserve(&mut self, additional: usize)

🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
","Extend","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T> From<&[T]> for Vec<T>
where\n T: Clone,

source§

fn from(s: &[T]) -> Vec<T>

Allocates a Vec<T> and fills it by cloning s’s items.

\n
§Examples
\n
assert_eq!(Vec::from(&[1, 2, 3][..]), vec![1, 2, 3]);
\n
","From<&[T]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.74.0 · source§

impl<T, const N: usize> From<&[T; N]> for Vec<T>
where\n T: Clone,

source§

fn from(s: &[T; N]) -> Vec<T>

Allocates a Vec<T> and fills it by cloning s’s items.

\n
§Examples
\n
assert_eq!(Vec::from(&[1, 2, 3]), vec![1, 2, 3]);
\n
","From<&[T; N]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.19.0 · source§

impl<T> From<&mut [T]> for Vec<T>
where\n T: Clone,

source§

fn from(s: &mut [T]) -> Vec<T>

Allocates a Vec<T> and fills it by cloning s’s items.

\n
§Examples
\n
assert_eq!(Vec::from(&mut [1, 2, 3][..]), vec![1, 2, 3]);
\n
","From<&mut [T]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.74.0 · source§

impl<T, const N: usize> From<&mut [T; N]> for Vec<T>
where\n T: Clone,

source§

fn from(s: &mut [T; N]) -> Vec<T>

Allocates a Vec<T> and fills it by cloning s’s items.

\n
§Examples
\n
assert_eq!(Vec::from(&mut [1, 2, 3]), vec![1, 2, 3]);
\n
","From<&mut [T; N]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.44.0 · source§

impl<T, const N: usize> From<[T; N]> for Vec<T>

source§

fn from(s: [T; N]) -> Vec<T>

Allocates a Vec<T> and moves s’s items into it.

\n
§Examples
\n
assert_eq!(Vec::from([1, 2, 3]), vec![1, 2, 3]);
\n
","From<[T; N]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.5.0 · source§

impl<T, A> From<BinaryHeap<T, A>> for Vec<T, A>
where\n A: Allocator,

source§

fn from(heap: BinaryHeap<T, A>) -> Vec<T, A>

Converts a BinaryHeap<T> into a Vec<T>.

\n

This conversion requires no data movement or allocation, and has\nconstant time complexity.

\n
","From>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.18.0 · source§

impl<T, A> From<Box<[T], A>> for Vec<T, A>
where\n A: Allocator,

source§

fn from(s: Box<[T], A>) -> Vec<T, A>

Converts a boxed slice into a vector by transferring ownership of\nthe existing heap allocation.

\n
§Examples
\n
let b: Box<[i32]> = vec![1, 2, 3].into_boxed_slice();\nassert_eq!(Vec::from(b), vec![1, 2, 3]);
\n
","From>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.14.0 · source§

impl<'a, T> From<Cow<'a, [T]>> for Vec<T>
where\n [T]: ToOwned<Owned = Vec<T>>,

source§

fn from(s: Cow<'a, [T]>) -> Vec<T>

Converts a clone-on-write slice into a vector.

\n

If s already owns a Vec<T>, it will be returned directly.\nIf s is borrowing a slice, a new Vec<T> will be allocated and\nfilled by cloning s’s items into it.

\n
§Examples
\n
let o: Cow<'_, [i32]> = Cow::Owned(vec![1, 2, 3]);\nlet b: Cow<'_, [i32]> = Cow::Borrowed(&[1, 2, 3]);\nassert_eq!(Vec::from(o), Vec::from(b));
\n
","From>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.10.0 · source§

impl<T, A> From<VecDeque<T, A>> for Vec<T, A>
where\n A: Allocator,

source§

fn from(other: VecDeque<T, A>) -> Vec<T, A>

Turn a VecDeque<T> into a Vec<T>.

\n

This never needs to re-allocate, but does need to do O(n) data movement if\nthe circular buffer doesn’t happen to be at the beginning of the allocation.

\n
§Examples
\n
use std::collections::VecDeque;\n\n// This one is *O*(1).\nlet deque: VecDeque<_> = (1..5).collect();\nlet ptr = deque.as_slices().0.as_ptr();\nlet vec = Vec::from(deque);\nassert_eq!(vec, [1, 2, 3, 4]);\nassert_eq!(vec.as_ptr(), ptr);\n\n// This one needs data rearranging.\nlet mut deque: VecDeque<_> = (1..5).collect();\ndeque.push_front(9);\ndeque.push_front(8);\nlet ptr = deque.as_slices().1.as_ptr();\nlet vec = Vec::from(deque);\nassert_eq!(vec, [8, 9, 1, 2, 3, 4]);\nassert_eq!(vec.as_ptr(), ptr);
\n
","From>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T> FromIterator<T> for Vec<T>

Collects an iterator into a Vec, commonly called via Iterator::collect()

\n

§Allocation behavior

\n

In general Vec does not guarantee any particular growth or allocation strategy.\nThat also applies to this trait impl.

\n

Note: This section covers implementation details and is therefore exempt from\nstability guarantees.

\n

Vec may use any or none of the following strategies,\ndepending on the supplied iterator:

\n
    \n
  • preallocate based on Iterator::size_hint()\n
      \n
    • and panic if the number of items is outside the provided lower/upper bounds
    • \n
    \n
  • \n
  • use an amortized growth strategy similar to pushing one item at a time
  • \n
  • perform the iteration in-place on the original allocation backing the iterator
  • \n
\n

The last case warrants some attention. It is an optimization that in many cases reduces peak memory\nconsumption and improves cache locality. But when big, short-lived allocations are created,\nonly a small fraction of their items get collected, no further use is made of the spare capacity\nand the resulting Vec is moved into a longer-lived structure, then this can lead to the large\nallocations having their lifetimes unnecessarily extended which can result in increased memory\nfootprint.

\n

In cases where this is an issue, the excess capacity can be discarded with Vec::shrink_to(),\nVec::shrink_to_fit() or by collecting into Box<[T]> instead, which additionally reduces\nthe size of the long-lived struct.

\n\n
static LONG_LIVED: Mutex<Vec<Vec<u16>>> = Mutex::new(Vec::new());\n\nfor i in 0..10 {\n    let big_temporary: Vec<u16> = (0..1024).collect();\n    // discard most items\n    let mut result: Vec<_> = big_temporary.into_iter().filter(|i| i % 100 == 0).collect();\n    // without this a lot of unused capacity might be moved into the global\n    result.shrink_to_fit();\n    LONG_LIVED.lock().unwrap().push(result);\n}
\n
source§

fn from_iter<I>(iter: I) -> Vec<T>
where\n I: IntoIterator<Item = T>,

Creates a value from an iterator. Read more
","FromIterator","fred::types::timeseries::Resp2TimeSeriesValues"],["
source§

impl<T> FromRedis for Vec<T>
where\n T: FromRedis,

","FromRedis","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> Hash for Vec<T, A>
where\n T: Hash,\n A: Allocator,

The hash of a vector is the same as that of the corresponding slice,\nas required by the core::borrow::Borrow implementation.

\n\n
use std::hash::BuildHasher;\n\nlet b = std::hash::RandomState::new();\nlet v: Vec<u8> = vec![0xa8, 0x3c, 0x09];\nlet s: &[u8] = &[0xa8, 0x3c, 0x09];\nassert_eq!(b.hash_one(v), b.hash_one(s));
\n
source§

fn hash<H>(&self, state: &mut H)
where\n H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where\n H: Hasher,\n Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
","Hash","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, I, A> Index<I> for Vec<T, A>
where\n I: SliceIndex<[T]>,\n A: Allocator,

§

type Output = <I as SliceIndex<[T]>>::Output

The returned type after indexing.
source§

fn index(&self, index: I) -> &<Vec<T, A> as Index<I>>::Output

Performs the indexing (container[index]) operation. Read more
","Index","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, I, A> IndexMut<I> for Vec<T, A>
where\n I: SliceIndex<[T]>,\n A: Allocator,

source§

fn index_mut(&mut self, index: I) -> &mut <Vec<T, A> as Index<I>>::Output

Performs the mutable indexing (container[index]) operation. Read more
","IndexMut","fred::types::timeseries::Resp2TimeSeriesValues"],["
source§

impl<'de, T, E> IntoDeserializer<'de, E> for Vec<T>
where\n T: IntoDeserializer<'de, E>,\n E: Error,

§

type Deserializer = SeqDeserializer<<Vec<T> as IntoIterator>::IntoIter, E>

The type of the deserializer being converted into.
source§

fn into_deserializer(self) -> <Vec<T> as IntoDeserializer<'de, E>>::Deserializer

Convert this value into a deserializer.
","IntoDeserializer<'de, E>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> IntoIterator for Vec<T, A>
where\n A: Allocator,

source§

fn into_iter(self) -> <Vec<T, A> as IntoIterator>::IntoIter

Creates a consuming iterator, that is, one that moves each value out of\nthe vector (from start to end). The vector cannot be used after calling\nthis.

\n
§Examples
\n
let v = vec![\"a\".to_string(), \"b\".to_string()];\nlet mut v_iter = v.into_iter();\n\nlet first_element: Option<String> = v_iter.next();\n\nassert_eq!(first_element, Some(\"a\".to_string()));\nassert_eq!(v_iter.next(), Some(\"b\".to_string()));\nassert_eq!(v_iter.next(), None);
\n
§

type Item = T

The type of the elements being iterated over.
§

type IntoIter = IntoIter<T, A>

Which kind of iterator are we turning this into?
","IntoIterator","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> Ord for Vec<T, A>
where\n T: Ord,\n A: Allocator,

Implements ordering of vectors, lexicographically.

\n
source§

fn cmp(&self, other: &Vec<T, A>) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · source§

fn max(self, other: Self) -> Self
where\n Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · source§

fn min(self, other: Self) -> Self
where\n Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Self
where\n Self: Sized + PartialOrd,

Restrict a value to a certain interval. Read more
","Ord","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, U, A> PartialEq<&[U]> for Vec<T, A>
where\n A: Allocator,\n T: PartialEq<U>,

source§

fn eq(&self, other: &&[U]) -> bool

Tests for self and other values to be equal, and is used by ==.
source§

fn ne(&self, other: &&[U]) -> bool

Tests for !=. The default implementation is almost always sufficient,\nand should not be overridden without very good reason.
","PartialEq<&[U]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, U, A, const N: usize> PartialEq<&[U; N]> for Vec<T, A>
where\n A: Allocator,\n T: PartialEq<U>,

source§

fn eq(&self, other: &&[U; N]) -> bool

Tests for self and other values to be equal, and is used by ==.
source§

fn ne(&self, other: &&[U; N]) -> bool

Tests for !=. The default implementation is almost always sufficient,\nand should not be overridden without very good reason.
","PartialEq<&[U; N]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, U, A> PartialEq<&mut [U]> for Vec<T, A>
where\n A: Allocator,\n T: PartialEq<U>,

source§

fn eq(&self, other: &&mut [U]) -> bool

Tests for self and other values to be equal, and is used by ==.
source§

fn ne(&self, other: &&mut [U]) -> bool

Tests for !=. The default implementation is almost always sufficient,\nand should not be overridden without very good reason.
","PartialEq<&mut [U]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.48.0 · source§

impl<T, U, A> PartialEq<[U]> for Vec<T, A>
where\n A: Allocator,\n T: PartialEq<U>,

source§

fn eq(&self, other: &[U]) -> bool

Tests for self and other values to be equal, and is used by ==.
source§

fn ne(&self, other: &[U]) -> bool

Tests for !=. The default implementation is almost always sufficient,\nand should not be overridden without very good reason.
","PartialEq<[U]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, U, A, const N: usize> PartialEq<[U; N]> for Vec<T, A>
where\n A: Allocator,\n T: PartialEq<U>,

source§

fn eq(&self, other: &[U; N]) -> bool

Tests for self and other values to be equal, and is used by ==.
source§

fn ne(&self, other: &[U; N]) -> bool

Tests for !=. The default implementation is almost always sufficient,\nand should not be overridden without very good reason.
","PartialEq<[U; N]>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, U, A1, A2> PartialEq<Vec<U, A2>> for Vec<T, A1>
where\n A1: Allocator,\n A2: Allocator,\n T: PartialEq<U>,

source§

fn eq(&self, other: &Vec<U, A2>) -> bool

Tests for self and other values to be equal, and is used by ==.
source§

fn ne(&self, other: &Vec<U, A2>) -> bool

Tests for !=. The default implementation is almost always sufficient,\nand should not be overridden without very good reason.
","PartialEq>","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A1, A2> PartialOrd<Vec<T, A2>> for Vec<T, A1>
where\n T: PartialOrd,\n A1: Allocator,\n A2: Allocator,

Implements comparison of vectors, lexicographically.

\n
source§

fn partial_cmp(&self, other: &Vec<T, A2>) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the\n<= operator. Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the >\noperator. Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by\nthe >= operator. Read more
","PartialOrd>","fred::types::timeseries::Resp2TimeSeriesValues"],["
source§

impl<T> Serialize for Vec<T>
where\n T: Serialize,

source§

fn serialize<S>(\n &self,\n serializer: S,\n) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where\n S: Serializer,

Serialize this value into the given Serde serializer. Read more
","Serialize","fred::types::timeseries::Resp2TimeSeriesValues"],["
§

impl<T> Sink<T> for Vec<T>

§

type Error = Infallible

The type of value produced by the sink when an error occurs.
§

fn poll_ready(\n self: Pin<&mut Vec<T>>,\n _: &mut Context<'_>,\n) -> Poll<Result<(), <Vec<T> as Sink<T>>::Error>>

Attempts to prepare the Sink to receive a value. Read more
§

fn start_send(\n self: Pin<&mut Vec<T>>,\n item: T,\n) -> Result<(), <Vec<T> as Sink<T>>::Error>

Begin the process of sending a value to the sink.\nEach call to this function must be preceded by a successful call to\npoll_ready which returned Poll::Ready(Ok(())). Read more
§

fn poll_flush(\n self: Pin<&mut Vec<T>>,\n _: &mut Context<'_>,\n) -> Poll<Result<(), <Vec<T> as Sink<T>>::Error>>

Flush any remaining output from this sink. Read more
§

fn poll_close(\n self: Pin<&mut Vec<T>>,\n _: &mut Context<'_>,\n) -> Poll<Result<(), <Vec<T> as Sink<T>>::Error>>

Flush any remaining output and close this sink, if necessary. Read more
","Sink","fred::types::timeseries::Resp2TimeSeriesValues"],["
source§

impl<T> Vec<T>

1.0.0 (const: 1.39.0) · source

pub const fn new() -> Vec<T>

Constructs a new, empty Vec<T>.

\n

The vector will not allocate until elements are pushed onto it.

\n
§Examples
\n
let mut vec: Vec<i32> = Vec::new();
\n
1.0.0 · source

pub fn with_capacity(capacity: usize) -> Vec<T>

Constructs a new, empty Vec<T> with at least the specified capacity.

\n

The vector will be able to hold at least capacity elements without\nreallocating. This method is allowed to allocate for more elements than\ncapacity. If capacity is 0, the vector will not allocate.

\n

It is important to note that although the returned vector has the\nminimum capacity specified, the vector will have a zero length. For\nan explanation of the difference between length and capacity, see\nCapacity and reallocation.

\n

If it is important to know the exact allocated capacity of a Vec,\nalways use the capacity method after construction.

\n

For Vec<T> where T is a zero-sized type, there will be no allocation\nand the capacity will always be usize::MAX.

\n
§Panics
\n

Panics if the new capacity exceeds isize::MAX bytes.

\n
§Examples
\n
let mut vec = Vec::with_capacity(10);\n\n// The vector contains no items, even though it has capacity for more\nassert_eq!(vec.len(), 0);\nassert!(vec.capacity() >= 10);\n\n// These are all done without reallocating...\nfor i in 0..10 {\n    vec.push(i);\n}\nassert_eq!(vec.len(), 10);\nassert!(vec.capacity() >= 10);\n\n// ...but this may make the vector reallocate\nvec.push(11);\nassert_eq!(vec.len(), 11);\nassert!(vec.capacity() >= 11);\n\n// A vector of a zero-sized type will always over-allocate, since no\n// allocation is necessary\nlet vec_units = Vec::<()>::with_capacity(10);\nassert_eq!(vec_units.capacity(), usize::MAX);
\n
source

pub fn try_with_capacity(capacity: usize) -> Result<Vec<T>, TryReserveError>

🔬This is a nightly-only experimental API. (try_with_capacity)

Constructs a new, empty Vec<T> with at least the specified capacity.

\n

The vector will be able to hold at least capacity elements without\nreallocating. This method is allowed to allocate for more elements than\ncapacity. If capacity is 0, the vector will not allocate.

\n
§Errors
\n

Returns an error if the capacity exceeds isize::MAX bytes,\nor if the allocator reports allocation failure.

\n
1.0.0 · source

pub unsafe fn from_raw_parts(\n ptr: *mut T,\n length: usize,\n capacity: usize,\n) -> Vec<T>

Creates a Vec<T> directly from a pointer, a length, and a capacity.

\n
§Safety
\n

This is highly unsafe, due to the number of invariants that aren’t\nchecked:

\n
    \n
  • ptr must have been allocated using the global allocator, such as via\nthe alloc::alloc function.
  • \n
  • T needs to have the same alignment as what ptr was allocated with.\n(T having a less strict alignment is not sufficient, the alignment really\nneeds to be equal to satisfy the dealloc requirement that memory must be\nallocated and deallocated with the same layout.)
  • \n
  • The size of T times the capacity (ie. the allocated size in bytes) needs\nto be the same size as the pointer was allocated with. (Because similar to\nalignment, dealloc must be called with the same layout size.)
  • \n
  • length needs to be less than or equal to capacity.
  • \n
  • The first length values must be properly initialized values of type T.
  • \n
  • capacity needs to be the capacity that the pointer was allocated with.
  • \n
  • The allocated size in bytes must be no larger than isize::MAX.\nSee the safety documentation of pointer::offset.
  • \n
\n

These requirements are always upheld by any ptr that has been allocated\nvia Vec<T>. Other allocation sources are allowed if the invariants are\nupheld.

\n

Violating these may cause problems like corrupting the allocator’s\ninternal data structures. For example it is normally not safe\nto build a Vec<u8> from a pointer to a C char array with length\nsize_t, doing so is only safe if the array was initially allocated by\na Vec or String.\nIt’s also not safe to build one from a Vec<u16> and its length, because\nthe allocator cares about the alignment, and these two types have different\nalignments. The buffer was allocated with alignment 2 (for u16), but after\nturning it into a Vec<u8> it’ll be deallocated with alignment 1. To avoid\nthese issues, it is often preferable to do casting/transmuting using\nslice::from_raw_parts instead.

\n

The ownership of ptr is effectively transferred to the\nVec<T> which may then deallocate, reallocate or change the\ncontents of memory pointed to by the pointer at will. Ensure\nthat nothing else uses the pointer after calling this\nfunction.

\n
§Examples
\n
use std::ptr;\nuse std::mem;\n\nlet v = vec![1, 2, 3];\n\n// Prevent running `v`'s destructor so we are in complete control\n// of the allocation.\nlet mut v = mem::ManuallyDrop::new(v);\n\n// Pull out the various important pieces of information about `v`\nlet p = v.as_mut_ptr();\nlet len = v.len();\nlet cap = v.capacity();\n\nunsafe {\n    // Overwrite memory with 4, 5, 6\n    for i in 0..len {\n        ptr::write(p.add(i), 4 + i);\n    }\n\n    // Put everything back together into a Vec\n    let rebuilt = Vec::from_raw_parts(p, len, cap);\n    assert_eq!(rebuilt, [4, 5, 6]);\n}
\n

Using memory that was allocated elsewhere:

\n\n
use std::alloc::{alloc, Layout};\n\nfn main() {\n    let layout = Layout::array::<u32>(16).expect(\"overflow cannot happen\");\n\n    let vec = unsafe {\n        let mem = alloc(layout).cast::<u32>();\n        if mem.is_null() {\n            return;\n        }\n\n        mem.write(1_000_000);\n\n        Vec::from_raw_parts(mem, 1, 16)\n    };\n\n    assert_eq!(vec, &[1_000_000]);\n    assert_eq!(vec.capacity(), 16);\n}
\n
",0,"fred::types::timeseries::Resp2TimeSeriesValues"],["
source§

impl<T, A> Vec<T, A>
where\n T: Clone,\n A: Allocator,

1.5.0 · source

pub fn resize(&mut self, new_len: usize, value: T)

Resizes the Vec in-place so that len is equal to new_len.

\n

If new_len is greater than len, the Vec is extended by the\ndifference, with each additional slot filled with value.\nIf new_len is less than len, the Vec is simply truncated.

\n

This method requires T to implement Clone,\nin order to be able to clone the passed value.\nIf you need more flexibility (or want to rely on Default instead of\nClone), use Vec::resize_with.\nIf you only need to resize to a smaller size, use Vec::truncate.

\n
§Examples
\n
let mut vec = vec![\"hello\"];\nvec.resize(3, \"world\");\nassert_eq!(vec, [\"hello\", \"world\", \"world\"]);\n\nlet mut vec = vec![1, 2, 3, 4];\nvec.resize(2, 0);\nassert_eq!(vec, [1, 2]);
\n
1.6.0 · source

pub fn extend_from_slice(&mut self, other: &[T])

Clones and appends all elements in a slice to the Vec.

\n

Iterates over the slice other, clones each element, and then appends\nit to this Vec. The other slice is traversed in-order.

\n

Note that this function is same as extend except that it is\nspecialized to work with slices instead. If and when Rust gets\nspecialization this function will likely be deprecated (but still\navailable).

\n
§Examples
\n
let mut vec = vec![1];\nvec.extend_from_slice(&[2, 3, 4]);\nassert_eq!(vec, [1, 2, 3, 4]);
\n
1.53.0 · source

pub fn extend_from_within<R>(&mut self, src: R)
where\n R: RangeBounds<usize>,

Copies elements from src range to the end of the vector.

\n
§Panics
\n

Panics if the starting point is greater than the end point or if\nthe end point is greater than the length of the vector.

\n
§Examples
\n
let mut vec = vec![0, 1, 2, 3, 4];\n\nvec.extend_from_within(2..);\nassert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4]);\n\nvec.extend_from_within(..2);\nassert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1]);\n\nvec.extend_from_within(4..8);\nassert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1, 4, 2, 3, 4]);
\n
",0,"fred::types::timeseries::Resp2TimeSeriesValues"],["
source§

impl<T, A> Vec<T, A>
where\n T: PartialEq,\n A: Allocator,

1.0.0 · source

pub fn dedup(&mut self)

Removes consecutive repeated elements in the vector according to the\nPartialEq trait implementation.

\n

If the vector is sorted, this removes all duplicates.

\n
§Examples
\n
let mut vec = vec![1, 2, 2, 3, 2];\n\nvec.dedup();\n\nassert_eq!(vec, [1, 2, 3, 2]);
\n
",0,"fred::types::timeseries::Resp2TimeSeriesValues"],["
source§

impl<T, A> Vec<T, A>
where\n A: Allocator,

1.21.0 · source

pub fn splice<R, I>(\n &mut self,\n range: R,\n replace_with: I,\n) -> Splice<'_, <I as IntoIterator>::IntoIter, A>
where\n R: RangeBounds<usize>,\n I: IntoIterator<Item = T>,

Creates a splicing iterator that replaces the specified range in the vector\nwith the given replace_with iterator and yields the removed items.\nreplace_with does not need to be the same length as range.

\n

range is removed even if the iterator is not consumed until the end.

\n

It is unspecified how many elements are removed from the vector\nif the Splice value is leaked.

\n

The input iterator replace_with is only consumed when the Splice value is dropped.

\n

This is optimal if:

\n
    \n
  • The tail (elements in the vector after range) is empty,
  • \n
  • or replace_with yields fewer or equal elements than range’s length
  • \n
  • or the lower bound of its size_hint() is exact.
  • \n
\n

Otherwise, a temporary vector is allocated and the tail is moved twice.

\n
§Panics
\n

Panics if the starting point is greater than the end point or if\nthe end point is greater than the length of the vector.

\n
§Examples
\n
let mut v = vec![1, 2, 3, 4];\nlet new = [7, 8, 9];\nlet u: Vec<_> = v.splice(1..3, new).collect();\nassert_eq!(v, &[1, 7, 8, 9, 4]);\nassert_eq!(u, &[2, 3]);
\n
source

pub fn extract_if<F>(&mut self, filter: F) -> ExtractIf<'_, T, F, A>
where\n F: FnMut(&mut T) -> bool,

🔬This is a nightly-only experimental API. (extract_if)

Creates an iterator which uses a closure to determine if an element should be removed.

\n

If the closure returns true, then the element is removed and yielded.\nIf the closure returns false, the element will remain in the vector and will not be yielded\nby the iterator.

\n

If the returned ExtractIf is not exhausted, e.g. because it is dropped without iterating\nor the iteration short-circuits, then the remaining elements will be retained.\nUse retain with a negated predicate if you do not need the returned iterator.

\n

Using this method is equivalent to the following code:

\n\n
let mut i = 0;\nwhile i < vec.len() {\n    if some_predicate(&mut vec[i]) {\n        let val = vec.remove(i);\n        // your code here\n    } else {\n        i += 1;\n    }\n}\n
\n

But extract_if is easier to use. extract_if is also more efficient,\nbecause it can backshift the elements of the array in bulk.

\n

Note that extract_if also lets you mutate every element in the filter closure,\nregardless of whether you choose to keep or remove it.

\n
§Examples
\n

Splitting an array into evens and odds, reusing the original allocation:

\n\n
#![feature(extract_if)]\nlet mut numbers = vec![1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15];\n\nlet evens = numbers.extract_if(|x| *x % 2 == 0).collect::<Vec<_>>();\nlet odds = numbers;\n\nassert_eq!(evens, vec![2, 4, 6, 8, 14]);\nassert_eq!(odds, vec![1, 3, 5, 9, 11, 13, 15]);
\n
",0,"fred::types::timeseries::Resp2TimeSeriesValues"],["
source§

impl<T, A> Vec<T, A>
where\n A: Allocator,

source

pub const fn new_in(alloc: A) -> Vec<T, A>

🔬This is a nightly-only experimental API. (allocator_api)

Constructs a new, empty Vec<T, A>.

\n

The vector will not allocate until elements are pushed onto it.

\n
§Examples
\n
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet mut vec: Vec<i32, _> = Vec::new_in(System);
\n
source

pub fn with_capacity_in(capacity: usize, alloc: A) -> Vec<T, A>

🔬This is a nightly-only experimental API. (allocator_api)

Constructs a new, empty Vec<T, A> with at least the specified capacity\nwith the provided allocator.

\n

The vector will be able to hold at least capacity elements without\nreallocating. This method is allowed to allocate for more elements than\ncapacity. If capacity is 0, the vector will not allocate.

\n

It is important to note that although the returned vector has the\nminimum capacity specified, the vector will have a zero length. For\nan explanation of the difference between length and capacity, see\nCapacity and reallocation.

\n

If it is important to know the exact allocated capacity of a Vec,\nalways use the capacity method after construction.

\n

For Vec<T, A> where T is a zero-sized type, there will be no allocation\nand the capacity will always be usize::MAX.

\n
§Panics
\n

Panics if the new capacity exceeds isize::MAX bytes.

\n
§Examples
\n
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet mut vec = Vec::with_capacity_in(10, System);\n\n// The vector contains no items, even though it has capacity for more\nassert_eq!(vec.len(), 0);\nassert!(vec.capacity() >= 10);\n\n// These are all done without reallocating...\nfor i in 0..10 {\n    vec.push(i);\n}\nassert_eq!(vec.len(), 10);\nassert!(vec.capacity() >= 10);\n\n// ...but this may make the vector reallocate\nvec.push(11);\nassert_eq!(vec.len(), 11);\nassert!(vec.capacity() >= 11);\n\n// A vector of a zero-sized type will always over-allocate, since no\n// allocation is necessary\nlet vec_units = Vec::<(), System>::with_capacity_in(10, System);\nassert_eq!(vec_units.capacity(), usize::MAX);
\n
source

pub fn try_with_capacity_in(\n capacity: usize,\n alloc: A,\n) -> Result<Vec<T, A>, TryReserveError>

🔬This is a nightly-only experimental API. (allocator_api)

Constructs a new, empty Vec<T, A> with at least the specified capacity\nwith the provided allocator.

\n

The vector will be able to hold at least capacity elements without\nreallocating. This method is allowed to allocate for more elements than\ncapacity. If capacity is 0, the vector will not allocate.

\n
§Errors
\n

Returns an error if the capacity exceeds isize::MAX bytes,\nor if the allocator reports allocation failure.

\n
source

pub unsafe fn from_raw_parts_in(\n ptr: *mut T,\n length: usize,\n capacity: usize,\n alloc: A,\n) -> Vec<T, A>

🔬This is a nightly-only experimental API. (allocator_api)

Creates a Vec<T, A> directly from a pointer, a length, a capacity,\nand an allocator.

\n
§Safety
\n

This is highly unsafe, due to the number of invariants that aren’t\nchecked:

\n
    \n
  • ptr must be currently allocated via the given allocator alloc.
  • \n
  • T needs to have the same alignment as what ptr was allocated with.\n(T having a less strict alignment is not sufficient, the alignment really\nneeds to be equal to satisfy the dealloc requirement that memory must be\nallocated and deallocated with the same layout.)
  • \n
  • The size of T times the capacity (ie. the allocated size in bytes) needs\nto be the same size as the pointer was allocated with. (Because similar to\nalignment, dealloc must be called with the same layout size.)
  • \n
  • length needs to be less than or equal to capacity.
  • \n
  • The first length values must be properly initialized values of type T.
  • \n
  • capacity needs to fit the layout size that the pointer was allocated with.
  • \n
  • The allocated size in bytes must be no larger than isize::MAX.\nSee the safety documentation of pointer::offset.
  • \n
\n

These requirements are always upheld by any ptr that has been allocated\nvia Vec<T, A>. Other allocation sources are allowed if the invariants are\nupheld.

\n

Violating these may cause problems like corrupting the allocator’s\ninternal data structures. For example it is not safe\nto build a Vec<u8> from a pointer to a C char array with length size_t.\nIt’s also not safe to build one from a Vec<u16> and its length, because\nthe allocator cares about the alignment, and these two types have different\nalignments. The buffer was allocated with alignment 2 (for u16), but after\nturning it into a Vec<u8> it’ll be deallocated with alignment 1.

\n

The ownership of ptr is effectively transferred to the\nVec<T> which may then deallocate, reallocate or change the\ncontents of memory pointed to by the pointer at will. Ensure\nthat nothing else uses the pointer after calling this\nfunction.

\n
§Examples
\n
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nuse std::ptr;\nuse std::mem;\n\nlet mut v = Vec::with_capacity_in(3, System);\nv.push(1);\nv.push(2);\nv.push(3);\n\n// Prevent running `v`'s destructor so we are in complete control\n// of the allocation.\nlet mut v = mem::ManuallyDrop::new(v);\n\n// Pull out the various important pieces of information about `v`\nlet p = v.as_mut_ptr();\nlet len = v.len();\nlet cap = v.capacity();\nlet alloc = v.allocator();\n\nunsafe {\n    // Overwrite memory with 4, 5, 6\n    for i in 0..len {\n        ptr::write(p.add(i), 4 + i);\n    }\n\n    // Put everything back together into a Vec\n    let rebuilt = Vec::from_raw_parts_in(p, len, cap, alloc.clone());\n    assert_eq!(rebuilt, [4, 5, 6]);\n}
\n

Using memory that was allocated elsewhere:

\n\n
#![feature(allocator_api)]\n\nuse std::alloc::{AllocError, Allocator, Global, Layout};\n\nfn main() {\n    let layout = Layout::array::<u32>(16).expect(\"overflow cannot happen\");\n\n    let vec = unsafe {\n        let mem = match Global.allocate(layout) {\n            Ok(mem) => mem.cast::<u32>().as_ptr(),\n            Err(AllocError) => return,\n        };\n\n        mem.write(1_000_000);\n\n        Vec::from_raw_parts_in(mem, 1, 16, Global)\n    };\n\n    assert_eq!(vec, &[1_000_000]);\n    assert_eq!(vec.capacity(), 16);\n}
\n
source

pub fn into_raw_parts(self) -> (*mut T, usize, usize)

🔬This is a nightly-only experimental API. (vec_into_raw_parts)

Decomposes a Vec<T> into its raw components: (pointer, length, capacity).

\n

Returns the raw pointer to the underlying data, the length of\nthe vector (in elements), and the allocated capacity of the\ndata (in elements). These are the same arguments in the same\norder as the arguments to from_raw_parts.

\n

After calling this function, the caller is responsible for the\nmemory previously managed by the Vec. The only way to do\nthis is to convert the raw pointer, length, and capacity back\ninto a Vec with the from_raw_parts function, allowing\nthe destructor to perform the cleanup.

\n
§Examples
\n
#![feature(vec_into_raw_parts)]\nlet v: Vec<i32> = vec![-1, 0, 1];\n\nlet (ptr, len, cap) = v.into_raw_parts();\n\nlet rebuilt = unsafe {\n    // We can now make changes to the components, such as\n    // transmuting the raw pointer to a compatible type.\n    let ptr = ptr as *mut u32;\n\n    Vec::from_raw_parts(ptr, len, cap)\n};\nassert_eq!(rebuilt, [4294967295, 0, 1]);
\n
source

pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A)

🔬This is a nightly-only experimental API. (allocator_api)

Decomposes a Vec<T> into its raw components: (pointer, length, capacity, allocator).

\n

Returns the raw pointer to the underlying data, the length of the vector (in elements),\nthe allocated capacity of the data (in elements), and the allocator. These are the same\narguments in the same order as the arguments to from_raw_parts_in.

\n

After calling this function, the caller is responsible for the\nmemory previously managed by the Vec. The only way to do\nthis is to convert the raw pointer, length, and capacity back\ninto a Vec with the from_raw_parts_in function, allowing\nthe destructor to perform the cleanup.

\n
§Examples
\n
#![feature(allocator_api, vec_into_raw_parts)]\n\nuse std::alloc::System;\n\nlet mut v: Vec<i32, System> = Vec::new_in(System);\nv.push(-1);\nv.push(0);\nv.push(1);\n\nlet (ptr, len, cap, alloc) = v.into_raw_parts_with_alloc();\n\nlet rebuilt = unsafe {\n    // We can now make changes to the components, such as\n    // transmuting the raw pointer to a compatible type.\n    let ptr = ptr as *mut u32;\n\n    Vec::from_raw_parts_in(ptr, len, cap, alloc)\n};\nassert_eq!(rebuilt, [4294967295, 0, 1]);
\n
1.0.0 · source

pub fn capacity(&self) -> usize

Returns the total number of elements the vector can hold without\nreallocating.

\n
§Examples
\n
let mut vec: Vec<i32> = Vec::with_capacity(10);\nvec.push(42);\nassert!(vec.capacity() >= 10);
\n
1.0.0 · source

pub fn reserve(&mut self, additional: usize)

Reserves capacity for at least additional more elements to be inserted\nin the given Vec<T>. The collection may reserve more space to\nspeculatively avoid frequent reallocations. After calling reserve,\ncapacity will be greater than or equal to self.len() + additional.\nDoes nothing if capacity is already sufficient.

\n
§Panics
\n

Panics if the new capacity exceeds isize::MAX bytes.

\n
§Examples
\n
let mut vec = vec![1];\nvec.reserve(10);\nassert!(vec.capacity() >= 11);
\n
1.0.0 · source

pub fn reserve_exact(&mut self, additional: usize)

Reserves the minimum capacity for at least additional more elements to\nbe inserted in the given Vec<T>. Unlike reserve, this will not\ndeliberately over-allocate to speculatively avoid frequent allocations.\nAfter calling reserve_exact, capacity will be greater than or equal to\nself.len() + additional. Does nothing if the capacity is already\nsufficient.

\n

Note that the allocator may give the collection more space than it\nrequests. Therefore, capacity can not be relied upon to be precisely\nminimal. Prefer reserve if future insertions are expected.

\n
§Panics
\n

Panics if the new capacity exceeds isize::MAX bytes.

\n
§Examples
\n
let mut vec = vec![1];\nvec.reserve_exact(10);\nassert!(vec.capacity() >= 11);
\n
1.57.0 · source

pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>

Tries to reserve capacity for at least additional more elements to be inserted\nin the given Vec<T>. The collection may reserve more space to speculatively avoid\nfrequent reallocations. After calling try_reserve, capacity will be\ngreater than or equal to self.len() + additional if it returns\nOk(()). Does nothing if capacity is already sufficient. This method\npreserves the contents even if an error occurs.

\n
§Errors
\n

If the capacity overflows, or the allocator reports a failure, then an error\nis returned.

\n
§Examples
\n
use std::collections::TryReserveError;\n\nfn process_data(data: &[u32]) -> Result<Vec<u32>, TryReserveError> {\n    let mut output = Vec::new();\n\n    // Pre-reserve the memory, exiting if we can't\n    output.try_reserve(data.len())?;\n\n    // Now we know this can't OOM in the middle of our complex work\n    output.extend(data.iter().map(|&val| {\n        val * 2 + 5 // very complicated\n    }));\n\n    Ok(output)\n}
\n
1.57.0 · source

pub fn try_reserve_exact(\n &mut self,\n additional: usize,\n) -> Result<(), TryReserveError>

Tries to reserve the minimum capacity for at least additional\nelements to be inserted in the given Vec<T>. Unlike try_reserve,\nthis will not deliberately over-allocate to speculatively avoid frequent\nallocations. After calling try_reserve_exact, capacity will be greater\nthan or equal to self.len() + additional if it returns Ok(()).\nDoes nothing if the capacity is already sufficient.

\n

Note that the allocator may give the collection more space than it\nrequests. Therefore, capacity can not be relied upon to be precisely\nminimal. Prefer try_reserve if future insertions are expected.

\n
§Errors
\n

If the capacity overflows, or the allocator reports a failure, then an error\nis returned.

\n
§Examples
\n
use std::collections::TryReserveError;\n\nfn process_data(data: &[u32]) -> Result<Vec<u32>, TryReserveError> {\n    let mut output = Vec::new();\n\n    // Pre-reserve the memory, exiting if we can't\n    output.try_reserve_exact(data.len())?;\n\n    // Now we know this can't OOM in the middle of our complex work\n    output.extend(data.iter().map(|&val| {\n        val * 2 + 5 // very complicated\n    }));\n\n    Ok(output)\n}
\n
1.0.0 · source

pub fn shrink_to_fit(&mut self)

Shrinks the capacity of the vector as much as possible.

\n

The behavior of this method depends on the allocator, which may either shrink the vector\nin-place or reallocate. The resulting vector might still have some excess capacity, just as\nis the case for with_capacity. See Allocator::shrink for more details.

\n
§Examples
\n
let mut vec = Vec::with_capacity(10);\nvec.extend([1, 2, 3]);\nassert!(vec.capacity() >= 10);\nvec.shrink_to_fit();\nassert!(vec.capacity() >= 3);
\n
1.56.0 · source

pub fn shrink_to(&mut self, min_capacity: usize)

Shrinks the capacity of the vector with a lower bound.

\n

The capacity will remain at least as large as both the length\nand the supplied value.

\n

If the current capacity is less than the lower limit, this is a no-op.

\n
§Examples
\n
let mut vec = Vec::with_capacity(10);\nvec.extend([1, 2, 3]);\nassert!(vec.capacity() >= 10);\nvec.shrink_to(4);\nassert!(vec.capacity() >= 4);\nvec.shrink_to(0);\nassert!(vec.capacity() >= 3);
\n
1.0.0 · source

pub fn into_boxed_slice(self) -> Box<[T], A>

Converts the vector into Box<[T]>.

\n

Before doing the conversion, this method discards excess capacity like shrink_to_fit.

\n
§Examples
\n
let v = vec![1, 2, 3];\n\nlet slice = v.into_boxed_slice();
\n

Any excess capacity is removed:

\n\n
let mut vec = Vec::with_capacity(10);\nvec.extend([1, 2, 3]);\n\nassert!(vec.capacity() >= 10);\nlet slice = vec.into_boxed_slice();\nassert_eq!(slice.into_vec().capacity(), 3);
\n
1.0.0 · source

pub fn truncate(&mut self, len: usize)

Shortens the vector, keeping the first len elements and dropping\nthe rest.

\n

If len is greater or equal to the vector’s current length, this has\nno effect.

\n

The drain method can emulate truncate, but causes the excess\nelements to be returned instead of dropped.

\n

Note that this method has no effect on the allocated capacity\nof the vector.

\n
§Examples
\n

Truncating a five element vector to two elements:

\n\n
let mut vec = vec![1, 2, 3, 4, 5];\nvec.truncate(2);\nassert_eq!(vec, [1, 2]);
\n

No truncation occurs when len is greater than the vector’s current\nlength:

\n\n
let mut vec = vec![1, 2, 3];\nvec.truncate(8);\nassert_eq!(vec, [1, 2, 3]);
\n

Truncating when len == 0 is equivalent to calling the clear\nmethod.

\n\n
let mut vec = vec![1, 2, 3];\nvec.truncate(0);\nassert_eq!(vec, []);
\n
1.7.0 · source

pub fn as_slice(&self) -> &[T]

Extracts a slice containing the entire vector.

\n

Equivalent to &s[..].

\n
§Examples
\n
use std::io::{self, Write};\nlet buffer = vec![1, 2, 3, 5, 8];\nio::sink().write(buffer.as_slice()).unwrap();
\n
1.7.0 · source

pub fn as_mut_slice(&mut self) -> &mut [T]

Extracts a mutable slice of the entire vector.

\n

Equivalent to &mut s[..].

\n
§Examples
\n
use std::io::{self, Read};\nlet mut buffer = vec![0; 3];\nio::repeat(0b101).read_exact(buffer.as_mut_slice()).unwrap();
\n
1.37.0 · source

pub fn as_ptr(&self) -> *const T

Returns a raw pointer to the vector’s buffer, or a dangling raw pointer\nvalid for zero sized reads if the vector didn’t allocate.

\n

The caller must ensure that the vector outlives the pointer this\nfunction returns, or else it will end up dangling.\nModifying the vector may cause its buffer to be reallocated,\nwhich would also make any pointers to it invalid.

\n

The caller must also ensure that the memory the pointer (non-transitively) points to\nis never written to (except inside an UnsafeCell) using this pointer or any pointer\nderived from it. If you need to mutate the contents of the slice, use as_mut_ptr.

\n

This method guarantees that for the purpose of the aliasing model, this method\ndoes not materialize a reference to the underlying slice, and thus the returned pointer\nwill remain valid when mixed with other calls to as_ptr and as_mut_ptr.\nNote that calling other methods that materialize mutable references to the slice,\nor mutable references to specific elements you are planning on accessing through this pointer,\nas well as writing to those elements, may still invalidate this pointer.\nSee the second example below for how this guarantee can be used.

\n
§Examples
\n
let x = vec![1, 2, 4];\nlet x_ptr = x.as_ptr();\n\nunsafe {\n    for i in 0..x.len() {\n        assert_eq!(*x_ptr.add(i), 1 << i);\n    }\n}
\n

Due to the aliasing guarantee, the following code is legal:

\n\n
unsafe {\n    let mut v = vec![0, 1, 2];\n    let ptr1 = v.as_ptr();\n    let _ = ptr1.read();\n    let ptr2 = v.as_mut_ptr().offset(2);\n    ptr2.write(2);\n    // Notably, the write to `ptr2` did *not* invalidate `ptr1`\n    // because it mutated a different element:\n    let _ = ptr1.read();\n}
\n
1.37.0 · source

pub fn as_mut_ptr(&mut self) -> *mut T

Returns an unsafe mutable pointer to the vector’s buffer, or a dangling\nraw pointer valid for zero sized reads if the vector didn’t allocate.

\n

The caller must ensure that the vector outlives the pointer this\nfunction returns, or else it will end up dangling.\nModifying the vector may cause its buffer to be reallocated,\nwhich would also make any pointers to it invalid.

\n

This method guarantees that for the purpose of the aliasing model, this method\ndoes not materialize a reference to the underlying slice, and thus the returned pointer\nwill remain valid when mixed with other calls to as_ptr and as_mut_ptr.\nNote that calling other methods that materialize references to the slice,\nor references to specific elements you are planning on accessing through this pointer,\nmay still invalidate this pointer.\nSee the second example below for how this guarantee can be used.

\n
§Examples
\n
// Allocate vector big enough for 4 elements.\nlet size = 4;\nlet mut x: Vec<i32> = Vec::with_capacity(size);\nlet x_ptr = x.as_mut_ptr();\n\n// Initialize elements via raw pointer writes, then set length.\nunsafe {\n    for i in 0..size {\n        *x_ptr.add(i) = i as i32;\n    }\n    x.set_len(size);\n}\nassert_eq!(&*x, &[0, 1, 2, 3]);
\n

Due to the aliasing guarantee, the following code is legal:

\n\n
unsafe {\n    let mut v = vec![0];\n    let ptr1 = v.as_mut_ptr();\n    ptr1.write(1);\n    let ptr2 = v.as_mut_ptr();\n    ptr2.write(2);\n    // Notably, the write to `ptr2` did *not* invalidate `ptr1`:\n    ptr1.write(3);\n}
\n
source

pub fn allocator(&self) -> &A

🔬This is a nightly-only experimental API. (allocator_api)

Returns a reference to the underlying allocator.

\n
1.0.0 · source

pub unsafe fn set_len(&mut self, new_len: usize)

Forces the length of the vector to new_len.

\n

This is a low-level operation that maintains none of the normal\ninvariants of the type. Normally changing the length of a vector\nis done using one of the safe operations instead, such as\ntruncate, resize, extend, or clear.

\n
§Safety
\n
    \n
  • new_len must be less than or equal to capacity().
  • \n
  • The elements at old_len..new_len must be initialized.
  • \n
\n
§Examples
\n

This method can be useful for situations in which the vector\nis serving as a buffer for other code, particularly over FFI:

\n\n
pub fn get_dictionary(&self) -> Option<Vec<u8>> {\n    // Per the FFI method's docs, \"32768 bytes is always enough\".\n    let mut dict = Vec::with_capacity(32_768);\n    let mut dict_length = 0;\n    // SAFETY: When `deflateGetDictionary` returns `Z_OK`, it holds that:\n    // 1. `dict_length` elements were initialized.\n    // 2. `dict_length` <= the capacity (32_768)\n    // which makes `set_len` safe to call.\n    unsafe {\n        // Make the FFI call...\n        let r = deflateGetDictionary(self.strm, dict.as_mut_ptr(), &mut dict_length);\n        if r == Z_OK {\n            // ...and update the length to what was initialized.\n            dict.set_len(dict_length);\n            Some(dict)\n        } else {\n            None\n        }\n    }\n}
\n

While the following example is sound, there is a memory leak since\nthe inner vectors were not freed prior to the set_len call:

\n\n
let mut vec = vec![vec![1, 0, 0],\n                   vec![0, 1, 0],\n                   vec![0, 0, 1]];\n// SAFETY:\n// 1. `old_len..0` is empty so no elements need to be initialized.\n// 2. `0 <= capacity` always holds whatever `capacity` is.\nunsafe {\n    vec.set_len(0);\n}
\n

Normally, here, one would use clear instead to correctly drop\nthe contents and thus not leak memory.

\n
1.0.0 · source

pub fn swap_remove(&mut self, index: usize) -> T

Removes an element from the vector and returns it.

\n

The removed element is replaced by the last element of the vector.

\n

This does not preserve ordering of the remaining elements, but is O(1).\nIf you need to preserve the element order, use remove instead.

\n
§Panics
\n

Panics if index is out of bounds.

\n
§Examples
\n
let mut v = vec![\"foo\", \"bar\", \"baz\", \"qux\"];\n\nassert_eq!(v.swap_remove(1), \"bar\");\nassert_eq!(v, [\"foo\", \"qux\", \"baz\"]);\n\nassert_eq!(v.swap_remove(0), \"foo\");\nassert_eq!(v, [\"baz\", \"qux\"]);
\n
1.0.0 · source

pub fn insert(&mut self, index: usize, element: T)

Inserts an element at position index within the vector, shifting all\nelements after it to the right.

\n
§Panics
\n

Panics if index > len.

\n
§Examples
\n
let mut vec = vec![1, 2, 3];\nvec.insert(1, 4);\nassert_eq!(vec, [1, 4, 2, 3]);\nvec.insert(4, 5);\nassert_eq!(vec, [1, 4, 2, 3, 5]);
\n
§Time complexity
\n

Takes O(Vec::len) time. All items after the insertion index must be\nshifted to the right. In the worst case, all elements are shifted when\nthe insertion index is 0.

\n
1.0.0 · source

pub fn remove(&mut self, index: usize) -> T

Removes and returns the element at position index within the vector,\nshifting all elements after it to the left.

\n

Note: Because this shifts over the remaining elements, it has a\nworst-case performance of O(n). If you don’t need the order of elements\nto be preserved, use swap_remove instead. If you’d like to remove\nelements from the beginning of the Vec, consider using\nVecDeque::pop_front instead.

\n
§Panics
\n

Panics if index is out of bounds.

\n
§Examples
\n
let mut v = vec![1, 2, 3];\nassert_eq!(v.remove(1), 2);\nassert_eq!(v, [1, 3]);
\n
1.0.0 · source

pub fn retain<F>(&mut self, f: F)
where\n F: FnMut(&T) -> bool,

Retains only the elements specified by the predicate.

\n

In other words, remove all elements e for which f(&e) returns false.\nThis method operates in place, visiting each element exactly once in the\noriginal order, and preserves the order of the retained elements.

\n
§Examples
\n
let mut vec = vec![1, 2, 3, 4];\nvec.retain(|&x| x % 2 == 0);\nassert_eq!(vec, [2, 4]);
\n

Because the elements are visited exactly once in the original order,\nexternal state may be used to decide which elements to keep.

\n\n
let mut vec = vec![1, 2, 3, 4, 5];\nlet keep = [false, true, true, false, true];\nlet mut iter = keep.iter();\nvec.retain(|_| *iter.next().unwrap());\nassert_eq!(vec, [2, 3, 5]);
\n
1.61.0 · source

pub fn retain_mut<F>(&mut self, f: F)
where\n F: FnMut(&mut T) -> bool,

Retains only the elements specified by the predicate, passing a mutable reference to it.

\n

In other words, remove all elements e such that f(&mut e) returns false.\nThis method operates in place, visiting each element exactly once in the\noriginal order, and preserves the order of the retained elements.

\n
§Examples
\n
let mut vec = vec![1, 2, 3, 4];\nvec.retain_mut(|x| if *x <= 3 {\n    *x += 1;\n    true\n} else {\n    false\n});\nassert_eq!(vec, [2, 3, 4]);
\n
1.16.0 · source

pub fn dedup_by_key<F, K>(&mut self, key: F)
where\n F: FnMut(&mut T) -> K,\n K: PartialEq,

Removes all but the first of consecutive elements in the vector that resolve to the same\nkey.

\n

If the vector is sorted, this removes all duplicates.

\n
§Examples
\n
let mut vec = vec![10, 20, 21, 30, 20];\n\nvec.dedup_by_key(|i| *i / 10);\n\nassert_eq!(vec, [10, 20, 30, 20]);
\n
1.16.0 · source

pub fn dedup_by<F>(&mut self, same_bucket: F)
where\n F: FnMut(&mut T, &mut T) -> bool,

Removes all but the first of consecutive elements in the vector satisfying a given equality\nrelation.

\n

The same_bucket function is passed references to two elements from the vector and\nmust determine if the elements compare equal. The elements are passed in opposite order\nfrom their order in the slice, so if same_bucket(a, b) returns true, a is removed.

\n

If the vector is sorted, this removes all duplicates.

\n
§Examples
\n
let mut vec = vec![\"foo\", \"bar\", \"Bar\", \"baz\", \"bar\"];\n\nvec.dedup_by(|a, b| a.eq_ignore_ascii_case(b));\n\nassert_eq!(vec, [\"foo\", \"bar\", \"baz\", \"bar\"]);
\n
1.0.0 · source

pub fn push(&mut self, value: T)

Appends an element to the back of a collection.

\n
§Panics
\n

Panics if the new capacity exceeds isize::MAX bytes.

\n
§Examples
\n
let mut vec = vec![1, 2];\nvec.push(3);\nassert_eq!(vec, [1, 2, 3]);
\n
§Time complexity
\n

Takes amortized O(1) time. If the vector’s length would exceed its\ncapacity after the push, O(capacity) time is taken to copy the\nvector’s elements to a larger allocation. This expensive operation is\noffset by the capacity O(1) insertions it allows.

\n
source

pub fn push_within_capacity(&mut self, value: T) -> Result<(), T>

🔬This is a nightly-only experimental API. (vec_push_within_capacity)

Appends an element if there is sufficient spare capacity, otherwise an error is returned\nwith the element.

\n

Unlike push this method will not reallocate when there’s insufficient capacity.\nThe caller should use reserve or try_reserve to ensure that there is enough capacity.

\n
§Examples
\n

A manual, panic-free alternative to FromIterator:

\n\n
#![feature(vec_push_within_capacity)]\n\nuse std::collections::TryReserveError;\nfn from_iter_fallible<T>(iter: impl Iterator<Item=T>) -> Result<Vec<T>, TryReserveError> {\n    let mut vec = Vec::new();\n    for value in iter {\n        if let Err(value) = vec.push_within_capacity(value) {\n            vec.try_reserve(1)?;\n            // this cannot fail, the previous line either returned or added at least 1 free slot\n            let _ = vec.push_within_capacity(value);\n        }\n    }\n    Ok(vec)\n}\nassert_eq!(from_iter_fallible(0..100), Ok(Vec::from_iter(0..100)));
\n
§Time complexity
\n

Takes O(1) time.

\n
1.0.0 · source

pub fn pop(&mut self) -> Option<T>

Removes the last element from a vector and returns it, or None if it\nis empty.

\n

If you’d like to pop the first element, consider using\nVecDeque::pop_front instead.

\n
§Examples
\n
let mut vec = vec![1, 2, 3];\nassert_eq!(vec.pop(), Some(3));\nassert_eq!(vec, [1, 2]);
\n
§Time complexity
\n

Takes O(1) time.

\n
source

pub fn pop_if<F>(&mut self, f: F) -> Option<T>
where\n F: FnOnce(&mut T) -> bool,

🔬This is a nightly-only experimental API. (vec_pop_if)

Removes and returns the last element in a vector if the predicate\nreturns true, or None if the predicate returns false or the vector\nis empty.

\n
§Examples
\n
#![feature(vec_pop_if)]\n\nlet mut vec = vec![1, 2, 3, 4];\nlet pred = |x: &mut i32| *x % 2 == 0;\n\nassert_eq!(vec.pop_if(pred), Some(4));\nassert_eq!(vec, [1, 2, 3]);\nassert_eq!(vec.pop_if(pred), None);
\n
1.4.0 · source

pub fn append(&mut self, other: &mut Vec<T, A>)

Moves all the elements of other into self, leaving other empty.

\n
§Panics
\n

Panics if the new capacity exceeds isize::MAX bytes.

\n
§Examples
\n
let mut vec = vec![1, 2, 3];\nlet mut vec2 = vec![4, 5, 6];\nvec.append(&mut vec2);\nassert_eq!(vec, [1, 2, 3, 4, 5, 6]);\nassert_eq!(vec2, []);
\n
1.6.0 · source

pub fn drain<R>(&mut self, range: R) -> Drain<'_, T, A>
where\n R: RangeBounds<usize>,

Removes the specified range from the vector in bulk, returning all\nremoved elements as an iterator. If the iterator is dropped before\nbeing fully consumed, it drops the remaining removed elements.

\n

The returned iterator keeps a mutable borrow on the vector to optimize\nits implementation.

\n
§Panics
\n

Panics if the starting point is greater than the end point or if\nthe end point is greater than the length of the vector.

\n
§Leaking
\n

If the returned iterator goes out of scope without being dropped (due to\nmem::forget, for example), the vector may have lost and leaked\nelements arbitrarily, including elements outside the range.

\n
§Examples
\n
let mut v = vec![1, 2, 3];\nlet u: Vec<_> = v.drain(1..).collect();\nassert_eq!(v, &[1]);\nassert_eq!(u, &[2, 3]);\n\n// A full range clears the vector, like `clear()` does\nv.drain(..);\nassert_eq!(v, &[]);
\n
1.0.0 · source

pub fn clear(&mut self)

Clears the vector, removing all values.

\n

Note that this method has no effect on the allocated capacity\nof the vector.

\n
§Examples
\n
let mut v = vec![1, 2, 3];\n\nv.clear();\n\nassert!(v.is_empty());
\n
1.0.0 · source

pub fn len(&self) -> usize

Returns the number of elements in the vector, also referred to\nas its ‘length’.

\n
§Examples
\n
let a = vec![1, 2, 3];\nassert_eq!(a.len(), 3);
\n
1.0.0 · source

pub fn is_empty(&self) -> bool

Returns true if the vector contains no elements.

\n
§Examples
\n
let mut v = Vec::new();\nassert!(v.is_empty());\n\nv.push(1);\nassert!(!v.is_empty());
\n
1.4.0 · source

pub fn split_off(&mut self, at: usize) -> Vec<T, A>
where\n A: Clone,

Splits the collection into two at the given index.

\n

Returns a newly allocated vector containing the elements in the range\n[at, len). After the call, the original vector will be left containing\nthe elements [0, at) with its previous capacity unchanged.

\n
    \n
  • If you want to take ownership of the entire contents and capacity of\nthe vector, see mem::take or mem::replace.
  • \n
  • If you don’t need the returned vector at all, see Vec::truncate.
  • \n
  • If you want to take ownership of an arbitrary subslice, or you don’t\nnecessarily want to store the removed items in a vector, see Vec::drain.
  • \n
\n
§Panics
\n

Panics if at > len.

\n
§Examples
\n
let mut vec = vec![1, 2, 3];\nlet vec2 = vec.split_off(1);\nassert_eq!(vec, [1]);\nassert_eq!(vec2, [2, 3]);
\n
1.33.0 · source

pub fn resize_with<F>(&mut self, new_len: usize, f: F)
where\n F: FnMut() -> T,

Resizes the Vec in-place so that len is equal to new_len.

\n

If new_len is greater than len, the Vec is extended by the\ndifference, with each additional slot filled with the result of\ncalling the closure f. The return values from f will end up\nin the Vec in the order they have been generated.

\n

If new_len is less than len, the Vec is simply truncated.

\n

This method uses a closure to create new values on every push. If\nyou’d rather Clone a given value, use Vec::resize. If you\nwant to use the Default trait to generate values, you can\npass Default::default as the second argument.

\n
§Examples
\n
let mut vec = vec![1, 2, 3];\nvec.resize_with(5, Default::default);\nassert_eq!(vec, [1, 2, 3, 0, 0]);\n\nlet mut vec = vec![];\nlet mut p = 1;\nvec.resize_with(4, || { p *= 2; p });\nassert_eq!(vec, [2, 4, 8, 16]);
\n
1.47.0 · source

pub fn leak<'a>(self) -> &'a mut [T]
where\n A: 'a,

Consumes and leaks the Vec, returning a mutable reference to the contents,\n&'a mut [T].

\n

Note that the type T must outlive the chosen lifetime 'a. If the type\nhas only static references, or none at all, then this may be chosen to be\n'static.

\n

As of Rust 1.57, this method does not reallocate or shrink the Vec,\nso the leaked allocation may include unused capacity that is not part\nof the returned slice.

\n

This function is mainly useful for data that lives for the remainder of\nthe program’s life. Dropping the returned reference will cause a memory\nleak.

\n
§Examples
\n

Simple usage:

\n\n
let x = vec![1, 2, 3];\nlet static_ref: &'static mut [usize] = x.leak();\nstatic_ref[0] += 1;\nassert_eq!(static_ref, &[2, 2, 3]);
\n
1.60.0 · source

pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<T>]

Returns the remaining spare capacity of the vector as a slice of\nMaybeUninit<T>.

\n

The returned slice can be used to fill the vector with data (e.g. by\nreading from a file) before marking the data as initialized using the\nset_len method.

\n
§Examples
\n
// Allocate vector big enough for 10 elements.\nlet mut v = Vec::with_capacity(10);\n\n// Fill in the first 3 elements.\nlet uninit = v.spare_capacity_mut();\nuninit[0].write(0);\nuninit[1].write(1);\nuninit[2].write(2);\n\n// Mark the first 3 elements of the vector as being initialized.\nunsafe {\n    v.set_len(3);\n}\n\nassert_eq!(&v, &[0, 1, 2]);
\n
source

pub fn split_at_spare_mut(&mut self) -> (&mut [T], &mut [MaybeUninit<T>])

🔬This is a nightly-only experimental API. (vec_split_at_spare)

Returns vector content as a slice of T, along with the remaining spare\ncapacity of the vector as a slice of MaybeUninit<T>.

\n

The returned spare capacity slice can be used to fill the vector with data\n(e.g. by reading from a file) before marking the data as initialized using\nthe set_len method.

\n

Note that this is a low-level API, which should be used with care for\noptimization purposes. If you need to append data to a Vec\nyou can use push, extend, extend_from_slice,\nextend_from_within, insert, append, resize or\nresize_with, depending on your exact needs.

\n
§Examples
\n
#![feature(vec_split_at_spare)]\n\nlet mut v = vec![1, 1, 2];\n\n// Reserve additional space big enough for 10 elements.\nv.reserve(10);\n\nlet (init, uninit) = v.split_at_spare_mut();\nlet sum = init.iter().copied().sum::<u32>();\n\n// Fill in the next 4 elements.\nuninit[0].write(sum);\nuninit[1].write(sum * 2);\nuninit[2].write(sum * 3);\nuninit[3].write(sum * 4);\n\n// Mark the 4 elements of the vector as being initialized.\nunsafe {\n    let len = v.len();\n    v.set_len(len + 4);\n}\n\nassert_eq!(&v, &[1, 1, 2, 4, 8, 12, 16]);
\n
",0,"fred::types::timeseries::Resp2TimeSeriesValues"],["
§

impl<Z> Zeroize for Vec<Z>
where\n Z: Zeroize,

§

fn zeroize(&mut self)

“Best effort” zeroization for Vec.

\n

Ensures the entire capacity of the Vec is zeroed. Cannot ensure that\nprevious reallocations did not leave values on the heap.

\n
","Zeroize","fred::types::timeseries::Resp2TimeSeriesValues"],["
source§

impl<T, A> DerefPure for Vec<T, A>
where\n A: Allocator,

","DerefPure","fred::types::timeseries::Resp2TimeSeriesValues"],["
1.0.0 · source§

impl<T, A> Eq for Vec<T, A>
where\n T: Eq,\n A: Allocator,

","Eq","fred::types::timeseries::Resp2TimeSeriesValues"],["
§

impl<Z> ZeroizeOnDrop for Vec<Z>
where\n Z: ZeroizeOnDrop,

","ZeroizeOnDrop","fred::types::timeseries::Resp2TimeSeriesValues"]]]]); + if (window.register_type_impls) { + window.register_type_impls(type_impls); + } else { + window.pending_type_impls = type_impls; + } +})() +//{"start":55,"fragment_lengths":[275364]} \ No newline at end of file diff --git a/doc/type.impl/core/option/enum.Option.js b/doc/type.impl/core/option/enum.Option.js new file mode 100644 index 00000000..12c054e0 --- /dev/null +++ b/doc/type.impl/core/option/enum.Option.js @@ -0,0 +1,9 @@ +(function() { + var type_impls = Object.fromEntries([["fred",[["
1.0.0 · source§

impl<T> Clone for Option<T>
where\n T: Clone,

source§

fn clone(&self) -> Option<T>

Returns a copy of the value. Read more
source§

fn clone_from(&mut self, source: &Option<T>)

Performs copy-assignment from source. Read more
","Clone","fred::types::LimitCount"],["
1.0.0 · source§

impl<T> Debug for Option<T>
where\n T: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
","Debug","fred::types::LimitCount"],["
1.0.0 · source§

impl<T> Default for Option<T>

source§

fn default() -> Option<T>

Returns None.

\n
§Examples
\n
let opt: Option<u32> = Option::default();\nassert!(opt.is_none());
\n
","Default","fred::types::LimitCount"],["
source§

impl<'de, T> Deserialize<'de> for Option<T>
where\n T: Deserialize<'de>,

source§

fn deserialize<D>(\n deserializer: D,\n) -> Result<Option<T>, <D as Deserializer<'de>>::Error>
where\n D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
","Deserialize<'de>","fred::types::LimitCount"],["
source§

impl<T> From<CtOption<T>> for Option<T>

source§

fn from(source: CtOption<T>) -> Option<T>

Convert the CtOption<T> wrapper into an Option<T>, depending on whether\nthe underlying is_some Choice was a 0 or a 1 once unwrapped.

\n
§Note
\n

This function exists to avoid ending up with ugly, verbose and/or bad handled\nconversions from the CtOption<T> wraps to an Option<T> or Result<T, E>.\nThis implementation doesn’t intend to be constant-time nor try to protect the\nleakage of the T since the Option<T> will do it anyways.

\n
","From>","fred::types::LimitCount"],["
1.12.0 · source§

impl<T> From<T> for Option<T>

source§

fn from(val: T) -> Option<T>

Moves val into a new Some.

\n
§Examples
\n
let o: Option<u8> = Option::from(67);\n\nassert_eq!(Some(67), o);
\n
","From","fred::types::LimitCount"],["
1.0.0 · source§

impl<A, V> FromIterator<Option<A>> for Option<V>
where\n V: FromIterator<A>,

source§

fn from_iter<I>(iter: I) -> Option<V>
where\n I: IntoIterator<Item = Option<A>>,

Takes each element in the Iterator: if it is None,\nno further elements are taken, and the None is\nreturned. Should no None occur, a container of type\nV containing the values of each Option is returned.

\n
§Examples
\n

Here is an example which increments every integer in a vector.\nWe use the checked variant of add that returns None when the\ncalculation would result in an overflow.

\n\n
let items = vec![0_u16, 1, 2];\n\nlet res: Option<Vec<u16>> = items\n    .iter()\n    .map(|x| x.checked_add(1))\n    .collect();\n\nassert_eq!(res, Some(vec![1, 2, 3]));
\n

As you can see, this will return the expected, valid items.

\n

Here is another example that tries to subtract one from another list\nof integers, this time checking for underflow:

\n\n
let items = vec![2_u16, 1, 0];\n\nlet res: Option<Vec<u16>> = items\n    .iter()\n    .map(|x| x.checked_sub(1))\n    .collect();\n\nassert_eq!(res, None);
\n

Since the last element is zero, it would underflow. Thus, the resulting\nvalue is None.

\n

Here is a variation on the previous example, showing that no\nfurther elements are taken from iter after the first None.

\n\n
let items = vec![3_u16, 2, 1, 10];\n\nlet mut shared = 0;\n\nlet res: Option<Vec<u16>> = items\n    .iter()\n    .map(|x| { shared += x; x.checked_sub(2) })\n    .collect();\n\nassert_eq!(res, None);\nassert_eq!(shared, 6);
\n

Since the third element caused an underflow, no further elements were taken,\nso the final value of shared is 6 (= 3 + 2 + 1), not 16.

\n
","FromIterator>","fred::types::LimitCount"],["
source§

impl<T> FromRedis for Option<T>
where\n T: FromRedis,

","FromRedis","fred::types::LimitCount"],["
source§

impl<T> FromResidual<Option<Infallible>> for Option<T>

source§

fn from_residual(residual: Option<Infallible>) -> Option<T>

🔬This is a nightly-only experimental API. (try_trait_v2)
Constructs the type from a compatible Residual type. Read more
","FromResidual>","fred::types::LimitCount"],["
source§

impl<T> FromResidual<Yeet<()>> for Option<T>

source§

fn from_residual(_: Yeet<()>) -> Option<T>

🔬This is a nightly-only experimental API. (try_trait_v2)
Constructs the type from a compatible Residual type. Read more
","FromResidual>","fred::types::LimitCount"],["
1.0.0 · source§

impl<T> Hash for Option<T>
where\n T: Hash,

source§

fn hash<__H>(&self, state: &mut __H)
where\n __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where\n H: Hasher,\n Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
","Hash","fred::types::LimitCount"],["
1.0.0 · source§

impl<T> IntoIterator for Option<T>

source§

fn into_iter(self) -> IntoIter<T>

Returns a consuming iterator over the possibly contained value.

\n
§Examples
\n
let x = Some(\"string\");\nlet v: Vec<&str> = x.into_iter().collect();\nassert_eq!(v, [\"string\"]);\n\nlet x = None;\nlet v: Vec<&str> = x.into_iter().collect();\nassert!(v.is_empty());
\n
§

type Item = T

The type of the elements being iterated over.
§

type IntoIter = IntoIter<T>

Which kind of iterator are we turning this into?
","IntoIterator","fred::types::LimitCount"],["
source§

impl<T> Option<T>

1.0.0 (const: 1.48.0) · source

pub const fn is_some(&self) -> bool

Returns true if the option is a Some value.

\n
§Examples
\n
let x: Option<u32> = Some(2);\nassert_eq!(x.is_some(), true);\n\nlet x: Option<u32> = None;\nassert_eq!(x.is_some(), false);
\n
1.70.0 · source

pub fn is_some_and(self, f: impl FnOnce(T) -> bool) -> bool

Returns true if the option is a Some and the value inside of it matches a predicate.

\n
§Examples
\n
let x: Option<u32> = Some(2);\nassert_eq!(x.is_some_and(|x| x > 1), true);\n\nlet x: Option<u32> = Some(0);\nassert_eq!(x.is_some_and(|x| x > 1), false);\n\nlet x: Option<u32> = None;\nassert_eq!(x.is_some_and(|x| x > 1), false);
\n
1.0.0 (const: 1.48.0) · source

pub const fn is_none(&self) -> bool

Returns true if the option is a None value.

\n
§Examples
\n
let x: Option<u32> = Some(2);\nassert_eq!(x.is_none(), false);\n\nlet x: Option<u32> = None;\nassert_eq!(x.is_none(), true);
\n
1.82.0 · source

pub fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool

Returns true if the option is a None or the value inside of it matches a predicate.

\n
§Examples
\n
let x: Option<u32> = Some(2);\nassert_eq!(x.is_none_or(|x| x > 1), true);\n\nlet x: Option<u32> = Some(0);\nassert_eq!(x.is_none_or(|x| x > 1), false);\n\nlet x: Option<u32> = None;\nassert_eq!(x.is_none_or(|x| x > 1), true);
\n
1.0.0 (const: 1.48.0) · source

pub const fn as_ref(&self) -> Option<&T>

Converts from &Option<T> to Option<&T>.

\n
§Examples
\n

Calculates the length of an Option<String> as an Option<usize>\nwithout moving the String. The map method takes the self argument by value,\nconsuming the original, so this technique uses as_ref to first take an Option to a\nreference to the value inside the original.

\n\n
let text: Option<String> = Some(\"Hello, world!\".to_string());\n// First, cast `Option<String>` to `Option<&String>` with `as_ref`,\n// then consume *that* with `map`, leaving `text` on the stack.\nlet text_length: Option<usize> = text.as_ref().map(|s| s.len());\nprintln!(\"still can print text: {text:?}\");
\n
1.0.0 (const: unstable) · source

pub fn as_mut(&mut self) -> Option<&mut T>

Converts from &mut Option<T> to Option<&mut T>.

\n
§Examples
\n
let mut x = Some(2);\nmatch x.as_mut() {\n    Some(v) => *v = 42,\n    None => {},\n}\nassert_eq!(x, Some(42));
\n
1.33.0 (const: unstable) · source

pub fn as_pin_ref(self: Pin<&Option<T>>) -> Option<Pin<&T>>

Converts from Pin<&Option<T>> to Option<Pin<&T>>.

\n
1.33.0 (const: unstable) · source

pub fn as_pin_mut(self: Pin<&mut Option<T>>) -> Option<Pin<&mut T>>

Converts from Pin<&mut Option<T>> to Option<Pin<&mut T>>.

\n
1.75.0 (const: unstable) · source

pub fn as_slice(&self) -> &[T]

Returns a slice of the contained value, if any. If this is None, an\nempty slice is returned. This can be useful to have a single type of\niterator over an Option or slice.

\n

Note: Should you have an Option<&T> and wish to get a slice of T,\nyou can unpack it via opt.map_or(&[], std::slice::from_ref).

\n
§Examples
\n
assert_eq!(\n    [Some(1234).as_slice(), None.as_slice()],\n    [&[1234][..], &[][..]],\n);
\n

The inverse of this function is (discounting\nborrowing) [_]::first:

\n\n
for i in [Some(1234_u16), None] {\n    assert_eq!(i.as_ref(), i.as_slice().first());\n}
\n
1.75.0 (const: unstable) · source

pub fn as_mut_slice(&mut self) -> &mut [T]

Returns a mutable slice of the contained value, if any. If this is\nNone, an empty slice is returned. This can be useful to have a\nsingle type of iterator over an Option or slice.

\n

Note: Should you have an Option<&mut T> instead of a\n&mut Option<T>, which this method takes, you can obtain a mutable\nslice via opt.map_or(&mut [], std::slice::from_mut).

\n
§Examples
\n
assert_eq!(\n    [Some(1234).as_mut_slice(), None.as_mut_slice()],\n    [&mut [1234][..], &mut [][..]],\n);
\n

The result is a mutable slice of zero or one items that points into\nour original Option:

\n\n
let mut x = Some(1234);\nx.as_mut_slice()[0] += 1;\nassert_eq!(x, Some(1235));
\n

The inverse of this method (discounting borrowing)\nis [_]::first_mut:

\n\n
assert_eq!(Some(123).as_mut_slice().first_mut(), Some(&mut 123))
\n
1.0.0 (const: unstable) · source

pub fn expect(self, msg: &str) -> T

Returns the contained Some value, consuming the self value.

\n
§Panics
\n

Panics if the value is a None with a custom panic message provided by\nmsg.

\n
§Examples
\n
let x = Some(\"value\");\nassert_eq!(x.expect(\"fruits are healthy\"), \"value\");
\n\n
let x: Option<&str> = None;\nx.expect(\"fruits are healthy\"); // panics with `fruits are healthy`
\n
§Recommended Message Style
\n

We recommend that expect messages are used to describe the reason you\nexpect the Option should be Some.

\n\n
let item = slice.get(0)\n    .expect(\"slice should not be empty\");
\n

Hint: If you’re having trouble remembering how to phrase expect\nerror messages remember to focus on the word “should” as in “env\nvariable should be set by blah” or “the given binary should be available\nand executable by the current user”.

\n

For more detail on expect message styles and the reasoning behind our\nrecommendation please refer to the section on “Common Message\nStyles” in the std::error module docs.

\n
1.0.0 (const: unstable) · source

pub fn unwrap(self) -> T

Returns the contained Some value, consuming the self value.

\n

Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the None\ncase explicitly, or call unwrap_or, unwrap_or_else, or\nunwrap_or_default.

\n
§Panics
\n

Panics if the self value equals None.

\n
§Examples
\n
let x = Some(\"air\");\nassert_eq!(x.unwrap(), \"air\");
\n\n
let x: Option<&str> = None;\nassert_eq!(x.unwrap(), \"air\"); // fails
\n
1.0.0 · source

pub fn unwrap_or(self, default: T) -> T

Returns the contained Some value or a provided default.

\n

Arguments passed to unwrap_or are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use unwrap_or_else,\nwhich is lazily evaluated.

\n
§Examples
\n
assert_eq!(Some(\"car\").unwrap_or(\"bike\"), \"car\");\nassert_eq!(None.unwrap_or(\"bike\"), \"bike\");
\n
1.0.0 · source

pub fn unwrap_or_else<F>(self, f: F) -> T
where\n F: FnOnce() -> T,

Returns the contained Some value or computes it from a closure.

\n
§Examples
\n
let k = 10;\nassert_eq!(Some(4).unwrap_or_else(|| 2 * k), 4);\nassert_eq!(None.unwrap_or_else(|| 2 * k), 20);
\n
1.0.0 · source

pub fn unwrap_or_default(self) -> T
where\n T: Default,

Returns the contained Some value or a default.

\n

Consumes the self argument then, if Some, returns the contained\nvalue, otherwise if None, returns the default value for that\ntype.

\n
§Examples
\n
let x: Option<u32> = None;\nlet y: Option<u32> = Some(12);\n\nassert_eq!(x.unwrap_or_default(), 0);\nassert_eq!(y.unwrap_or_default(), 12);
\n
1.58.0 (const: unstable) · source

pub unsafe fn unwrap_unchecked(self) -> T

Returns the contained Some value, consuming the self value,\nwithout checking that the value is not None.

\n
§Safety
\n

Calling this method on None is undefined behavior.

\n
§Examples
\n
let x = Some(\"air\");\nassert_eq!(unsafe { x.unwrap_unchecked() }, \"air\");
\n\n
let x: Option<&str> = None;\nassert_eq!(unsafe { x.unwrap_unchecked() }, \"air\"); // Undefined behavior!
\n
1.0.0 · source

pub fn map<U, F>(self, f: F) -> Option<U>
where\n F: FnOnce(T) -> U,

Maps an Option<T> to Option<U> by applying a function to a contained value (if Some) or returns None (if None).

\n
§Examples
\n

Calculates the length of an Option<String> as an\nOption<usize>, consuming the original:

\n\n
let maybe_some_string = Some(String::from(\"Hello, World!\"));\n// `Option::map` takes self *by value*, consuming `maybe_some_string`\nlet maybe_some_len = maybe_some_string.map(|s| s.len());\nassert_eq!(maybe_some_len, Some(13));\n\nlet x: Option<&str> = None;\nassert_eq!(x.map(|s| s.len()), None);
\n
1.76.0 · source

pub fn inspect<F>(self, f: F) -> Option<T>
where\n F: FnOnce(&T),

Calls a function with a reference to the contained value if Some.

\n

Returns the original option.

\n
§Examples
\n
let list = vec![1, 2, 3];\n\n// prints \"got: 2\"\nlet x = list\n    .get(1)\n    .inspect(|x| println!(\"got: {x}\"))\n    .expect(\"list should be long enough\");\n\n// prints nothing\nlist.get(5).inspect(|x| println!(\"got: {x}\"));
\n
1.0.0 · source

pub fn map_or<U, F>(self, default: U, f: F) -> U
where\n F: FnOnce(T) -> U,

Returns the provided default result (if none),\nor applies a function to the contained value (if any).

\n

Arguments passed to map_or are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use map_or_else,\nwhich is lazily evaluated.

\n
§Examples
\n
let x = Some(\"foo\");\nassert_eq!(x.map_or(42, |v| v.len()), 3);\n\nlet x: Option<&str> = None;\nassert_eq!(x.map_or(42, |v| v.len()), 42);
\n
1.0.0 · source

pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U
where\n D: FnOnce() -> U,\n F: FnOnce(T) -> U,

Computes a default function result (if none), or\napplies a different function to the contained value (if any).

\n
§Basic examples
\n
let k = 21;\n\nlet x = Some(\"foo\");\nassert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3);\n\nlet x: Option<&str> = None;\nassert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42);
\n
§Handling a Result-based fallback
\n

A somewhat common occurrence when dealing with optional values\nin combination with Result<T, E> is the case where one wants to invoke\na fallible fallback if the option is not present. This example\nparses a command line argument (if present), or the contents of a file to\nan integer. However, unlike accessing the command line argument, reading\nthe file is fallible, so it must be wrapped with Ok.

\n\n
let v: u64 = std::env::args()\n   .nth(1)\n   .map_or_else(|| std::fs::read_to_string(\"/etc/someconfig.conf\"), Ok)?\n   .parse()?;
\n
1.0.0 · source

pub fn ok_or<E>(self, err: E) -> Result<T, E>

Transforms the Option<T> into a Result<T, E>, mapping Some(v) to\nOk(v) and None to Err(err).

\n

Arguments passed to ok_or are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use ok_or_else, which is\nlazily evaluated.

\n
§Examples
\n
let x = Some(\"foo\");\nassert_eq!(x.ok_or(0), Ok(\"foo\"));\n\nlet x: Option<&str> = None;\nassert_eq!(x.ok_or(0), Err(0));
\n
1.0.0 · source

pub fn ok_or_else<E, F>(self, err: F) -> Result<T, E>
where\n F: FnOnce() -> E,

Transforms the Option<T> into a Result<T, E>, mapping Some(v) to\nOk(v) and None to Err(err()).

\n
§Examples
\n
let x = Some(\"foo\");\nassert_eq!(x.ok_or_else(|| 0), Ok(\"foo\"));\n\nlet x: Option<&str> = None;\nassert_eq!(x.ok_or_else(|| 0), Err(0));
\n
1.40.0 · source

pub fn as_deref(&self) -> Option<&<T as Deref>::Target>
where\n T: Deref,

Converts from Option<T> (or &Option<T>) to Option<&T::Target>.

\n

Leaves the original Option in-place, creating a new one with a reference\nto the original one, additionally coercing the contents via Deref.

\n
§Examples
\n
let x: Option<String> = Some(\"hey\".to_owned());\nassert_eq!(x.as_deref(), Some(\"hey\"));\n\nlet x: Option<String> = None;\nassert_eq!(x.as_deref(), None);
\n
1.40.0 · source

pub fn as_deref_mut(&mut self) -> Option<&mut <T as Deref>::Target>
where\n T: DerefMut,

Converts from Option<T> (or &mut Option<T>) to Option<&mut T::Target>.

\n

Leaves the original Option in-place, creating a new one containing a mutable reference to\nthe inner type’s Deref::Target type.

\n
§Examples
\n
let mut x: Option<String> = Some(\"hey\".to_owned());\nassert_eq!(x.as_deref_mut().map(|x| {\n    x.make_ascii_uppercase();\n    x\n}), Some(\"HEY\".to_owned().as_mut_str()));
\n
1.0.0 (const: unstable) · source

pub fn iter(&self) -> Iter<'_, T>

Returns an iterator over the possibly contained value.

\n
§Examples
\n
let x = Some(4);\nassert_eq!(x.iter().next(), Some(&4));\n\nlet x: Option<u32> = None;\nassert_eq!(x.iter().next(), None);
\n
1.0.0 · source

pub fn iter_mut(&mut self) -> IterMut<'_, T>

Returns a mutable iterator over the possibly contained value.

\n
§Examples
\n
let mut x = Some(4);\nmatch x.iter_mut().next() {\n    Some(v) => *v = 42,\n    None => {},\n}\nassert_eq!(x, Some(42));\n\nlet mut x: Option<u32> = None;\nassert_eq!(x.iter_mut().next(), None);
\n
1.0.0 · source

pub fn and<U>(self, optb: Option<U>) -> Option<U>

Returns None if the option is None, otherwise returns optb.

\n

Arguments passed to and are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use and_then, which is\nlazily evaluated.

\n
§Examples
\n
let x = Some(2);\nlet y: Option<&str> = None;\nassert_eq!(x.and(y), None);\n\nlet x: Option<u32> = None;\nlet y = Some(\"foo\");\nassert_eq!(x.and(y), None);\n\nlet x = Some(2);\nlet y = Some(\"foo\");\nassert_eq!(x.and(y), Some(\"foo\"));\n\nlet x: Option<u32> = None;\nlet y: Option<&str> = None;\nassert_eq!(x.and(y), None);
\n
1.0.0 · source

pub fn and_then<U, F>(self, f: F) -> Option<U>
where\n F: FnOnce(T) -> Option<U>,

Returns None if the option is None, otherwise calls f with the\nwrapped value and returns the result.

\n

Some languages call this operation flatmap.

\n
§Examples
\n
fn sq_then_to_string(x: u32) -> Option<String> {\n    x.checked_mul(x).map(|sq| sq.to_string())\n}\n\nassert_eq!(Some(2).and_then(sq_then_to_string), Some(4.to_string()));\nassert_eq!(Some(1_000_000).and_then(sq_then_to_string), None); // overflowed!\nassert_eq!(None.and_then(sq_then_to_string), None);
\n

Often used to chain fallible operations that may return None.

\n\n
let arr_2d = [[\"A0\", \"A1\"], [\"B0\", \"B1\"]];\n\nlet item_0_1 = arr_2d.get(0).and_then(|row| row.get(1));\nassert_eq!(item_0_1, Some(&\"A1\"));\n\nlet item_2_0 = arr_2d.get(2).and_then(|row| row.get(0));\nassert_eq!(item_2_0, None);
\n
1.27.0 · source

pub fn filter<P>(self, predicate: P) -> Option<T>
where\n P: FnOnce(&T) -> bool,

Returns None if the option is None, otherwise calls predicate\nwith the wrapped value and returns:

\n
    \n
  • Some(t) if predicate returns true (where t is the wrapped\nvalue), and
  • \n
  • None if predicate returns false.
  • \n
\n

This function works similar to Iterator::filter(). You can imagine\nthe Option<T> being an iterator over one or zero elements. filter()\nlets you decide which elements to keep.

\n
§Examples
\n
fn is_even(n: &i32) -> bool {\n    n % 2 == 0\n}\n\nassert_eq!(None.filter(is_even), None);\nassert_eq!(Some(3).filter(is_even), None);\nassert_eq!(Some(4).filter(is_even), Some(4));
\n
1.0.0 · source

pub fn or(self, optb: Option<T>) -> Option<T>

Returns the option if it contains a value, otherwise returns optb.

\n

Arguments passed to or are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use or_else, which is\nlazily evaluated.

\n
§Examples
\n
let x = Some(2);\nlet y = None;\nassert_eq!(x.or(y), Some(2));\n\nlet x = None;\nlet y = Some(100);\nassert_eq!(x.or(y), Some(100));\n\nlet x = Some(2);\nlet y = Some(100);\nassert_eq!(x.or(y), Some(2));\n\nlet x: Option<u32> = None;\nlet y = None;\nassert_eq!(x.or(y), None);
\n
1.0.0 · source

pub fn or_else<F>(self, f: F) -> Option<T>
where\n F: FnOnce() -> Option<T>,

Returns the option if it contains a value, otherwise calls f and\nreturns the result.

\n
§Examples
\n
fn nobody() -> Option<&'static str> { None }\nfn vikings() -> Option<&'static str> { Some(\"vikings\") }\n\nassert_eq!(Some(\"barbarians\").or_else(vikings), Some(\"barbarians\"));\nassert_eq!(None.or_else(vikings), Some(\"vikings\"));\nassert_eq!(None.or_else(nobody), None);
\n
1.37.0 · source

pub fn xor(self, optb: Option<T>) -> Option<T>

Returns Some if exactly one of self, optb is Some, otherwise returns None.

\n
§Examples
\n
let x = Some(2);\nlet y: Option<u32> = None;\nassert_eq!(x.xor(y), Some(2));\n\nlet x: Option<u32> = None;\nlet y = Some(2);\nassert_eq!(x.xor(y), Some(2));\n\nlet x = Some(2);\nlet y = Some(2);\nassert_eq!(x.xor(y), None);\n\nlet x: Option<u32> = None;\nlet y: Option<u32> = None;\nassert_eq!(x.xor(y), None);
\n
1.53.0 · source

pub fn insert(&mut self, value: T) -> &mut T

Inserts value into the option, then returns a mutable reference to it.

\n

If the option already contains a value, the old value is dropped.

\n

See also Option::get_or_insert, which doesn’t update the value if\nthe option already contains Some.

\n
§Example
\n
let mut opt = None;\nlet val = opt.insert(1);\nassert_eq!(*val, 1);\nassert_eq!(opt.unwrap(), 1);\nlet val = opt.insert(2);\nassert_eq!(*val, 2);\n*val = 3;\nassert_eq!(opt.unwrap(), 3);
\n
1.20.0 · source

pub fn get_or_insert(&mut self, value: T) -> &mut T

Inserts value into the option if it is None, then\nreturns a mutable reference to the contained value.

\n

See also Option::insert, which updates the value even if\nthe option already contains Some.

\n
§Examples
\n
let mut x = None;\n\n{\n    let y: &mut u32 = x.get_or_insert(5);\n    assert_eq!(y, &5);\n\n    *y = 7;\n}\n\nassert_eq!(x, Some(7));
\n
source

pub fn get_or_insert_default(&mut self) -> &mut T
where\n T: Default,

🔬This is a nightly-only experimental API. (option_get_or_insert_default)

Inserts the default value into the option if it is None, then\nreturns a mutable reference to the contained value.

\n
§Examples
\n
#![feature(option_get_or_insert_default)]\n\nlet mut x = None;\n\n{\n    let y: &mut u32 = x.get_or_insert_default();\n    assert_eq!(y, &0);\n\n    *y = 7;\n}\n\nassert_eq!(x, Some(7));
\n
1.20.0 · source

pub fn get_or_insert_with<F>(&mut self, f: F) -> &mut T
where\n F: FnOnce() -> T,

Inserts a value computed from f into the option if it is None,\nthen returns a mutable reference to the contained value.

\n
§Examples
\n
let mut x = None;\n\n{\n    let y: &mut u32 = x.get_or_insert_with(|| 5);\n    assert_eq!(y, &5);\n\n    *y = 7;\n}\n\nassert_eq!(x, Some(7));
\n
1.0.0 (const: unstable) · source

pub fn take(&mut self) -> Option<T>

Takes the value out of the option, leaving a None in its place.

\n
§Examples
\n
let mut x = Some(2);\nlet y = x.take();\nassert_eq!(x, None);\nassert_eq!(y, Some(2));\n\nlet mut x: Option<u32> = None;\nlet y = x.take();\nassert_eq!(x, None);\nassert_eq!(y, None);
\n
1.80.0 · source

pub fn take_if<P>(&mut self, predicate: P) -> Option<T>
where\n P: FnOnce(&mut T) -> bool,

Takes the value out of the option, but only if the predicate evaluates to\ntrue on a mutable reference to the value.

\n

In other words, replaces self with None if the predicate returns true.\nThis method operates similar to Option::take but conditional.

\n
§Examples
\n
let mut x = Some(42);\n\nlet prev = x.take_if(|v| if *v == 42 {\n    *v += 1;\n    false\n} else {\n    false\n});\nassert_eq!(x, Some(43));\nassert_eq!(prev, None);\n\nlet prev = x.take_if(|v| *v == 43);\nassert_eq!(x, None);\nassert_eq!(prev, Some(43));
\n
1.31.0 (const: unstable) · source

pub fn replace(&mut self, value: T) -> Option<T>

Replaces the actual value in the option by the value given in parameter,\nreturning the old value if present,\nleaving a Some in its place without deinitializing either one.

\n
§Examples
\n
let mut x = Some(2);\nlet old = x.replace(5);\nassert_eq!(x, Some(5));\nassert_eq!(old, Some(2));\n\nlet mut x = None;\nlet old = x.replace(3);\nassert_eq!(x, Some(3));\nassert_eq!(old, None);
\n
1.46.0 · source

pub fn zip<U>(self, other: Option<U>) -> Option<(T, U)>

Zips self with another Option.

\n

If self is Some(s) and other is Some(o), this method returns Some((s, o)).\nOtherwise, None is returned.

\n
§Examples
\n
let x = Some(1);\nlet y = Some(\"hi\");\nlet z = None::<u8>;\n\nassert_eq!(x.zip(y), Some((1, \"hi\")));\nassert_eq!(x.zip(z), None);
\n
source

pub fn zip_with<U, F, R>(self, other: Option<U>, f: F) -> Option<R>
where\n F: FnOnce(T, U) -> R,

🔬This is a nightly-only experimental API. (option_zip)

Zips self and another Option with function f.

\n

If self is Some(s) and other is Some(o), this method returns Some(f(s, o)).\nOtherwise, None is returned.

\n
§Examples
\n
#![feature(option_zip)]\n\n#[derive(Debug, PartialEq)]\nstruct Point {\n    x: f64,\n    y: f64,\n}\n\nimpl Point {\n    fn new(x: f64, y: f64) -> Self {\n        Self { x, y }\n    }\n}\n\nlet x = Some(17.5);\nlet y = Some(42.7);\n\nassert_eq!(x.zip_with(y, Point::new), Some(Point { x: 17.5, y: 42.7 }));\nassert_eq!(x.zip_with(None, Point::new), None);
\n
",0,"fred::types::LimitCount"],["
1.0.0 · source§

impl<T> Ord for Option<T>
where\n T: Ord,

source§

fn cmp(&self, other: &Option<T>) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · source§

fn max(self, other: Self) -> Self
where\n Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · source§

fn min(self, other: Self) -> Self
where\n Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Self
where\n Self: Sized + PartialOrd,

Restrict a value to a certain interval. Read more
","Ord","fred::types::LimitCount"],["
1.0.0 · source§

impl<T> PartialEq for Option<T>
where\n T: PartialEq,

source§

fn eq(&self, other: &Option<T>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient,\nand should not be overridden without very good reason.
","PartialEq","fred::types::LimitCount"],["
1.0.0 · source§

impl<T> PartialOrd for Option<T>
where\n T: PartialOrd,

source§

fn partial_cmp(&self, other: &Option<T>) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the\n<= operator. Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the >\noperator. Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by\nthe >= operator. Read more
","PartialOrd","fred::types::LimitCount"],["
1.37.0 · source§

impl<T, U> Product<Option<U>> for Option<T>
where\n T: Product<U>,

source§

fn product<I>(iter: I) -> Option<T>
where\n I: Iterator<Item = Option<U>>,

Takes each element in the Iterator: if it is a None, no further\nelements are taken, and the None is returned. Should no None\noccur, the product of all elements is returned.

\n
§Examples
\n

This multiplies each number in a vector of strings,\nif a string could not be parsed the operation returns None:

\n\n
let nums = vec![\"5\", \"10\", \"1\", \"2\"];\nlet total: Option<usize> = nums.iter().map(|w| w.parse::<usize>().ok()).product();\nassert_eq!(total, Some(100));\nlet nums = vec![\"5\", \"10\", \"one\", \"2\"];\nlet total: Option<usize> = nums.iter().map(|w| w.parse::<usize>().ok()).product();\nassert_eq!(total, None);
\n
","Product>","fred::types::LimitCount"],["
source§

impl<T> Serialize for Option<T>
where\n T: Serialize,

source§

fn serialize<S>(\n &self,\n serializer: S,\n) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where\n S: Serializer,

Serialize this value into the given Serde serializer. Read more
","Serialize","fred::types::LimitCount"],["
1.37.0 · source§

impl<T, U> Sum<Option<U>> for Option<T>
where\n T: Sum<U>,

source§

fn sum<I>(iter: I) -> Option<T>
where\n I: Iterator<Item = Option<U>>,

Takes each element in the Iterator: if it is a None, no further\nelements are taken, and the None is returned. Should no None\noccur, the sum of all elements is returned.

\n
§Examples
\n

This sums up the position of the character ‘a’ in a vector of strings,\nif a word did not have the character ‘a’ the operation returns None:

\n\n
let words = vec![\"have\", \"a\", \"great\", \"day\"];\nlet total: Option<usize> = words.iter().map(|w| w.find('a')).sum();\nassert_eq!(total, Some(5));\nlet words = vec![\"have\", \"a\", \"good\", \"day\"];\nlet total: Option<usize> = words.iter().map(|w| w.find('a')).sum();\nassert_eq!(total, None);
\n
","Sum>","fred::types::LimitCount"],["
source§

impl<T> Try for Option<T>

§

type Output = T

🔬This is a nightly-only experimental API. (try_trait_v2)
The type of the value produced by ? when not short-circuiting.
§

type Residual = Option<Infallible>

🔬This is a nightly-only experimental API. (try_trait_v2)
The type of the value passed to FromResidual::from_residual\nas part of ? when short-circuiting. Read more
source§

fn from_output(output: <Option<T> as Try>::Output) -> Option<T>

🔬This is a nightly-only experimental API. (try_trait_v2)
Constructs the type from its Output type. Read more
source§

fn branch(\n self,\n) -> ControlFlow<<Option<T> as Try>::Residual, <Option<T> as Try>::Output>

🔬This is a nightly-only experimental API. (try_trait_v2)
Used in ? to decide whether the operator should produce a value\n(because this returned ControlFlow::Continue)\nor propagate a value back to the caller\n(because this returned ControlFlow::Break). Read more
","Try","fred::types::LimitCount"],["
§

impl<T> Value for Option<T>
where\n T: Value,

§

fn record(&self, key: &Field, visitor: &mut dyn Visit)

Visits this value with the given Visitor.
","Value","fred::types::LimitCount"],["
§

impl<Z> Zeroize for Option<Z>
where\n Z: Zeroize,

§

fn zeroize(&mut self)

Zero out this object from memory using Rust intrinsics which ensure the\nzeroization operation is not “optimized away” by the compiler.
","Zeroize","fred::types::LimitCount"],["
1.0.0 · source§

impl<T> Copy for Option<T>
where\n T: Copy,

","Copy","fred::types::LimitCount"],["
1.0.0 · source§

impl<T> Eq for Option<T>
where\n T: Eq,

","Eq","fred::types::LimitCount"],["
1.0.0 · source§

impl<T> StructuralPartialEq for Option<T>

","StructuralPartialEq","fred::types::LimitCount"],["
§

impl<Z> ZeroizeOnDrop for Option<Z>
where\n Z: ZeroizeOnDrop,

","ZeroizeOnDrop","fred::types::LimitCount"]]]]); + if (window.register_type_impls) { + window.register_type_impls(type_impls); + } else { + window.pending_type_impls = type_impls; + } +})() +//{"start":55,"fragment_lengths":[167396]} \ No newline at end of file diff --git a/doc/type.impl/core/result/enum.Result.js b/doc/type.impl/core/result/enum.Result.js new file mode 100644 index 00000000..4d0e8b64 --- /dev/null +++ b/doc/type.impl/core/result/enum.Result.js @@ -0,0 +1,9 @@ +(function() { + var type_impls = Object.fromEntries([["fred",[["
1.0.0 · source§

impl<T, E> Clone for Result<T, E>
where\n T: Clone,\n E: Clone,

source§

fn clone(&self) -> Result<T, E>

Returns a copy of the value. Read more
source§

fn clone_from(&mut self, source: &Result<T, E>)

Performs copy-assignment from source. Read more
","Clone","fred::interfaces::RedisResult"],["
1.0.0 · source§

impl<T, E> Debug for Result<T, E>
where\n T: Debug,\n E: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
","Debug","fred::interfaces::RedisResult"],["
source§

impl<'de, T, E> Deserialize<'de> for Result<T, E>
where\n T: Deserialize<'de>,\n E: Deserialize<'de>,

source§

fn deserialize<D>(\n deserializer: D,\n) -> Result<Result<T, E>, <D as Deserializer<'de>>::Error>
where\n D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
","Deserialize<'de>","fred::interfaces::RedisResult"],["
1.0.0 · source§

impl<A, E, V> FromIterator<Result<A, E>> for Result<V, E>
where\n V: FromIterator<A>,

source§

fn from_iter<I>(iter: I) -> Result<V, E>
where\n I: IntoIterator<Item = Result<A, E>>,

Takes each element in the Iterator: if it is an Err, no further\nelements are taken, and the Err is returned. Should no Err occur, a\ncontainer with the values of each Result is returned.

\n

Here is an example which increments every integer in a vector,\nchecking for overflow:

\n\n
let v = vec![1, 2];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n    x.checked_add(1).ok_or(\"Overflow!\")\n).collect();\nassert_eq!(res, Ok(vec![2, 3]));
\n

Here is another example that tries to subtract one from another list\nof integers, this time checking for underflow:

\n\n
let v = vec![1, 2, 0];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n    x.checked_sub(1).ok_or(\"Underflow!\")\n).collect();\nassert_eq!(res, Err(\"Underflow!\"));
\n

Here is a variation on the previous example, showing that no\nfurther elements are taken from iter after the first Err.

\n\n
let v = vec![3, 2, 1, 10];\nlet mut shared = 0;\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32| {\n    shared += x;\n    x.checked_sub(2).ok_or(\"Underflow!\")\n}).collect();\nassert_eq!(res, Err(\"Underflow!\"));\nassert_eq!(shared, 6);
\n

Since the third element caused an underflow, no further elements were taken,\nso the final value of shared is 6 (= 3 + 2 + 1), not 16.

\n
","FromIterator>","fred::interfaces::RedisResult"],["
source§

impl<T, E, F> FromResidual<Result<Infallible, E>> for Result<T, F>
where\n F: From<E>,

source§

fn from_residual(residual: Result<Infallible, E>) -> Result<T, F>

🔬This is a nightly-only experimental API. (try_trait_v2)
Constructs the type from a compatible Residual type. Read more
","FromResidual>","fred::interfaces::RedisResult"],["
source§

impl<T, E, F> FromResidual<Yeet<E>> for Result<T, F>
where\n F: From<E>,

source§

fn from_residual(_: Yeet<E>) -> Result<T, F>

🔬This is a nightly-only experimental API. (try_trait_v2)
Constructs the type from a compatible Residual type. Read more
","FromResidual>","fred::interfaces::RedisResult"],["
1.0.0 · source§

impl<T, E> Hash for Result<T, E>
where\n T: Hash,\n E: Hash,

source§

fn hash<__H>(&self, state: &mut __H)
where\n __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where\n H: Hasher,\n Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
","Hash","fred::interfaces::RedisResult"],["
1.0.0 · source§

impl<T, E> IntoIterator for Result<T, E>

source§

fn into_iter(self) -> IntoIter<T>

Returns a consuming iterator over the possibly contained value.

\n

The iterator yields one value if the result is Result::Ok, otherwise none.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(5);\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, [5]);\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, []);
\n
§

type Item = T

The type of the elements being iterated over.
§

type IntoIter = IntoIter<T>

Which kind of iterator are we turning this into?
","IntoIterator","fred::interfaces::RedisResult"],["
1.0.0 · source§

impl<T, E> Ord for Result<T, E>
where\n T: Ord,\n E: Ord,

source§

fn cmp(&self, other: &Result<T, E>) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · source§

fn max(self, other: Self) -> Self
where\n Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · source§

fn min(self, other: Self) -> Self
where\n Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Self
where\n Self: Sized + PartialOrd,

Restrict a value to a certain interval. Read more
","Ord","fred::interfaces::RedisResult"],["
1.0.0 · source§

impl<T, E> PartialEq for Result<T, E>
where\n T: PartialEq,\n E: PartialEq,

source§

fn eq(&self, other: &Result<T, E>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient,\nand should not be overridden without very good reason.
","PartialEq","fred::interfaces::RedisResult"],["
1.0.0 · source§

impl<T, E> PartialOrd for Result<T, E>
where\n T: PartialOrd,\n E: PartialOrd,

source§

fn partial_cmp(&self, other: &Result<T, E>) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the\n<= operator. Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the >\noperator. Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by\nthe >= operator. Read more
","PartialOrd","fred::interfaces::RedisResult"],["
1.16.0 · source§

impl<T, U, E> Product<Result<U, E>> for Result<T, E>
where\n T: Product<U>,

source§

fn product<I>(iter: I) -> Result<T, E>
where\n I: Iterator<Item = Result<U, E>>,

Takes each element in the Iterator: if it is an Err, no further\nelements are taken, and the Err is returned. Should no Err\noccur, the product of all elements is returned.

\n
§Examples
\n

This multiplies each number in a vector of strings,\nif a string could not be parsed the operation returns Err:

\n\n
let nums = vec![\"5\", \"10\", \"1\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert_eq!(total, Ok(100));\nlet nums = vec![\"5\", \"10\", \"one\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert!(total.is_err());
\n
","Product>","fred::interfaces::RedisResult"],["
source§

impl<T, E> Residual<T> for Result<Infallible, E>

§

type TryType = Result<T, E>

🔬This is a nightly-only experimental API. (try_trait_v2_residual)
The “return” type of this meta-function.
","Residual","fred::interfaces::RedisResult"],["
§

impl<R, A> RestrictedMath for Result<R, A>
where\n R: RestrictedMath,\n A: 'static + Copy,

§

type Arg = <R as RestrictedMath>::Arg

Argument for the math operations
§

type Value = <R as RestrictedMath>::Value

Return value, generally the same as Arg
§

fn checked_add(\n &self,\n arg: <Result<R, A> as RestrictedMath>::Arg,\n) -> Result<Restrict<<Result<R, A> as RestrictedMath>::Value>, <Result<R, A> as RestrictedMath>::Arg>

Checked addition, see usize::checked_add
§

fn checked_sub(\n &self,\n arg: <Result<R, A> as RestrictedMath>::Arg,\n) -> Result<Restrict<<Result<R, A> as RestrictedMath>::Value>, <Result<R, A> as RestrictedMath>::Arg>

Checked subtraction, see usize::checked_sub
§

fn checked_mul(\n &self,\n arg: <Result<R, A> as RestrictedMath>::Arg,\n) -> Result<Restrict<<Result<R, A> as RestrictedMath>::Value>, <Result<R, A> as RestrictedMath>::Arg>

Checked multiplication, see usize::checked_mul
","RestrictedMath","fred::interfaces::RedisResult"],["
source§

impl<T, E> Result<&T, E>

1.59.0 · source

pub fn copied(self) -> Result<T, E>
where\n T: Copy,

Maps a Result<&T, E> to a Result<T, E> by copying the contents of the\nOk part.

\n
§Examples
\n
let val = 12;\nlet x: Result<&i32, i32> = Ok(&val);\nassert_eq!(x, Ok(&12));\nlet copied = x.copied();\nassert_eq!(copied, Ok(12));
\n
1.59.0 · source

pub fn cloned(self) -> Result<T, E>
where\n T: Clone,

Maps a Result<&T, E> to a Result<T, E> by cloning the contents of the\nOk part.

\n
§Examples
\n
let val = 12;\nlet x: Result<&i32, i32> = Ok(&val);\nassert_eq!(x, Ok(&12));\nlet cloned = x.cloned();\nassert_eq!(cloned, Ok(12));
\n
",0,"fred::interfaces::RedisResult"],["
source§

impl<T, E> Result<&mut T, E>

1.59.0 · source

pub fn copied(self) -> Result<T, E>
where\n T: Copy,

Maps a Result<&mut T, E> to a Result<T, E> by copying the contents of the\nOk part.

\n
§Examples
\n
let mut val = 12;\nlet x: Result<&mut i32, i32> = Ok(&mut val);\nassert_eq!(x, Ok(&mut 12));\nlet copied = x.copied();\nassert_eq!(copied, Ok(12));
\n
1.59.0 · source

pub fn cloned(self) -> Result<T, E>
where\n T: Clone,

Maps a Result<&mut T, E> to a Result<T, E> by cloning the contents of the\nOk part.

\n
§Examples
\n
let mut val = 12;\nlet x: Result<&mut i32, i32> = Ok(&mut val);\nassert_eq!(x, Ok(&mut 12));\nlet cloned = x.cloned();\nassert_eq!(cloned, Ok(12));
\n
",0,"fred::interfaces::RedisResult"],["
source§

impl<T, E> Result<Option<T>, E>

1.33.0 (const: unstable) · source

pub fn transpose(self) -> Option<Result<T, E>>

Transposes a Result of an Option into an Option of a Result.

\n

Ok(None) will be mapped to None.\nOk(Some(_)) and Err(_) will be mapped to Some(Ok(_)) and Some(Err(_)).

\n
§Examples
\n
#[derive(Debug, Eq, PartialEq)]\nstruct SomeErr;\n\nlet x: Result<Option<i32>, SomeErr> = Ok(Some(5));\nlet y: Option<Result<i32, SomeErr>> = Some(Ok(5));\nassert_eq!(x.transpose(), y);
\n
",0,"fred::interfaces::RedisResult"],["
source§

impl<T, E> Result<Result<T, E>, E>

source

pub fn flatten(self) -> Result<T, E>

🔬This is a nightly-only experimental API. (result_flattening)

Converts from Result<Result<T, E>, E> to Result<T, E>

\n
§Examples
\n
#![feature(result_flattening)]\nlet x: Result<Result<&'static str, u32>, u32> = Ok(Ok(\"hello\"));\nassert_eq!(Ok(\"hello\"), x.flatten());\n\nlet x: Result<Result<&'static str, u32>, u32> = Ok(Err(6));\nassert_eq!(Err(6), x.flatten());\n\nlet x: Result<Result<&'static str, u32>, u32> = Err(6);\nassert_eq!(Err(6), x.flatten());
\n

Flattening only removes one level of nesting at a time:

\n\n
#![feature(result_flattening)]\nlet x: Result<Result<Result<&'static str, u32>, u32>, u32> = Ok(Ok(Ok(\"hello\")));\nassert_eq!(Ok(Ok(\"hello\")), x.flatten());\nassert_eq!(Ok(\"hello\"), x.flatten().flatten());
\n
",0,"fred::interfaces::RedisResult"],["
source§

impl<T, E> Result<T, E>

1.0.0 (const: 1.48.0) · source

pub const fn is_ok(&self) -> bool

Returns true if the result is Ok.

\n
§Examples
\n
let x: Result<i32, &str> = Ok(-3);\nassert_eq!(x.is_ok(), true);\n\nlet x: Result<i32, &str> = Err(\"Some error message\");\nassert_eq!(x.is_ok(), false);
\n
1.70.0 · source

pub fn is_ok_and(self, f: impl FnOnce(T) -> bool) -> bool

Returns true if the result is Ok and the value inside of it matches a predicate.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.is_ok_and(|x| x > 1), true);\n\nlet x: Result<u32, &str> = Ok(0);\nassert_eq!(x.is_ok_and(|x| x > 1), false);\n\nlet x: Result<u32, &str> = Err(\"hey\");\nassert_eq!(x.is_ok_and(|x| x > 1), false);
\n
1.0.0 (const: 1.48.0) · source

pub const fn is_err(&self) -> bool

Returns true if the result is Err.

\n
§Examples
\n
let x: Result<i32, &str> = Ok(-3);\nassert_eq!(x.is_err(), false);\n\nlet x: Result<i32, &str> = Err(\"Some error message\");\nassert_eq!(x.is_err(), true);
\n
1.70.0 · source

pub fn is_err_and(self, f: impl FnOnce(E) -> bool) -> bool

Returns true if the result is Err and the value inside of it matches a predicate.

\n
§Examples
\n
use std::io::{Error, ErrorKind};\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::NotFound, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), true);\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::PermissionDenied, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);\n\nlet x: Result<u32, Error> = Ok(123);\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);
\n
1.0.0 · source

pub fn ok(self) -> Option<T>

Converts from Result<T, E> to Option<T>.

\n

Converts self into an Option<T>, consuming self,\nand discarding the error, if any.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.ok(), Some(2));\n\nlet x: Result<u32, &str> = Err(\"Nothing here\");\nassert_eq!(x.ok(), None);
\n
1.0.0 · source

pub fn err(self) -> Option<E>

Converts from Result<T, E> to Option<E>.

\n

Converts self into an Option<E>, consuming self,\nand discarding the success value, if any.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.err(), None);\n\nlet x: Result<u32, &str> = Err(\"Nothing here\");\nassert_eq!(x.err(), Some(\"Nothing here\"));
\n
1.0.0 (const: 1.48.0) · source

pub const fn as_ref(&self) -> Result<&T, &E>

Converts from &Result<T, E> to Result<&T, &E>.

\n

Produces a new Result, containing a reference\ninto the original, leaving the original in place.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.as_ref(), Ok(&2));\n\nlet x: Result<u32, &str> = Err(\"Error\");\nassert_eq!(x.as_ref(), Err(&\"Error\"));
\n
1.0.0 (const: unstable) · source

pub fn as_mut(&mut self) -> Result<&mut T, &mut E>

Converts from &mut Result<T, E> to Result<&mut T, &mut E>.

\n
§Examples
\n
fn mutate(r: &mut Result<i32, i32>) {\n    match r.as_mut() {\n        Ok(v) => *v = 42,\n        Err(e) => *e = 0,\n    }\n}\n\nlet mut x: Result<i32, i32> = Ok(2);\nmutate(&mut x);\nassert_eq!(x.unwrap(), 42);\n\nlet mut x: Result<i32, i32> = Err(13);\nmutate(&mut x);\nassert_eq!(x.unwrap_err(), 0);
\n
1.0.0 · source

pub fn map<U, F>(self, op: F) -> Result<U, E>
where\n F: FnOnce(T) -> U,

Maps a Result<T, E> to Result<U, E> by applying a function to a\ncontained Ok value, leaving an Err value untouched.

\n

This function can be used to compose the results of two functions.

\n
§Examples
\n

Print the numbers on each line of a string multiplied by two.

\n\n
let line = \"1\\n2\\n3\\n4\\n\";\n\nfor num in line.lines() {\n    match num.parse::<i32>().map(|i| i * 2) {\n        Ok(n) => println!(\"{n}\"),\n        Err(..) => {}\n    }\n}
\n
1.41.0 · source

pub fn map_or<U, F>(self, default: U, f: F) -> U
where\n F: FnOnce(T) -> U,

Returns the provided default (if Err), or\napplies a function to the contained value (if Ok).

\n

Arguments passed to map_or are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use map_or_else,\nwhich is lazily evaluated.

\n
§Examples
\n
let x: Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or(42, |v| v.len()), 3);\n\nlet x: Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or(42, |v| v.len()), 42);
\n
1.41.0 · source

pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U
where\n D: FnOnce(E) -> U,\n F: FnOnce(T) -> U,

Maps a Result<T, E> to U by applying fallback function default to\na contained Err value, or function f to a contained Ok value.

\n

This function can be used to unpack a successful result\nwhile handling an error.

\n
§Examples
\n
let k = 21;\n\nlet x : Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 3);\n\nlet x : Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 42);
\n
1.0.0 · source

pub fn map_err<F, O>(self, op: O) -> Result<T, F>
where\n O: FnOnce(E) -> F,

Maps a Result<T, E> to Result<T, F> by applying a function to a\ncontained Err value, leaving an Ok value untouched.

\n

This function can be used to pass through a successful result while handling\nan error.

\n
§Examples
\n
fn stringify(x: u32) -> String { format!(\"error code: {x}\") }\n\nlet x: Result<u32, u32> = Ok(2);\nassert_eq!(x.map_err(stringify), Ok(2));\n\nlet x: Result<u32, u32> = Err(13);\nassert_eq!(x.map_err(stringify), Err(\"error code: 13\".to_string()));
\n
1.76.0 · source

pub fn inspect<F>(self, f: F) -> Result<T, E>
where\n F: FnOnce(&T),

Calls a function with a reference to the contained value if Ok.

\n

Returns the original result.

\n
§Examples
\n
let x: u8 = \"4\"\n    .parse::<u8>()\n    .inspect(|x| println!(\"original: {x}\"))\n    .map(|x| x.pow(3))\n    .expect(\"failed to parse number\");
\n
1.76.0 · source

pub fn inspect_err<F>(self, f: F) -> Result<T, E>
where\n F: FnOnce(&E),

Calls a function with a reference to the contained value if Err.

\n

Returns the original result.

\n
§Examples
\n
use std::{fs, io};\n\nfn read() -> io::Result<String> {\n    fs::read_to_string(\"address.txt\")\n        .inspect_err(|e| eprintln!(\"failed to read file: {e}\"))\n}
\n
1.47.0 · source

pub fn as_deref(&self) -> Result<&<T as Deref>::Target, &E>
where\n T: Deref,

Converts from Result<T, E> (or &Result<T, E>) to Result<&<T as Deref>::Target, &E>.

\n

Coerces the Ok variant of the original Result via Deref\nand returns the new Result.

\n
§Examples
\n
let x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&str, &u32> = Ok(\"hello\");\nassert_eq!(x.as_deref(), y);\n\nlet x: Result<String, u32> = Err(42);\nlet y: Result<&str, &u32> = Err(&42);\nassert_eq!(x.as_deref(), y);
\n
1.47.0 · source

pub fn as_deref_mut(&mut self) -> Result<&mut <T as Deref>::Target, &mut E>
where\n T: DerefMut,

Converts from Result<T, E> (or &mut Result<T, E>) to Result<&mut <T as DerefMut>::Target, &mut E>.

\n

Coerces the Ok variant of the original Result via DerefMut\nand returns the new Result.

\n
§Examples
\n
let mut s = \"HELLO\".to_string();\nlet mut x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&mut str, &mut u32> = Ok(&mut s);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);\n\nlet mut i = 42;\nlet mut x: Result<String, u32> = Err(42);\nlet y: Result<&mut str, &mut u32> = Err(&mut i);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);
\n
1.0.0 · source

pub fn iter(&self) -> Iter<'_, T>

Returns an iterator over the possibly contained value.

\n

The iterator yields one value if the result is Result::Ok, otherwise none.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(7);\nassert_eq!(x.iter().next(), Some(&7));\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter().next(), None);
\n
1.0.0 · source

pub fn iter_mut(&mut self) -> IterMut<'_, T>

Returns a mutable iterator over the possibly contained value.

\n

The iterator yields one value if the result is Result::Ok, otherwise none.

\n
§Examples
\n
let mut x: Result<u32, &str> = Ok(7);\nmatch x.iter_mut().next() {\n    Some(v) => *v = 40,\n    None => {},\n}\nassert_eq!(x, Ok(40));\n\nlet mut x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter_mut().next(), None);
\n
1.4.0 · source

pub fn expect(self, msg: &str) -> T
where\n E: Debug,

Returns the contained Ok value, consuming the self value.

\n

Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the Err\ncase explicitly, or call unwrap_or, unwrap_or_else, or\nunwrap_or_default.

\n
§Panics
\n

Panics if the value is an Err, with a panic message including the\npassed message, and the content of the Err.

\n
§Examples
\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.expect(\"Testing expect\"); // panics with `Testing expect: emergency failure`
\n
§Recommended Message Style
\n

We recommend that expect messages are used to describe the reason you\nexpect the Result should be Ok.

\n\n
let path = std::env::var(\"IMPORTANT_PATH\")\n    .expect(\"env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`\");
\n

Hint: If you’re having trouble remembering how to phrase expect\nerror messages remember to focus on the word “should” as in “env\nvariable should be set by blah” or “the given binary should be available\nand executable by the current user”.

\n

For more detail on expect message styles and the reasoning behind our recommendation please\nrefer to the section on “Common Message\nStyles” in the\nstd::error module docs.

\n
1.0.0 · source

pub fn unwrap(self) -> T
where\n E: Debug,

Returns the contained Ok value, consuming the self value.

\n

Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the Err\ncase explicitly, or call unwrap_or, unwrap_or_else, or\nunwrap_or_default.

\n
§Panics
\n

Panics if the value is an Err, with a panic message provided by the\nErr’s value.

\n
§Examples
\n

Basic usage:

\n\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.unwrap(), 2);
\n\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.unwrap(); // panics with `emergency failure`
\n
1.16.0 · source

pub fn unwrap_or_default(self) -> T
where\n T: Default,

Returns the contained Ok value or a default

\n

Consumes the self argument then, if Ok, returns the contained\nvalue, otherwise if Err, returns the default value for that\ntype.

\n
§Examples
\n

Converts a string to an integer, turning poorly-formed strings\ninto 0 (the default value for integers). parse converts\na string to any other type that implements FromStr, returning an\nErr on error.

\n\n
let good_year_from_input = \"1909\";\nlet bad_year_from_input = \"190blarg\";\nlet good_year = good_year_from_input.parse().unwrap_or_default();\nlet bad_year = bad_year_from_input.parse().unwrap_or_default();\n\nassert_eq!(1909, good_year);\nassert_eq!(0, bad_year);
\n
1.17.0 · source

pub fn expect_err(self, msg: &str) -> E
where\n T: Debug,

Returns the contained Err value, consuming the self value.

\n
§Panics
\n

Panics if the value is an Ok, with a panic message including the\npassed message, and the content of the Ok.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(10);\nx.expect_err(\"Testing expect_err\"); // panics with `Testing expect_err: 10`
\n
1.0.0 · source

pub fn unwrap_err(self) -> E
where\n T: Debug,

Returns the contained Err value, consuming the self value.

\n
§Panics
\n

Panics if the value is an Ok, with a custom panic message provided\nby the Ok’s value.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nx.unwrap_err(); // panics with `2`
\n\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(x.unwrap_err(), \"emergency failure\");
\n
source

pub fn into_ok(self) -> T
where\n E: Into<!>,

🔬This is a nightly-only experimental API. (unwrap_infallible)

Returns the contained Ok value, but never panics.

\n

Unlike unwrap, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap as a maintainability safeguard that will fail\nto compile if the error type of the Result is later changed\nto an error that can actually occur.

\n
§Examples
\n
\nfn only_good_news() -> Result<String, !> {\n    Ok(\"this is fine\".into())\n}\n\nlet s: String = only_good_news().into_ok();\nprintln!(\"{s}\");
\n
source

pub fn into_err(self) -> E
where\n T: Into<!>,

🔬This is a nightly-only experimental API. (unwrap_infallible)

Returns the contained Err value, but never panics.

\n

Unlike unwrap_err, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap_err as a maintainability safeguard that will fail\nto compile if the ok type of the Result is later changed\nto a type that can actually occur.

\n
§Examples
\n
\nfn only_bad_news() -> Result<!, String> {\n    Err(\"Oops, it failed\".into())\n}\n\nlet error: String = only_bad_news().into_err();\nprintln!(\"{error}\");
\n
1.0.0 · source

pub fn and<U>(self, res: Result<U, E>) -> Result<U, E>

Returns res if the result is Ok, otherwise returns the Err value of self.

\n

Arguments passed to and are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use and_then, which is\nlazily evaluated.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<&str, &str> = Ok(\"foo\");\nassert_eq!(x.and(y), Err(\"early error\"));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"not a 2\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Ok(\"different result type\");\nassert_eq!(x.and(y), Ok(\"different result type\"));
\n
1.0.0 · source

pub fn and_then<U, F>(self, op: F) -> Result<U, E>
where\n F: FnOnce(T) -> Result<U, E>,

Calls op if the result is Ok, otherwise returns the Err value of self.

\n

This function can be used for control flow based on Result values.

\n
§Examples
\n
fn sq_then_to_string(x: u32) -> Result<String, &'static str> {\n    x.checked_mul(x).map(|sq| sq.to_string()).ok_or(\"overflowed\")\n}\n\nassert_eq!(Ok(2).and_then(sq_then_to_string), Ok(4.to_string()));\nassert_eq!(Ok(1_000_000).and_then(sq_then_to_string), Err(\"overflowed\"));\nassert_eq!(Err(\"not a number\").and_then(sq_then_to_string), Err(\"not a number\"));
\n

Often used to chain fallible operations that may return Err.

\n\n
use std::{io::ErrorKind, path::Path};\n\n// Note: on Windows \"/\" maps to \"C:\\\"\nlet root_modified_time = Path::new(\"/\").metadata().and_then(|md| md.modified());\nassert!(root_modified_time.is_ok());\n\nlet should_fail = Path::new(\"/bad/path\").metadata().and_then(|md| md.modified());\nassert!(should_fail.is_err());\nassert_eq!(should_fail.unwrap_err().kind(), ErrorKind::NotFound);
\n
1.0.0 · source

pub fn or<F>(self, res: Result<T, F>) -> Result<T, F>

Returns res if the result is Err, otherwise returns the Ok value of self.

\n

Arguments passed to or are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use or_else, which is\nlazily evaluated.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<u32, &str> = Ok(2);\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Ok(100);\nassert_eq!(x.or(y), Ok(2));
\n
1.0.0 · source

pub fn or_else<F, O>(self, op: O) -> Result<T, F>
where\n O: FnOnce(E) -> Result<T, F>,

Calls op if the result is Err, otherwise returns the Ok value of self.

\n

This function can be used for control flow based on result values.

\n
§Examples
\n
fn sq(x: u32) -> Result<u32, u32> { Ok(x * x) }\nfn err(x: u32) -> Result<u32, u32> { Err(x) }\n\nassert_eq!(Ok(2).or_else(sq).or_else(sq), Ok(2));\nassert_eq!(Ok(2).or_else(err).or_else(sq), Ok(2));\nassert_eq!(Err(3).or_else(sq).or_else(err), Ok(9));\nassert_eq!(Err(3).or_else(err).or_else(err), Err(3));
\n
1.0.0 · source

pub fn unwrap_or(self, default: T) -> T

Returns the contained Ok value or a provided default.

\n

Arguments passed to unwrap_or are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use unwrap_or_else,\nwhich is lazily evaluated.

\n
§Examples
\n
let default = 2;\nlet x: Result<u32, &str> = Ok(9);\nassert_eq!(x.unwrap_or(default), 9);\n\nlet x: Result<u32, &str> = Err(\"error\");\nassert_eq!(x.unwrap_or(default), default);
\n
1.0.0 · source

pub fn unwrap_or_else<F>(self, op: F) -> T
where\n F: FnOnce(E) -> T,

Returns the contained Ok value or computes it from a closure.

\n
§Examples
\n
fn count(x: &str) -> usize { x.len() }\n\nassert_eq!(Ok(2).unwrap_or_else(count), 2);\nassert_eq!(Err(\"foo\").unwrap_or_else(count), 3);
\n
1.58.0 · source

pub unsafe fn unwrap_unchecked(self) -> T

Returns the contained Ok value, consuming the self value,\nwithout checking that the value is not an Err.

\n
§Safety
\n

Calling this method on an Err is undefined behavior.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(unsafe { x.unwrap_unchecked() }, 2);
\n\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nunsafe { x.unwrap_unchecked(); } // Undefined behavior!
\n
1.58.0 · source

pub unsafe fn unwrap_err_unchecked(self) -> E

Returns the contained Err value, consuming the self value,\nwithout checking that the value is not an Ok.

\n
§Safety
\n

Calling this method on an Ok is undefined behavior.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nunsafe { x.unwrap_err_unchecked() }; // Undefined behavior!
\n\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(unsafe { x.unwrap_err_unchecked() }, \"emergency failure\");
\n
",0,"fred::interfaces::RedisResult"],["
§

impl<T, E> ResultExt<T, E> for Result<T, E>

§

fn context<X>(self, x: X) -> Result<T, Context<X, E>>

The method is use to add context information to current operation Read more
","ResultExt","fred::interfaces::RedisResult"],["
source§

impl<T, E> Serialize for Result<T, E>
where\n T: Serialize,\n E: Serialize,

source§

fn serialize<S>(\n &self,\n serializer: S,\n) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where\n S: Serializer,

Serialize this value into the given Serde serializer. Read more
","Serialize","fred::interfaces::RedisResult"],["
1.16.0 · source§

impl<T, U, E> Sum<Result<U, E>> for Result<T, E>
where\n T: Sum<U>,

source§

fn sum<I>(iter: I) -> Result<T, E>
where\n I: Iterator<Item = Result<U, E>>,

Takes each element in the Iterator: if it is an Err, no further\nelements are taken, and the Err is returned. Should no Err\noccur, the sum of all elements is returned.

\n
§Examples
\n

This sums up every integer in a vector, rejecting the sum if a negative\nelement is encountered:

\n\n
let f = |&x: &i32| if x < 0 { Err(\"Negative element found\") } else { Ok(x) };\nlet v = vec![1, 2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Ok(3));\nlet v = vec![1, -2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Err(\"Negative element found\"));
\n
","Sum>","fred::interfaces::RedisResult"],["
1.61.0 · source§

impl<T, E> Termination for Result<T, E>
where\n T: Termination,\n E: Debug,

source§

fn report(self) -> ExitCode

Is called to get the representation of the value as status code.\nThis status code is returned to the operating system.
","Termination","fred::interfaces::RedisResult"],["
source§

impl<T, E> Try for Result<T, E>

§

type Output = T

🔬This is a nightly-only experimental API. (try_trait_v2)
The type of the value produced by ? when not short-circuiting.
§

type Residual = Result<Infallible, E>

🔬This is a nightly-only experimental API. (try_trait_v2)
The type of the value passed to FromResidual::from_residual\nas part of ? when short-circuiting. Read more
source§

fn from_output(output: <Result<T, E> as Try>::Output) -> Result<T, E>

🔬This is a nightly-only experimental API. (try_trait_v2)
Constructs the type from its Output type. Read more
source§

fn branch(\n self,\n) -> ControlFlow<<Result<T, E> as Try>::Residual, <Result<T, E> as Try>::Output>

🔬This is a nightly-only experimental API. (try_trait_v2)
Used in ? to decide whether the operator should produce a value\n(because this returned ControlFlow::Continue)\nor propagate a value back to the caller\n(because this returned ControlFlow::Break). Read more
","Try","fred::interfaces::RedisResult"],["
1.0.0 · source§

impl<T, E> Copy for Result<T, E>
where\n T: Copy,\n E: Copy,

","Copy","fred::interfaces::RedisResult"],["
1.0.0 · source§

impl<T, E> Eq for Result<T, E>
where\n T: Eq,\n E: Eq,

","Eq","fred::interfaces::RedisResult"],["
1.0.0 · source§

impl<T, E> StructuralPartialEq for Result<T, E>

","StructuralPartialEq","fred::interfaces::RedisResult"]]]]); + if (window.register_type_impls) { + window.register_type_impls(type_impls); + } else { + window.pending_type_impls = type_impls; + } +})() +//{"start":55,"fragment_lengths":[170923]} \ No newline at end of file diff --git a/doc/type.impl/fred/types/enum.RedisValue.js b/doc/type.impl/fred/types/enum.RedisValue.js new file mode 100644 index 00000000..e06b354d --- /dev/null +++ b/doc/type.impl/fred/types/enum.RedisValue.js @@ -0,0 +1,9 @@ +(function() { + var type_impls = Object.fromEntries([["fred",[["
source§

impl Clone for RedisValue

source§

fn clone(&self) -> RedisValue

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
","Clone","fred::types::multiple::MultipleValues"],["
source§

impl Debug for RedisValue

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
","Debug","fred::types::multiple::MultipleValues"],["
source§

impl<'a> From<&'a [u8]> for RedisValue

source§

fn from(b: &'a [u8]) -> Self

Converts to this type from the input type.
","From<&'a [u8]>","fred::types::multiple::MultipleValues"],["
source§

impl<'a> From<&'a String> for RedisValue

source§

fn from(d: &'a String) -> Self

Converts to this type from the input type.
","From<&'a String>","fred::types::multiple::MultipleValues"],["
source§

impl<'a> From<&'a str> for RedisValue

source§

fn from(d: &'a str) -> Self

Converts to this type from the input type.
","From<&'a str>","fred::types::multiple::MultipleValues"],["
source§

impl From<()> for RedisValue

source§

fn from(_: ()) -> Self

Converts to this type from the input type.
","From<()>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>> From<(A0, A1)> for RedisValue

source§

fn from(value: (A0, A1)) -> Self

Converts to this type from the input type.
","From<(A0, A1)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>> From<(A0, A1, A2)> for RedisValue

source§

fn from(value: (A0, A1, A2)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>> From<(A0, A1, A2, A3)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>> From<(A0, A1, A2, A3, A4)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)> for RedisValue

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>, A13: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)> for RedisValue

source§

fn from(\n value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13),\n) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>, A13: Into<RedisValue>, A14: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)> for RedisValue

source§

fn from(\n value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14),\n) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>, A13: Into<RedisValue>, A14: Into<RedisValue>, A15: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)> for RedisValue

source§

fn from(\n value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15),\n) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)>","fred::types::multiple::MultipleValues"],["
source§

impl<A0: Into<RedisValue>, A1: Into<RedisValue>, A2: Into<RedisValue>, A3: Into<RedisValue>, A4: Into<RedisValue>, A5: Into<RedisValue>, A6: Into<RedisValue>, A7: Into<RedisValue>, A8: Into<RedisValue>, A9: Into<RedisValue>, A10: Into<RedisValue>, A11: Into<RedisValue>, A12: Into<RedisValue>, A13: Into<RedisValue>, A14: Into<RedisValue>, A15: Into<RedisValue>, A16: Into<RedisValue>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16)> for RedisValue

source§

fn from(\n value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16),\n) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16)>","fred::types::multiple::MultipleValues"],["
source§

impl From<Box<[u8]>> for RedisValue

source§

fn from(b: Box<[u8]>) -> Self

Converts to this type from the input type.
","From>","fred::types::multiple::MultipleValues"],["
source§

impl From<Bytes> for RedisValue

source§

fn from(b: Bytes) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl From<RedisKey> for RedisValue

source§

fn from(d: RedisKey) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl From<RedisMap> for RedisValue

source§

fn from(m: RedisMap) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl From<StrInner<Bytes>> for RedisValue

source§

fn from(s: Str) -> Self

Converts to this type from the input type.
","From>","fred::types::multiple::MultipleValues"],["
source§

impl From<String> for RedisValue

source§

fn from(d: String) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl From<bool> for RedisValue

source§

fn from(d: bool) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl From<f32> for RedisValue

source§

fn from(f: f32) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl From<f64> for RedisValue

source§

fn from(f: f64) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl From<i16> for RedisValue

source§

fn from(d: i16) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl From<i32> for RedisValue

source§

fn from(d: i32) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl From<i64> for RedisValue

source§

fn from(d: i64) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl From<i8> for RedisValue

source§

fn from(d: i8) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl From<u16> for RedisValue

source§

fn from(d: u16) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl From<u32> for RedisValue

source§

fn from(d: u32) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl From<u8> for RedisValue

source§

fn from(d: u8) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleValues"],["
source§

impl<V> FromIterator<V> for RedisValue
where\n V: Into<RedisValue>,

source§

fn from_iter<I: IntoIterator<Item = V>>(iter: I) -> Self

Creates a value from an iterator. Read more
","FromIterator","fred::types::multiple::MultipleValues"],["
source§

impl FromRedis for RedisValue

","FromRedis","fred::types::multiple::MultipleValues"],["
source§

impl FromRedisKey for RedisValue

","FromRedisKey","fred::types::multiple::MultipleValues"],["
source§

impl Hash for RedisValue

source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where\n H: Hasher,\n Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
","Hash","fred::types::multiple::MultipleValues"],["
source§

impl PartialEq for RedisValue

source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient,\nand should not be overridden without very good reason.
","PartialEq","fred::types::multiple::MultipleValues"],["
source§

impl RedisValue

source

pub fn from_static(b: &'static [u8]) -> Self

Create a new RedisValue::Bytes from a static byte slice without copying.

\n
source

pub fn from_static_str(s: &'static str) -> Self

Create a new RedisValue::String from a static str without copying.

\n
source

pub fn new_ok() -> Self

Create a new RedisValue with the OK status.

\n
source

pub fn is_ok(&self) -> bool

Whether the value is a simple string OK value.

\n
source

pub fn into_integer(self) -> Result<RedisValue, RedisValue>

Attempt to convert the value into an integer, returning the original string as an error if the parsing fails.

\n
source

pub fn kind(&self) -> RedisValueKind

Read the type of the value without any associated data.

\n
source

pub fn is_null(&self) -> bool

Check if the value is null.

\n
source

pub fn is_integer(&self) -> bool

Check if the value is an integer.

\n
source

pub fn is_string(&self) -> bool

Check if the value is a string.

\n
source

pub fn is_bytes(&self) -> bool

Check if the value is an array of bytes.

\n
source

pub fn is_boolean(&self) -> bool

Whether the value is a boolean value or can be parsed as a boolean value.

\n
source

pub fn is_double(&self) -> bool

Whether the inner value is a double or can be parsed as a double.

\n
source

pub fn is_queued(&self) -> bool

Check if the value is a QUEUED response.

\n
source

pub fn is_aggregate_type(&self) -> bool

Whether the value is an array or map.

\n
source

pub fn is_map(&self) -> bool

Whether the value is a RedisMap.

\n

See is_maybe_map for a function that also checks for arrays that likely represent a map in\nRESP2 mode.

\n
source

pub fn is_maybe_map(&self) -> bool

Whether the value is a RedisMap or an array with an even number of elements where each even-numbered\nelement is not an aggregate type.

\n

RESP2 and RESP3 encode maps differently, and this function can be used to duck-type maps across protocol\nversions.

\n
source

pub fn is_array(&self) -> bool

Whether the value is an array.

\n
source

pub fn as_u64(&self) -> Option<u64>

Read and return the inner value as a u64, if possible.

\n
source

pub fn as_i64(&self) -> Option<i64>

Read and return the inner value as a i64, if possible.

\n
source

pub fn as_usize(&self) -> Option<usize>

Read and return the inner value as a usize, if possible.

\n
source

pub fn as_f64(&self) -> Option<f64>

Read and return the inner value as a f64, if possible.

\n
source

pub fn into_string(self) -> Option<String>

Read and return the inner String if the value is a string or scalar value.

\n
source

pub fn into_bytes_str(self) -> Option<Str>

Read and return the inner data as a Str from the bytes crate.

\n
source

pub fn as_bytes_str(&self) -> Option<Str>

Read the inner value as a Str.

\n
source

pub fn as_string(&self) -> Option<String>

Read and return the inner String if the value is a string or scalar value.

\n

Note: this will cast integers and doubles to strings.

\n
source

pub fn as_str(&self) -> Option<Cow<'_, str>>

Read the inner value as a string slice.

\n

Null is returned as \"nil\" and scalar values are cast to a string.

\n
source

pub fn as_str_lossy(&self) -> Option<Cow<'_, str>>

Read the inner value as a string, using String::from_utf8_lossy on byte slices.

\n
source

pub fn as_bytes(&self) -> Option<&[u8]>

Read the inner value as an array of bytes, if possible.

\n
source

pub fn as_bool(&self) -> Option<bool>

Attempt to convert the value to a bool.

\n
source

pub fn into_map(self) -> Result<RedisMap, RedisError>

Attempt to convert this value to a Redis map if it’s an array with an even number of elements.

\n
source

pub fn into_set(self) -> Result<HashSet<RedisValue>, RedisError>

Convert the array value to a set, if possible.

\n
source

pub fn into_zset_result(self) -> Result<Vec<(RedisValue, f64)>, RedisError>

Convert a RedisValue to Vec<(RedisValue, f64)>, if possible.

\n
source

pub fn into_array(self) -> Vec<RedisValue>

Convert this value to an array if it’s an array or map.

\n

If the value is not an array or map this returns a single-element array containing the original value.

\n
source

pub fn into_owned_bytes(self) -> Option<Vec<u8>>

Convert the value to an array of bytes, if possible.

\n
source

pub fn into_bytes(self) -> Option<Bytes>

Convert the value into a Bytes view.

\n
source

pub fn array_len(&self) -> Option<usize>

Return the length of the inner array if the value is an array.

\n
source

pub fn flatten_array_values(self, depth: usize) -> Self

Flatten adjacent nested arrays to the provided depth.

\n

See the XREAD documentation for an example of when this might be\nuseful.

\n
source

pub fn into_xread_response<K1, I, K2, V>(\n self,\n) -> Result<XReadResponse<K1, I, K2, V>, RedisError>
where\n K1: FromRedisKey + Hash + Eq,\n K2: FromRedisKey + Hash + Eq,\n I: FromRedis,\n V: FromRedis,

Available on crate feature i-streams only.

A utility function to convert the response from XREAD or XREADGROUP into a type with a less verbose type\ndeclaration.

\n

This function supports responses in both RESP2 and RESP3 formats.

\n

See the XREAD (or XREADGROUP) documentation for more\ninformation.

\n
source

pub fn into_xread_value<I, K, V>(\n self,\n) -> Result<Vec<XReadValue<I, K, V>>, RedisError>
where\n K: FromRedisKey + Hash + Eq,\n I: FromRedis,\n V: FromRedis,

Available on crate feature i-streams only.

A utility function to convert the response from XCLAIM, etc into a type with a less verbose type declaration.

\n

This function supports responses in both RESP2 and RESP3 formats.

\n
source

pub fn into_xautoclaim_values<I, K, V>(\n self,\n) -> Result<(String, Vec<XReadValue<I, K, V>>), RedisError>
where\n K: FromRedisKey + Hash + Eq,\n I: FromRedis,\n V: FromRedis,

Available on crate feature i-streams only.

A utility function to convert the response from XAUTOCLAIM into a type with a less verbose type declaration.

\n

This function supports responses in both RESP2 and RESP3 formats.

\n

Note: the new (as of Redis 7.x) return value containing message PIDs that were deleted from the PEL are dropped.\nCallers should use xautoclaim instead if this data is needed.

\n
source

pub fn as_functions(&self, name: &str) -> Result<Vec<Function>, RedisError>

Available on crate feature i-scripts only.

Parse the value as the response from FUNCTION LIST, including only functions with the provided library name.

\n
source

pub fn as_geo_position(&self) -> Result<Option<GeoPosition>, RedisError>

Available on crate feature i-geo only.

Convert the value into a GeoPosition, if possible.

\n

Null values are returned as None to work more easily with the result of the GEOPOS command.

\n
source

pub fn into_geo_radius_result(\n self,\n withcoord: bool,\n withdist: bool,\n withhash: bool,\n) -> Result<Vec<GeoRadiusInfo>, RedisError>

Available on crate feature i-geo only.

Parse the value as the response to any of the relevant GEO commands that return an array of\nGeoRadiusInfo values, such as GEOSEARCH, GEORADIUS`, etc.

\n
source

pub fn take(&mut self) -> RedisValue

Replace this value with RedisValue::Null, returning the original value.

\n
source

pub fn convert<R>(self) -> Result<R, RedisError>
where\n R: FromRedis,

Attempt to convert this value to any value that implements the FromRedis trait.

\n
source

pub fn can_hash(&self) -> bool

Whether the value can be hashed.

\n

Some use cases require using RedisValue types as keys in a HashMap, etc. Trying to do so with an aggregate\ntype can panic, and this function can be used to more gracefully handle this situation.

\n
source

pub fn into_json(self) -> Result<Value, RedisError>

Available on crate feature serde-json only.

Convert the value to JSON.

\n
",0,"fred::types::multiple::MultipleValues"],["
source§

impl<'a, T, const N: usize> TryFrom<&'a [T; N]> for RedisValue
where\n T: TryInto<RedisValue> + Clone,\n T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: &'a [T; N]) -> Result<Self, Self::Error>

Performs the conversion.
","TryFrom<&'a [T; N]>","fred::types::multiple::MultipleValues"],["
source§

impl<T, const N: usize> TryFrom<[T; N]> for RedisValue
where\n T: TryInto<RedisValue> + Clone,\n T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: [T; N]) -> Result<Self, Self::Error>

Performs the conversion.
","TryFrom<[T; N]>","fred::types::multiple::MultipleValues"],["
source§

impl<K, V> TryFrom<BTreeMap<K, V>> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: BTreeMap<K, V>) -> Result<Self, Self::Error>

Performs the conversion.
","TryFrom>","fred::types::multiple::MultipleValues"],["
source§

impl TryFrom<BytesFrame> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: Resp3Frame) -> Result<Self, Self::Error>

Performs the conversion.
","TryFrom","fred::types::multiple::MultipleValues"],["
source§

impl<K, V> TryFrom<HashMap<K, V>> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: HashMap<K, V>) -> Result<Self, Self::Error>

Performs the conversion.
","TryFrom>","fred::types::multiple::MultipleValues"],["
source§

impl<T> TryFrom<Option<T>> for RedisValue
where\n T: TryInto<RedisValue>,\n T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: Option<T>) -> Result<Self, Self::Error>

Performs the conversion.
","TryFrom>","fred::types::multiple::MultipleValues"],["
source§

impl<T> TryFrom<Vec<T>> for RedisValue
where\n T: TryInto<RedisValue>,\n T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: Vec<T>) -> Result<Self, Self::Error>

Performs the conversion.
","TryFrom>","fred::types::multiple::MultipleValues"],["
source§

impl<T> TryFrom<VecDeque<T>> for RedisValue
where\n T: TryInto<RedisValue>,\n T::Error: Into<RedisError>,

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(value: VecDeque<T>) -> Result<Self, Self::Error>

Performs the conversion.
","TryFrom>","fred::types::multiple::MultipleValues"],["
source§

impl TryFrom<i128> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: i128) -> Result<Self, Self::Error>

Performs the conversion.
","TryFrom","fred::types::multiple::MultipleValues"],["
source§

impl TryFrom<u128> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: u128) -> Result<Self, Self::Error>

Performs the conversion.
","TryFrom","fred::types::multiple::MultipleValues"],["
source§

impl TryFrom<u64> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: u64) -> Result<Self, Self::Error>

Performs the conversion.
","TryFrom","fred::types::multiple::MultipleValues"],["
source§

impl TryFrom<usize> for RedisValue

§

type Error = RedisError

The type returned in the event of a conversion error.
source§

fn try_from(d: usize) -> Result<Self, Self::Error>

Performs the conversion.
","TryFrom","fred::types::multiple::MultipleValues"],["
source§

impl Eq for RedisValue

","Eq","fred::types::multiple::MultipleValues"]]]]); + if (window.register_type_impls) { + window.register_type_impls(type_impls); + } else { + window.pending_type_impls = type_impls; + } +})() +//{"start":55,"fragment_lengths":[184202]} \ No newline at end of file diff --git a/doc/type.impl/fred/types/struct.MultipleKeys.js b/doc/type.impl/fred/types/struct.MultipleKeys.js new file mode 100644 index 00000000..8d1358ec --- /dev/null +++ b/doc/type.impl/fred/types/struct.MultipleKeys.js @@ -0,0 +1,9 @@ +(function() { + var type_impls = Object.fromEntries([["fred",[["
source§

impl Clone for MultipleKeys

source§

fn clone(&self) -> MultipleKeys

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
","Clone","fred::types::multiple::MultipleStrings"],["
source§

impl Debug for MultipleKeys

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
","Debug","fred::types::multiple::MultipleStrings"],["
source§

impl<'a, K, const N: usize> From<&'a [K; N]> for MultipleKeys
where\n K: Into<RedisKey> + Clone,

source§

fn from(value: &'a [K; N]) -> Self

Converts to this type from the input type.
","From<&'a [K; N]>","fred::types::multiple::MultipleStrings"],["
source§

impl From<()> for MultipleKeys

source§

fn from(_: ()) -> Self

Converts to this type from the input type.
","From<()>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>> From<(A0, A1)> for MultipleKeys

source§

fn from(value: (A0, A1)) -> Self

Converts to this type from the input type.
","From<(A0, A1)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>> From<(A0, A1, A2)> for MultipleKeys

source§

fn from(value: (A0, A1, A2)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>> From<(A0, A1, A2, A3)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>> From<(A0, A1, A2, A3, A4)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)> for MultipleKeys

source§

fn from(value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>, A13: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)> for MultipleKeys

source§

fn from(\n value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13),\n) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>, A13: Into<RedisKey>, A14: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)> for MultipleKeys

source§

fn from(\n value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14),\n) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>, A13: Into<RedisKey>, A14: Into<RedisKey>, A15: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)> for MultipleKeys

source§

fn from(\n value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15),\n) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)>","fred::types::multiple::MultipleStrings"],["
source§

impl<A0: Into<RedisKey>, A1: Into<RedisKey>, A2: Into<RedisKey>, A3: Into<RedisKey>, A4: Into<RedisKey>, A5: Into<RedisKey>, A6: Into<RedisKey>, A7: Into<RedisKey>, A8: Into<RedisKey>, A9: Into<RedisKey>, A10: Into<RedisKey>, A11: Into<RedisKey>, A12: Into<RedisKey>, A13: Into<RedisKey>, A14: Into<RedisKey>, A15: Into<RedisKey>, A16: Into<RedisKey>> From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16)> for MultipleKeys

source§

fn from(\n value: (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16),\n) -> Self

Converts to this type from the input type.
","From<(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16)>","fred::types::multiple::MultipleStrings"],["
source§

impl From<Option<RedisKey>> for MultipleKeys

source§

fn from(key: Option<RedisKey>) -> Self

Converts to this type from the input type.
","From>","fred::types::multiple::MultipleStrings"],["
source§

impl<T> From<T> for MultipleKeys
where\n T: Into<RedisKey>,

source§

fn from(d: T) -> Self

Converts to this type from the input type.
","From","fred::types::multiple::MultipleStrings"],["
source§

impl<T> From<Vec<T>> for MultipleKeys
where\n T: Into<RedisKey>,

source§

fn from(d: Vec<T>) -> Self

Converts to this type from the input type.
","From>","fred::types::multiple::MultipleStrings"],["
source§

impl<T> From<VecDeque<T>> for MultipleKeys
where\n T: Into<RedisKey>,

source§

fn from(d: VecDeque<T>) -> Self

Converts to this type from the input type.
","From>","fred::types::multiple::MultipleStrings"],["
source§

impl<T> FromIterator<T> for MultipleKeys
where\n T: Into<RedisKey>,

source§

fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self

Creates a value from an iterator. Read more
","FromIterator","fred::types::multiple::MultipleStrings"],["
source§

impl MultipleKeys

source

pub fn new() -> MultipleKeys

source

pub fn inner(self) -> Vec<RedisKey>

source

pub fn into_values(self) -> Vec<RedisValue>

source

pub fn len(&self) -> usize

",0,"fred::types::multiple::MultipleStrings"],["
source§

impl PartialEq for MultipleKeys

source§

fn eq(&self, other: &MultipleKeys) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient,\nand should not be overridden without very good reason.
","PartialEq","fred::types::multiple::MultipleStrings"],["
source§

impl Eq for MultipleKeys

","Eq","fred::types::multiple::MultipleStrings"],["
source§

impl StructuralPartialEq for MultipleKeys

","StructuralPartialEq","fred::types::multiple::MultipleStrings"]]]]); + if (window.register_type_impls) { + window.register_type_impls(type_impls); + } else { + window.pending_type_impls = type_impls; + } +})() +//{"start":55,"fragment_lengths":[87491]} \ No newline at end of file diff --git a/doc/type.impl/std/collections/hash/map/struct.HashMap.js b/doc/type.impl/std/collections/hash/map/struct.HashMap.js new file mode 100644 index 00000000..2b2339b4 --- /dev/null +++ b/doc/type.impl/std/collections/hash/map/struct.HashMap.js @@ -0,0 +1,9 @@ +(function() { + var type_impls = Object.fromEntries([["fred",[["
1.0.0 · source§

impl<K, V, S> Clone for HashMap<K, V, S>
where\n K: Clone,\n V: Clone,\n S: Clone,

source§

fn clone(&self) -> HashMap<K, V, S>

Returns a copy of the value. Read more
source§

fn clone_from(&mut self, source: &HashMap<K, V, S>)

Performs copy-assignment from source. Read more
","Clone","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
1.0.0 · source§

impl<K, V, S> Debug for HashMap<K, V, S>
where\n K: Debug,\n V: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
","Debug","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
1.0.0 · source§

impl<K, V, S> Default for HashMap<K, V, S>
where\n S: Default,

source§

fn default() -> HashMap<K, V, S>

Creates an empty HashMap<K, V, S>, with the Default value for the hasher.

\n
","Default","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
source§

impl<'de, K, V, S> Deserialize<'de> for HashMap<K, V, S>
where\n K: Deserialize<'de> + Eq + Hash,\n V: Deserialize<'de>,\n S: BuildHasher + Default,

source§

fn deserialize<D>(\n deserializer: D,\n) -> Result<HashMap<K, V, S>, <D as Deserializer<'de>>::Error>
where\n D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
","Deserialize<'de>","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
1.4.0 · source§

impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap<K, V, S>
where\n K: Eq + Hash + Copy,\n V: Copy,\n S: BuildHasher,

source§

fn extend<T>(&mut self, iter: T)
where\n T: IntoIterator<Item = (&'a K, &'a V)>,

Extends a collection with the contents of an iterator. Read more
source§

fn extend_one(&mut self, _: (&'a K, &'a V))

🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
source§

fn extend_reserve(&mut self, additional: usize)

🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
","Extend<(&'a K, &'a V)>","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
1.0.0 · source§

impl<K, V, S> Extend<(K, V)> for HashMap<K, V, S>
where\n K: Eq + Hash,\n S: BuildHasher,

Inserts all new key-values from the iterator and replaces values with existing\nkeys with new values returned from the iterator.

\n
source§

fn extend<T>(&mut self, iter: T)
where\n T: IntoIterator<Item = (K, V)>,

Extends a collection with the contents of an iterator. Read more
source§

fn extend_one(&mut self, _: (K, V))

🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
source§

fn extend_reserve(&mut self, additional: usize)

🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
","Extend<(K, V)>","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
1.56.0 · source§

impl<K, V, const N: usize> From<[(K, V); N]> for HashMap<K, V>
where\n K: Eq + Hash,

source§

fn from(arr: [(K, V); N]) -> HashMap<K, V>

§Examples
\n
use std::collections::HashMap;\n\nlet map1 = HashMap::from([(1, 2), (3, 4)]);\nlet map2: HashMap<_, _> = [(1, 2), (3, 4)].into();\nassert_eq!(map1, map2);
\n
","From<[(K, V); N]>","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
1.0.0 · source§

impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S>
where\n K: Eq + Hash,\n S: BuildHasher + Default,

source§

fn from_iter<T>(iter: T) -> HashMap<K, V, S>
where\n T: IntoIterator<Item = (K, V)>,

Creates a value from an iterator. Read more
","FromIterator<(K, V)>","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
source§

impl<K, V, S> FromRedis for HashMap<K, V, S>
where\n K: FromRedisKey + Eq + Hash,\n V: FromRedis,\n S: BuildHasher + Default,

","FromRedis","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
source§

impl<K, V> HashMap<K, V>

1.0.0 · source

pub fn new() -> HashMap<K, V>

Creates an empty HashMap.

\n

The hash map is initially created with a capacity of 0, so it will not allocate until it\nis first inserted into.

\n
§Examples
\n
use std::collections::HashMap;\nlet mut map: HashMap<&str, i32> = HashMap::new();
\n
1.0.0 · source

pub fn with_capacity(capacity: usize) -> HashMap<K, V>

Creates an empty HashMap with at least the specified capacity.

\n

The hash map will be able to hold at least capacity elements without\nreallocating. This method is allowed to allocate for more elements than\ncapacity. If capacity is 0, the hash map will not allocate.

\n
§Examples
\n
use std::collections::HashMap;\nlet mut map: HashMap<&str, i32> = HashMap::with_capacity(10);
\n
",0,"fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
source§

impl<K, V, S> HashMap<K, V, S>
where\n S: BuildHasher,

source

pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<'_, K, V, S>

🔬This is a nightly-only experimental API. (hash_raw_entry)

Creates a raw entry builder for the HashMap.

\n

Raw entries provide the lowest level of control for searching and\nmanipulating a map. They must be manually initialized with a hash and\nthen manually searched. After this, insertions into a vacant entry\nstill require an owned key to be provided.

\n

Raw entries are useful for such exotic situations as:

\n
    \n
  • Hash memoization
  • \n
  • Deferring the creation of an owned key until it is known to be required
  • \n
  • Using a search key that doesn’t work with the Borrow trait
  • \n
  • Using custom comparison logic without newtype wrappers
  • \n
\n

Because raw entries provide much more low-level control, it’s much easier\nto put the HashMap into an inconsistent state which, while memory-safe,\nwill cause the map to produce seemingly random results. Higher-level and\nmore foolproof APIs like entry should be preferred when possible.

\n

In particular, the hash used to initialize the raw entry must still be\nconsistent with the hash of the key that is ultimately stored in the entry.\nThis is because implementations of HashMap may need to recompute hashes\nwhen resizing, at which point only the keys are available.

\n

Raw entries give mutable access to the keys. This must not be used\nto modify how the key would compare or hash, as the map will not re-evaluate\nwhere the key should go, meaning the keys may become “lost” if their\nlocation does not reflect their state. For instance, if you change a key\nso that the map now contains keys which compare equal, search may start\nacting erratically, with two keys randomly masking each other. Implementations\nare free to assume this doesn’t happen (within the limits of memory-safety).

\n
source

pub fn raw_entry(&self) -> RawEntryBuilder<'_, K, V, S>

🔬This is a nightly-only experimental API. (hash_raw_entry)

Creates a raw immutable entry builder for the HashMap.

\n

Raw entries provide the lowest level of control for searching and\nmanipulating a map. They must be manually initialized with a hash and\nthen manually searched.

\n

This is useful for

\n
    \n
  • Hash memoization
  • \n
  • Using a search key that doesn’t work with the Borrow trait
  • \n
  • Using custom comparison logic without newtype wrappers
  • \n
\n

Unless you are in such a situation, higher-level and more foolproof APIs like\nget should be preferred.

\n

Immutable raw entries have very limited use; you might instead want raw_entry_mut.

\n
",0,"fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
source§

impl<K, V, S> HashMap<K, V, S>

1.7.0 (const: unstable) · source

pub fn with_hasher(hash_builder: S) -> HashMap<K, V, S>

Creates an empty HashMap which will use the given hash builder to hash\nkeys.

\n

The created map has the default initial capacity.

\n

Warning: hash_builder is normally randomly generated, and\nis designed to allow HashMaps to be resistant to attacks that\ncause many collisions and very poor performance. Setting it\nmanually using this function can expose a DoS attack vector.

\n

The hash_builder passed should implement the BuildHasher trait for\nthe HashMap to be useful, see its documentation for details.

\n
§Examples
\n
use std::collections::HashMap;\nuse std::hash::RandomState;\n\nlet s = RandomState::new();\nlet mut map = HashMap::with_hasher(s);\nmap.insert(1, 2);
\n
1.7.0 · source

pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashMap<K, V, S>

Creates an empty HashMap with at least the specified capacity, using\nhasher to hash the keys.

\n

The hash map will be able to hold at least capacity elements without\nreallocating. This method is allowed to allocate for more elements than\ncapacity. If capacity is 0, the hash map will not allocate.

\n

Warning: hasher is normally randomly generated, and\nis designed to allow HashMaps to be resistant to attacks that\ncause many collisions and very poor performance. Setting it\nmanually using this function can expose a DoS attack vector.

\n

The hasher passed should implement the BuildHasher trait for\nthe HashMap to be useful, see its documentation for details.

\n
§Examples
\n
use std::collections::HashMap;\nuse std::hash::RandomState;\n\nlet s = RandomState::new();\nlet mut map = HashMap::with_capacity_and_hasher(10, s);\nmap.insert(1, 2);
\n
1.0.0 · source

pub fn capacity(&self) -> usize

Returns the number of elements the map can hold without reallocating.

\n

This number is a lower bound; the HashMap<K, V> might be able to hold\nmore, but is guaranteed to be able to hold at least this many.

\n
§Examples
\n
use std::collections::HashMap;\nlet map: HashMap<i32, i32> = HashMap::with_capacity(100);\nassert!(map.capacity() >= 100);
\n
1.0.0 · source

pub fn keys(&self) -> Keys<'_, K, V>

An iterator visiting all keys in arbitrary order.\nThe iterator element type is &'a K.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet map = HashMap::from([\n    (\"a\", 1),\n    (\"b\", 2),\n    (\"c\", 3),\n]);\n\nfor key in map.keys() {\n    println!(\"{key}\");\n}
\n
§Performance
\n

In the current implementation, iterating over keys takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.54.0 · source

pub fn into_keys(self) -> IntoKeys<K, V>

Creates a consuming iterator visiting all the keys in arbitrary order.\nThe map cannot be used after calling this.\nThe iterator element type is K.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet map = HashMap::from([\n    (\"a\", 1),\n    (\"b\", 2),\n    (\"c\", 3),\n]);\n\nlet mut vec: Vec<&str> = map.into_keys().collect();\n// The `IntoKeys` iterator produces keys in arbitrary order, so the\n// keys must be sorted to test them against a sorted array.\nvec.sort_unstable();\nassert_eq!(vec, [\"a\", \"b\", \"c\"]);
\n
§Performance
\n

In the current implementation, iterating over keys takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.0.0 · source

pub fn values(&self) -> Values<'_, K, V>

An iterator visiting all values in arbitrary order.\nThe iterator element type is &'a V.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet map = HashMap::from([\n    (\"a\", 1),\n    (\"b\", 2),\n    (\"c\", 3),\n]);\n\nfor val in map.values() {\n    println!(\"{val}\");\n}
\n
§Performance
\n

In the current implementation, iterating over values takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.10.0 · source

pub fn values_mut(&mut self) -> ValuesMut<'_, K, V>

An iterator visiting all values mutably in arbitrary order.\nThe iterator element type is &'a mut V.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::from([\n    (\"a\", 1),\n    (\"b\", 2),\n    (\"c\", 3),\n]);\n\nfor val in map.values_mut() {\n    *val = *val + 10;\n}\n\nfor val in map.values() {\n    println!(\"{val}\");\n}
\n
§Performance
\n

In the current implementation, iterating over values takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.54.0 · source

pub fn into_values(self) -> IntoValues<K, V>

Creates a consuming iterator visiting all the values in arbitrary order.\nThe map cannot be used after calling this.\nThe iterator element type is V.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet map = HashMap::from([\n    (\"a\", 1),\n    (\"b\", 2),\n    (\"c\", 3),\n]);\n\nlet mut vec: Vec<i32> = map.into_values().collect();\n// The `IntoValues` iterator produces values in arbitrary order, so\n// the values must be sorted to test them against a sorted array.\nvec.sort_unstable();\nassert_eq!(vec, [1, 2, 3]);
\n
§Performance
\n

In the current implementation, iterating over values takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.0.0 · source

pub fn iter(&self) -> Iter<'_, K, V>

An iterator visiting all key-value pairs in arbitrary order.\nThe iterator element type is (&'a K, &'a V).

\n
§Examples
\n
use std::collections::HashMap;\n\nlet map = HashMap::from([\n    (\"a\", 1),\n    (\"b\", 2),\n    (\"c\", 3),\n]);\n\nfor (key, val) in map.iter() {\n    println!(\"key: {key} val: {val}\");\n}
\n
§Performance
\n

In the current implementation, iterating over map takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.0.0 · source

pub fn iter_mut(&mut self) -> IterMut<'_, K, V>

An iterator visiting all key-value pairs in arbitrary order,\nwith mutable references to the values.\nThe iterator element type is (&'a K, &'a mut V).

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::from([\n    (\"a\", 1),\n    (\"b\", 2),\n    (\"c\", 3),\n]);\n\n// Update all values\nfor (_, val) in map.iter_mut() {\n    *val *= 2;\n}\n\nfor (key, val) in &map {\n    println!(\"key: {key} val: {val}\");\n}
\n
§Performance
\n

In the current implementation, iterating over map takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.0.0 · source

pub fn len(&self) -> usize

Returns the number of elements in the map.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut a = HashMap::new();\nassert_eq!(a.len(), 0);\na.insert(1, \"a\");\nassert_eq!(a.len(), 1);
\n
1.0.0 · source

pub fn is_empty(&self) -> bool

Returns true if the map contains no elements.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut a = HashMap::new();\nassert!(a.is_empty());\na.insert(1, \"a\");\nassert!(!a.is_empty());
\n
1.6.0 · source

pub fn drain(&mut self) -> Drain<'_, K, V>

Clears the map, returning all key-value pairs as an iterator. Keeps the\nallocated memory for reuse.

\n

If the returned iterator is dropped before being fully consumed, it\ndrops the remaining key-value pairs. The returned iterator keeps a\nmutable borrow on the map to optimize its implementation.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut a = HashMap::new();\na.insert(1, \"a\");\na.insert(2, \"b\");\n\nfor (k, v) in a.drain().take(1) {\n    assert!(k == 1 || k == 2);\n    assert!(v == \"a\" || v == \"b\");\n}\n\nassert!(a.is_empty());
\n
source

pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, K, V, F>
where\n F: FnMut(&K, &mut V) -> bool,

🔬This is a nightly-only experimental API. (hash_extract_if)

Creates an iterator which uses a closure to determine if an element should be removed.

\n

If the closure returns true, the element is removed from the map and yielded.\nIf the closure returns false, or panics, the element remains in the map and will not be\nyielded.

\n

Note that extract_if lets you mutate every value in the filter closure, regardless of\nwhether you choose to keep or remove it.

\n

If the returned ExtractIf is not exhausted, e.g. because it is dropped without iterating\nor the iteration short-circuits, then the remaining elements will be retained.\nUse retain with a negated predicate if you do not need the returned iterator.

\n
§Examples
\n

Splitting a map into even and odd keys, reusing the original map:

\n\n
#![feature(hash_extract_if)]\nuse std::collections::HashMap;\n\nlet mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x)).collect();\nlet extracted: HashMap<i32, i32> = map.extract_if(|k, _v| k % 2 == 0).collect();\n\nlet mut evens = extracted.keys().copied().collect::<Vec<_>>();\nlet mut odds = map.keys().copied().collect::<Vec<_>>();\nevens.sort();\nodds.sort();\n\nassert_eq!(evens, vec![0, 2, 4, 6]);\nassert_eq!(odds, vec![1, 3, 5, 7]);
\n
1.18.0 · source

pub fn retain<F>(&mut self, f: F)
where\n F: FnMut(&K, &mut V) -> bool,

Retains only the elements specified by the predicate.

\n

In other words, remove all pairs (k, v) for which f(&k, &mut v) returns false.\nThe elements are visited in unsorted (and unspecified) order.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x*10)).collect();\nmap.retain(|&k, _| k % 2 == 0);\nassert_eq!(map.len(), 4);
\n
§Performance
\n

In the current implementation, this operation takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.0.0 · source

pub fn clear(&mut self)

Clears the map, removing all key-value pairs. Keeps the allocated memory\nfor reuse.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut a = HashMap::new();\na.insert(1, \"a\");\na.clear();\nassert!(a.is_empty());
\n
1.9.0 · source

pub fn hasher(&self) -> &S

Returns a reference to the map’s BuildHasher.

\n
§Examples
\n
use std::collections::HashMap;\nuse std::hash::RandomState;\n\nlet hasher = RandomState::new();\nlet map: HashMap<i32, i32> = HashMap::with_hasher(hasher);\nlet hasher: &RandomState = map.hasher();
\n
",0,"fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
source§

impl<K, V, S> HashMap<K, V, S>
where\n K: Eq + Hash,\n S: BuildHasher,

1.0.0 · source

pub fn reserve(&mut self, additional: usize)

Reserves capacity for at least additional more elements to be inserted\nin the HashMap. The collection may reserve more space to speculatively\navoid frequent reallocations. After calling reserve,\ncapacity will be greater than or equal to self.len() + additional.\nDoes nothing if capacity is already sufficient.

\n
§Panics
\n

Panics if the new allocation size overflows usize.

\n
§Examples
\n
use std::collections::HashMap;\nlet mut map: HashMap<&str, i32> = HashMap::new();\nmap.reserve(10);
\n
1.57.0 · source

pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>

Tries to reserve capacity for at least additional more elements to be inserted\nin the HashMap. The collection may reserve more space to speculatively\navoid frequent reallocations. After calling try_reserve,\ncapacity will be greater than or equal to self.len() + additional if\nit returns Ok(()).\nDoes nothing if capacity is already sufficient.

\n
§Errors
\n

If the capacity overflows, or the allocator reports a failure, then an error\nis returned.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut map: HashMap<&str, isize> = HashMap::new();\nmap.try_reserve(10).expect(\"why is the test harness OOMing on a handful of bytes?\");
\n
1.0.0 · source

pub fn shrink_to_fit(&mut self)

Shrinks the capacity of the map as much as possible. It will drop\ndown as much as possible while maintaining the internal rules\nand possibly leaving some space in accordance with the resize policy.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut map: HashMap<i32, i32> = HashMap::with_capacity(100);\nmap.insert(1, 2);\nmap.insert(3, 4);\nassert!(map.capacity() >= 100);\nmap.shrink_to_fit();\nassert!(map.capacity() >= 2);
\n
1.56.0 · source

pub fn shrink_to(&mut self, min_capacity: usize)

Shrinks the capacity of the map with a lower limit. It will drop\ndown no lower than the supplied limit while maintaining the internal rules\nand possibly leaving some space in accordance with the resize policy.

\n

If the current capacity is less than the lower limit, this is a no-op.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut map: HashMap<i32, i32> = HashMap::with_capacity(100);\nmap.insert(1, 2);\nmap.insert(3, 4);\nassert!(map.capacity() >= 100);\nmap.shrink_to(10);\nassert!(map.capacity() >= 10);\nmap.shrink_to(0);\nassert!(map.capacity() >= 2);
\n
1.0.0 · source

pub fn entry(&mut self, key: K) -> Entry<'_, K, V>

Gets the given key’s corresponding entry in the map for in-place manipulation.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut letters = HashMap::new();\n\nfor ch in \"a short treatise on fungi\".chars() {\n    letters.entry(ch).and_modify(|counter| *counter += 1).or_insert(1);\n}\n\nassert_eq!(letters[&'s'], 2);\nassert_eq!(letters[&'t'], 3);\nassert_eq!(letters[&'u'], 1);\nassert_eq!(letters.get(&'y'), None);
\n
1.0.0 · source

pub fn get<Q>(&self, k: &Q) -> Option<&V>
where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

Returns a reference to the value corresponding to the key.

\n

The key may be any borrowed form of the map’s key type, but\nHash and Eq on the borrowed form must match those for\nthe key type.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, \"a\");\nassert_eq!(map.get(&1), Some(&\"a\"));\nassert_eq!(map.get(&2), None);
\n
1.40.0 · source

pub fn get_key_value<Q>(&self, k: &Q) -> Option<(&K, &V)>
where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

Returns the key-value pair corresponding to the supplied key.

\n

The supplied key may be any borrowed form of the map’s key type, but\nHash and Eq on the borrowed form must match those for\nthe key type.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, \"a\");\nassert_eq!(map.get_key_value(&1), Some((&1, &\"a\")));\nassert_eq!(map.get_key_value(&2), None);
\n
source

pub fn get_many_mut<Q, const N: usize>(\n &mut self,\n ks: [&Q; N],\n) -> Option<[&mut V; N]>
where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

🔬This is a nightly-only experimental API. (map_many_mut)

Attempts to get mutable references to N values in the map at once.

\n

Returns an array of length N with the results of each query. For soundness, at most one\nmutable reference will be returned to any value. None will be returned if any of the\nkeys are duplicates or missing.

\n
§Examples
\n
#![feature(map_many_mut)]\nuse std::collections::HashMap;\n\nlet mut libraries = HashMap::new();\nlibraries.insert(\"Bodleian Library\".to_string(), 1602);\nlibraries.insert(\"Athenæum\".to_string(), 1807);\nlibraries.insert(\"Herzogin-Anna-Amalia-Bibliothek\".to_string(), 1691);\nlibraries.insert(\"Library of Congress\".to_string(), 1800);\n\nlet got = libraries.get_many_mut([\n    \"Athenæum\",\n    \"Library of Congress\",\n]);\nassert_eq!(\n    got,\n    Some([\n        &mut 1807,\n        &mut 1800,\n    ]),\n);\n\n// Missing keys result in None\nlet got = libraries.get_many_mut([\n    \"Athenæum\",\n    \"New York Public Library\",\n]);\nassert_eq!(got, None);\n\n// Duplicate keys result in None\nlet got = libraries.get_many_mut([\n    \"Athenæum\",\n    \"Athenæum\",\n]);\nassert_eq!(got, None);
\n
source

pub unsafe fn get_many_unchecked_mut<Q, const N: usize>(\n &mut self,\n ks: [&Q; N],\n) -> Option<[&mut V; N]>
where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

🔬This is a nightly-only experimental API. (map_many_mut)

Attempts to get mutable references to N values in the map at once, without validating that\nthe values are unique.

\n

Returns an array of length N with the results of each query. None will be returned if\nany of the keys are missing.

\n

For a safe alternative see get_many_mut.

\n
§Safety
\n

Calling this method with overlapping keys is undefined behavior even if the resulting\nreferences are not used.

\n
§Examples
\n
#![feature(map_many_mut)]\nuse std::collections::HashMap;\n\nlet mut libraries = HashMap::new();\nlibraries.insert(\"Bodleian Library\".to_string(), 1602);\nlibraries.insert(\"Athenæum\".to_string(), 1807);\nlibraries.insert(\"Herzogin-Anna-Amalia-Bibliothek\".to_string(), 1691);\nlibraries.insert(\"Library of Congress\".to_string(), 1800);\n\nlet got = libraries.get_many_mut([\n    \"Athenæum\",\n    \"Library of Congress\",\n]);\nassert_eq!(\n    got,\n    Some([\n        &mut 1807,\n        &mut 1800,\n    ]),\n);\n\n// Missing keys result in None\nlet got = libraries.get_many_mut([\n    \"Athenæum\",\n    \"New York Public Library\",\n]);\nassert_eq!(got, None);
\n
1.0.0 · source

pub fn contains_key<Q>(&self, k: &Q) -> bool
where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

Returns true if the map contains a value for the specified key.

\n

The key may be any borrowed form of the map’s key type, but\nHash and Eq on the borrowed form must match those for\nthe key type.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, \"a\");\nassert_eq!(map.contains_key(&1), true);\nassert_eq!(map.contains_key(&2), false);
\n
1.0.0 · source

pub fn get_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

Returns a mutable reference to the value corresponding to the key.

\n

The key may be any borrowed form of the map’s key type, but\nHash and Eq on the borrowed form must match those for\nthe key type.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, \"a\");\nif let Some(x) = map.get_mut(&1) {\n    *x = \"b\";\n}\nassert_eq!(map[&1], \"b\");
\n
1.0.0 · source

pub fn insert(&mut self, k: K, v: V) -> Option<V>

Inserts a key-value pair into the map.

\n

If the map did not have this key present, None is returned.

\n

If the map did have this key present, the value is updated, and the old\nvalue is returned. The key is not updated, though; this matters for\ntypes that can be == without being identical. See the module-level\ndocumentation for more.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nassert_eq!(map.insert(37, \"a\"), None);\nassert_eq!(map.is_empty(), false);\n\nmap.insert(37, \"b\");\nassert_eq!(map.insert(37, \"c\"), Some(\"b\"));\nassert_eq!(map[&37], \"c\");
\n
source

pub fn try_insert(\n &mut self,\n key: K,\n value: V,\n) -> Result<&mut V, OccupiedError<'_, K, V>>

🔬This is a nightly-only experimental API. (map_try_insert)

Tries to insert a key-value pair into the map, and returns\na mutable reference to the value in the entry.

\n

If the map already had this key present, nothing is updated, and\nan error containing the occupied entry and the value is returned.

\n
§Examples
\n

Basic usage:

\n\n
#![feature(map_try_insert)]\n\nuse std::collections::HashMap;\n\nlet mut map = HashMap::new();\nassert_eq!(map.try_insert(37, \"a\").unwrap(), &\"a\");\n\nlet err = map.try_insert(37, \"b\").unwrap_err();\nassert_eq!(err.entry.key(), &37);\nassert_eq!(err.entry.get(), &\"a\");\nassert_eq!(err.value, \"b\");
\n
1.0.0 · source

pub fn remove<Q>(&mut self, k: &Q) -> Option<V>
where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

Removes a key from the map, returning the value at the key if the key\nwas previously in the map.

\n

The key may be any borrowed form of the map’s key type, but\nHash and Eq on the borrowed form must match those for\nthe key type.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, \"a\");\nassert_eq!(map.remove(&1), Some(\"a\"));\nassert_eq!(map.remove(&1), None);
\n
1.27.0 · source

pub fn remove_entry<Q>(&mut self, k: &Q) -> Option<(K, V)>
where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

Removes a key from the map, returning the stored key and value if the\nkey was previously in the map.

\n

The key may be any borrowed form of the map’s key type, but\nHash and Eq on the borrowed form must match those for\nthe key type.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, \"a\");\nassert_eq!(map.remove_entry(&1), Some((1, \"a\")));\nassert_eq!(map.remove(&1), None);
\n
",0,"fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
1.0.0 · source§

impl<K, Q, V, S> Index<&Q> for HashMap<K, V, S>
where\n K: Eq + Hash + Borrow<Q>,\n Q: Eq + Hash + ?Sized,\n S: BuildHasher,

source§

fn index(&self, key: &Q) -> &V

Returns a reference to the value corresponding to the supplied key.

\n
§Panics
\n

Panics if the key is not present in the HashMap.

\n
§

type Output = V

The returned type after indexing.
","Index<&Q>","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
source§

impl<'de, K, V, S, E> IntoDeserializer<'de, E> for HashMap<K, V, S>
where\n K: IntoDeserializer<'de, E> + Eq + Hash,\n V: IntoDeserializer<'de, E>,\n S: BuildHasher,\n E: Error,

§

type Deserializer = MapDeserializer<'de, <HashMap<K, V, S> as IntoIterator>::IntoIter, E>

The type of the deserializer being converted into.
source§

fn into_deserializer(\n self,\n) -> <HashMap<K, V, S> as IntoDeserializer<'de, E>>::Deserializer

Convert this value into a deserializer.
","IntoDeserializer<'de, E>","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
1.0.0 · source§

impl<K, V, S> IntoIterator for HashMap<K, V, S>

source§

fn into_iter(self) -> IntoIter<K, V>

Creates a consuming iterator, that is, one that moves each key-value\npair out of the map in arbitrary order. The map cannot be used after\ncalling this.

\n
§Examples
\n
use std::collections::HashMap;\n\nlet map = HashMap::from([\n    (\"a\", 1),\n    (\"b\", 2),\n    (\"c\", 3),\n]);\n\n// Not possible with .iter()\nlet vec: Vec<(&str, i32)> = map.into_iter().collect();
\n
§

type Item = (K, V)

The type of the elements being iterated over.
§

type IntoIter = IntoIter<K, V>

Which kind of iterator are we turning this into?
","IntoIterator","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
1.0.0 · source§

impl<K, V, S> PartialEq for HashMap<K, V, S>
where\n K: Eq + Hash,\n V: PartialEq,\n S: BuildHasher,

source§

fn eq(&self, other: &HashMap<K, V, S>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient,\nand should not be overridden without very good reason.
","PartialEq","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
source§

impl<K, V, H> Serialize for HashMap<K, V, H>
where\n K: Serialize,\n V: Serialize,

source§

fn serialize<S>(\n &self,\n serializer: S,\n) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where\n S: Serializer,

Serialize this value into the given Serde serializer. Read more
","Serialize","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
1.0.0 · source§

impl<K, V, S> Eq for HashMap<K, V, S>
where\n K: Eq + Hash,\n V: Eq,\n S: BuildHasher,

","Eq","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"],["
1.36.0 · source§

impl<K, V, S> UnwindSafe for HashMap<K, V, S>
where\n K: UnwindSafe,\n V: UnwindSafe,\n S: UnwindSafe,

","UnwindSafe","fred::types::streams::XReadResponse","fred::types::timeseries::Resp3TimeSeriesValues"]]]]); + if (window.register_type_impls) { + window.register_type_impls(type_impls); + } else { + window.pending_type_impls = type_impls; + } +})() +//{"start":55,"fragment_lengths":[134612]} \ No newline at end of file diff --git a/doc/type.impl/std/primitive.bool.js b/doc/type.impl/std/primitive.bool.js new file mode 100644 index 00000000..34d921e9 --- /dev/null +++ b/doc/type.impl/std/primitive.bool.js @@ -0,0 +1,9 @@ +(function() { + var type_impls = Object.fromEntries([["fred",[["
source§

impl FromRedis for bool

","FromRedis","fred::types::Any"]]]]); + if (window.register_type_impls) { + window.register_type_impls(type_impls); + } else { + window.pending_type_impls = type_impls; + } +})() +//{"start":55,"fragment_lengths":[1357]} \ No newline at end of file diff --git a/doc/type.impl/std/primitive.tuple.js b/doc/type.impl/std/primitive.tuple.js new file mode 100644 index 00000000..34ab0a8c --- /dev/null +++ b/doc/type.impl/std/primitive.tuple.js @@ -0,0 +1,9 @@ +(function() { + var type_impls = Object.fromEntries([["fred",[]]]); + if (window.register_type_impls) { + window.register_type_impls(type_impls); + } else { + window.pending_type_impls = type_impls; + } +})() +//{"start":55,"fragment_lengths":[11]} \ No newline at end of file diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 71d836fe..00000000 --- a/examples/README.md +++ /dev/null @@ -1,30 +0,0 @@ -Examples -======== - -* [Basic](./basic.rs) - Basic client usage. -* [Axum](./axum.rs) - Use a client pool with [Axum](https://crates.io/crates/axum). -* [TLS](./tls.rs) - Setting up a client that uses TLS. -* [Publish-Subscribe](./pubsub.rs) - Use multiple clients together with the pubsub interface in a way that survives - network interruptions. -* [Blocking](./blocking.rs) - Use multiple clients with the blocking list interface. -* [Transactions](./transactions.rs) - Use the MULTI/EXEC interface on a client. -* [Pipeline](./pipeline.rs) - Use the manual pipeline interface. -* [Streams](./streams.rs) - Use `XADD` and `XREAD` to communicate between tasks. -* [Lua](./lua.rs) - Use the Lua scripting interface on a client. -* [Scan](./scan.rs) - Use the SCAN interface to scan and read keys. -* [Pool](./pool.rs) - Use a round-robin client pool. -* [Monitor](./monitor.rs) - Process a `MONITOR` stream. -* [Sentinel](./sentinel.rs) - Connect using a sentinel deployment. -* [Serde JSON](./serde_json.rs) - Use the `serde-json` feature to convert between Redis types and JSON. -* [Redis JSON](./redis_json.rs) - Use the `i-redis-json` feature with `serde-json` types. -* [Custom](./custom.rs) - Send custom commands or operate on RESP frames. -* [DNS](./dns.rs) - Customize the DNS resolution logic. -* [Client Tracking](./client_tracking.rs) - - Implement [client side caching](https://redis.io/docs/manual/client-side-caching/). -* [Events](./events.rs) - Respond to connection events with the `EventsInterface`. -* [Keyspace Notifications](./keyspace.rs) - Use - the [keyspace notifications](https://redis.io/docs/manual/keyspace-notifications/) interface. -* [Misc](./misc.rs) - Miscellaneous or advanced features. -* [Replicas](./replicas.rs) - Interact with cluster replica nodes via a `RedisPool`. - -Or see the [tests](../tests/integration) for more examples. \ No newline at end of file diff --git a/examples/axum.rs b/examples/axum.rs deleted file mode 100644 index 8d9f5a2d..00000000 --- a/examples/axum.rs +++ /dev/null @@ -1,129 +0,0 @@ -use axum::{ - body::Body, - extract::{Path, State}, - response::{IntoResponse, Response}, - routing::{get, post}, - Router, -}; -use bytes::Bytes; -use fred::{clients::RedisPool, prelude::*}; -use log::{debug, info}; -use std::{env, str, time::Duration}; -use tokio::net::TcpListener; - -#[derive(Clone)] -struct AppState { - // all client types are cheaply cloneable - pub pool: RedisPool, -} - -#[tokio::main] -async fn main() { - pretty_env_logger::init(); - - let pool_size = env::var("REDIS_POOL_SIZE") - .ok() - .and_then(|v| v.parse::().ok()) - .unwrap_or(8); - let config = - RedisConfig::from_url("redis://foo:bar@127.0.0.1:6379").expect("Failed to create redis config from url"); - let pool = Builder::from_config(config) - .with_connection_config(|config| { - config.connection_timeout = Duration::from_secs(10); - }) - // use exponential backoff, starting at 100 ms and doubling on each failed attempt up to 30 sec - .set_policy(ReconnectPolicy::new_exponential(0, 100, 30_000, 2)) - .build_pool(pool_size) - .expect("Failed to create redis pool"); - - pool.init().await.expect("Failed to connect to redis"); - info!("Connected to Redis"); - - let app = Router::new() - .route("/:key", get(get_kv).post(set_kv).delete(del_kv)) - .route("/:key/incr", post(incr_kv)) - .with_state(AppState { pool }); - - let listener = TcpListener::bind("127.0.0.1:3000") - .await - .expect("Failed to bind to port"); - info!("Starting server..."); - axum::serve(listener, app).await.unwrap(); -} - -fn map_error(err: RedisError) -> (u16, Body) { - let details: Body = err.details().to_string().into(); - let code = if *err.kind() == RedisErrorKind::NotFound { - 404 - } else if err.details().starts_with("WRONGTYPE") { - 400 - } else { - 500 - }; - - (code, details) -} - -async fn get_kv(Path(key): Path, State(state): State) -> impl IntoResponse { - debug!("get {}", key); - - let (code, val) = match state.pool.get::, _>(key).await { - Ok(Some(val)) => (200, val.into()), - Ok(None) => (404, "Not found".into()), - Err(err) => map_error(err), - }; - Response::builder().status(code).body(val).unwrap() -} - -async fn set_kv(Path(key): Path, State(state): State, body: Bytes) -> impl IntoResponse { - debug!("set {} {}", key, String::from_utf8_lossy(&body)); - - let (code, val) = match state.pool.set::(key, body, None, None, false).await { - Ok(val) => (200, val.into()), - Err(err) => map_error(err), - }; - Response::builder().status(code).body(val).unwrap() -} - -async fn del_kv(Path(key): Path, State(state): State) -> impl IntoResponse { - debug!("del {}", key); - - let (code, val) = match state.pool.del::(key).await { - Ok(0) => (404, "Not Found.".into()), - Ok(val) => (200, val.to_string().into()), - Err(err) => map_error(err), - }; - Response::builder().status(code).body(val).unwrap() -} - -async fn incr_kv(Path(key): Path, State(state): State, body: Bytes) -> impl IntoResponse { - let count = str::from_utf8(&body) - .ok() - .and_then(|s| s.parse::().ok()) - .unwrap_or(1); - debug!("incr {} by {}", key, count); - - let (code, val) = match state.pool.incr_by::(key, count).await { - Ok(val) => (200, val.to_string().into()), - Err(err) => map_error(err), - }; - Response::builder().status(code).body(val).unwrap() -} - -// example usage with curl: -// $ curl http://localhost:3000/foo -// Not found -// $ curl -X POST -d '100' http://localhost:3000/foo -// OK -// $ curl -X POST -d '50' http://localhost:3000/foo/incr -// 150 -// $ curl -X POST -d '50' http://localhost:3000/foo/incr -// 200 -// $ curl -X POST -d '50' http://localhost:3000/foo/incr -// 250 -// $ curl http://localhost:3000/foo -// 250 -// $ curl -X DELETE http://localhost:3000/foo -// 1 -// $ curl http://localhost:3000/foo -// Not found diff --git a/examples/basic.rs b/examples/basic.rs deleted file mode 100644 index 5b7c90b3..00000000 --- a/examples/basic.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::prelude::*; - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - // create a config from a URL - let config = RedisConfig::from_url("redis://username:password@foo.com:6379/1")?; - // see the `Builder` interface for more information - let client = Builder::from_config(config).build()?; - // callers can manage the tokio task driving the connections - let _connection_task = client.init().await?; - // convert response types to most common rust types - let foo: Option = client.get("foo").await?; - println!("Foo: {:?}", foo); - - client - .set("foo", "bar", Some(Expiration::EX(1)), Some(SetOptions::NX), false) - .await?; - - // or use turbofish. the first type is always the response type. - println!("Foo: {:?}", client.get::, _>("foo").await?); - - client.quit().await?; - Ok(()) -} diff --git a/examples/blocking.rs b/examples/blocking.rs deleted file mode 100644 index f5399690..00000000 --- a/examples/blocking.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::prelude::*; -use std::time::Duration; -use tokio::time::sleep; - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - pretty_env_logger::init(); - - let publisher_client = RedisClient::default(); - let subscriber_client = RedisClient::default(); - publisher_client.init().await?; - subscriber_client.init().await?; - - let _subscriber_jh = tokio::spawn(async move { - loop { - let (key, value): (String, i64) = match subscriber_client.blpop("foo", 5.0).await.ok() { - Some(value) => value, - None => continue, - }; - - println!("BLPOP result on {}: {}", key, value); - } - }); - - for idx in 0 .. 30 { - publisher_client.rpush("foo", idx).await?; - sleep(Duration::from_secs(1)).await; - } - - Ok(()) -} diff --git a/examples/client_tracking.rs b/examples/client_tracking.rs deleted file mode 100644 index 27c6995e..00000000 --- a/examples/client_tracking.rs +++ /dev/null @@ -1,99 +0,0 @@ -#![allow(clippy::disallowed_names)] - -use fred::{interfaces::TrackingInterface, prelude::*, types::RespVersion}; - -// this library supports 2 interfaces for implementing client-side caching - a high level `TrackingInterface` trait -// that requires RESP3 and works with all deployment types, and a lower level interface that directly exposes the -// `CLIENT TRACKING` commands but often requires a centralized server config. - -async fn resp3_tracking_interface_example() -> Result<(), RedisError> { - let client = Builder::default_centralized() - .with_config(|config| { - config.version = RespVersion::RESP3; - }) - .build()?; - client.init().await?; - - // spawn a task that processes invalidation messages. - let _invalidate_task = client.on_invalidation(|invalidation| { - println!("{}: Invalidate {:?}", invalidation.server, invalidation.keys); - Ok(()) - }); - - // enable client tracking on all connections. it's usually a good idea to do this in an `on_reconnect` block. - client.start_tracking(None, false, false, false, false).await?; - client.get("foo").await?; - - // send `CLIENT CACHING yes|no` before subsequent commands. the preceding `CLIENT CACHING yes|no` command will be - // sent when the command is retried as well. - let foo: i64 = client - .with_options(&Options { - caching: Some(true), - ..Default::default() - }) - .incr("foo") - .await?; - let bar: i64 = client - .with_options(&Options { - caching: Some(false), - ..Default::default() - }) - .incr("bar") - .await?; - - println!("foo: {}, bar: {}", foo, bar); - client.stop_tracking().await?; - Ok(()) -} - -async fn resp2_basic_interface_example() -> Result<(), RedisError> { - let subscriber = RedisClient::default(); - let client = subscriber.clone_new(); - - // RESP2 requires two connections - subscriber.init().await?; - client.init().await?; - - // the invalidation subscriber interface is the same as above even in RESP2 mode **as long as the `client-tracking` - // feature is enabled**. if the feature is disabled then the message will appear on the `on_message` receiver. - let mut invalidations = subscriber.invalidation_rx(); - let _invalidate_task = tokio::spawn(async move { - while let Ok(invalidation) = invalidations.recv().await { - println!("{}: Invalidate {:?}", invalidation.server, invalidation.keys); - } - }); - // in RESP2 mode we must manually subscribe to the invalidation channel. the `start_tracking` function does this - // automatically with the RESP3 interface. - subscriber.subscribe("__redis__:invalidate").await?; - - // enable client tracking, sending invalidation messages to the subscriber client - let (_, connection_id) = subscriber - .connection_ids() - .await - .into_iter() - .next() - .expect("Failed to read subscriber connection ID"); - client - .client_tracking("on", Some(connection_id), None, false, false, false, false) - .await?; - - println!("Tracking info: {:?}", client.client_trackinginfo::().await?); - println!("Redirection: {}", client.client_getredir::().await?); - - let pipeline = client.pipeline(); - // it's recommended to pipeline `CLIENT CACHING yes|no` if the client is used across multiple tasks - pipeline.client_caching(true).await?; - pipeline.incr("foo").await?; - println!("Foo: {}", pipeline.last::().await?); - - Ok(()) -} - -#[tokio::main] -// see https://redis.io/docs/manual/client-side-caching/ -async fn main() -> Result<(), RedisError> { - resp3_tracking_interface_example().await?; - resp2_basic_interface_example().await?; - - Ok(()) -} diff --git a/examples/custom.rs b/examples/custom.rs deleted file mode 100644 index 13fabac7..00000000 --- a/examples/custom.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::{ - cmd, - prelude::*, - types::{ClusterHash, CustomCommand}, -}; -use std::convert::TryInto; - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let client = Builder::default_centralized().build()?; - client.init().await?; - client.lpush("foo", vec![1, 2, 3]).await?; - - let result: Vec = client.custom(cmd!("LRANGE"), vec!["foo", "0", "3"]).await?; - println!("LRANGE Values: {:?}", result); - - // or customize routing and blocking parameters - let _ = cmd!("LRANGE", blocking: false); - let _ = cmd!("LRANGE", hash: ClusterHash::FirstKey); - let _ = cmd!("LRANGE", hash: ClusterHash::FirstKey, blocking: false); - // which is shorthand for - let command = CustomCommand::new("LRANGE", ClusterHash::FirstKey, false); - - // or use `custom_raw` to operate on RESP3 frames - let _result: Vec = client - .custom_raw(command, vec!["foo", "0", "3"]) - .await - .and_then(|frame| frame.try_into()) - .and_then(|value: RedisValue| value.convert())?; - Ok(()) -} diff --git a/examples/dns.rs b/examples/dns.rs deleted file mode 100644 index 0ab80740..00000000 --- a/examples/dns.rs +++ /dev/null @@ -1,48 +0,0 @@ -#![allow(clippy::disallowed_names)] - -use async_trait::async_trait; -use bytes_utils::Str; -use fred::{prelude::*, types::Resolve}; -use hickory_resolver::{ - config::{ResolverConfig, ResolverOpts}, - TokioAsyncResolver, -}; -use std::{net::SocketAddr, sync::Arc}; - -pub struct HickoryDnsResolver(TokioAsyncResolver); - -impl Default for HickoryDnsResolver { - fn default() -> Self { - HickoryDnsResolver(TokioAsyncResolver::tokio( - ResolverConfig::default(), - ResolverOpts::default(), - )) - } -} - -#[async_trait] -impl Resolve for HickoryDnsResolver { - async fn resolve(&self, host: Str, port: u16) -> Result, RedisError> { - Ok( - self - .0 - .lookup_ip(&*host) - .await? - .into_iter() - .map(|ip| SocketAddr::new(ip, port)) - .collect(), - ) - } -} - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let client = Builder::default_centralized().build()?; - client.set_resolver(Arc::new(HickoryDnsResolver::default())).await; - client.init().await?; - - // ... - - client.quit().await?; - Ok(()) -} diff --git a/examples/events.rs b/examples/events.rs deleted file mode 100644 index 27e19b2c..00000000 --- a/examples/events.rs +++ /dev/null @@ -1,99 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::prelude::*; -use futures::StreamExt; - -// requires tokio_stream 0.1.3 or later -use tokio_stream::wrappers::BroadcastStream; - -/// There are two interfaces for interacting with connection events on the `EventInterface`. -/// -/// * The `on_*` functions are generally easier to use but require spawning a new tokio task. They also currently only -/// support synchronous functions. -/// * The `*_rx` functions are somewhat more complicated to use but allow the caller to control the underlying channel -/// receiver directly. Additionally, these functions do not require spawning a new tokio task. -/// -/// See the source for `on_any` for an example of how one might handle multiple receivers in one task. -/// -/// The best approach depends on how many tasks the caller is willing to create. The `setup_pool` function shows -/// how one might combine multiple receiver streams in a `RedisPool` to minimize the overhead of new tokio tasks for -/// each underlying client. -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let client = Builder::default_centralized().build()?; - - // use the on_* functions - let _reconnect_task = client.on_reconnect(|server| { - println!("Reconnected to {}", server); - Ok(()) - }); - let _error_task = client.on_error(|error| { - println!("Connection error: {:?}", error); - Ok(()) - }); - - // use the *_rx functions to do the same thing shown above. although not shown here, callers have more freedom to - // reduce the number of spawned tokio tasks with this interface. - let mut reconnect_rx = client.reconnect_rx(); - let _reconnect_task_2 = tokio::spawn(async move { - while let Ok(server) = reconnect_rx.recv().await { - println!("Reconnected to {}", server); - } - }); - - let mut error_rx = client.error_rx(); - let _error_task_2 = tokio::spawn(async move { - while let Ok(error) = error_rx.recv().await { - println!("Connection error: {:?}", error); - } - }); - - client.init().await?; - - // ... - - client.quit().await?; - Ok(()) -} - -/// Shows how to combine multiple event streams from multiple clients into one tokio task. -#[allow(dead_code)] -async fn setup_pool() -> Result<(), RedisError> { - let pool = Builder::default_centralized().build_pool(5)?; - - // `select_all` does most of the work here but requires that the channel receivers implement `Stream`. the - // `tokio_stream::wrappers::BroadcastStream` wrapper can be used to do this. - let error_rxs: Vec<_> = pool - .clients() - .iter() - .map(|client| BroadcastStream::new(client.error_rx())) - .collect(); - let reconnect_rxs: Vec<_> = pool - .clients() - .iter() - .map(|client| BroadcastStream::new(client.reconnect_rx())) - .collect(); - let mut error_rx = futures::stream::select_all(error_rxs); - let mut reconnect_rx = futures::stream::select_all(reconnect_rxs); - - let _all_events_task = tokio::spawn(async move { - loop { - tokio::select! { - Some(Ok(error)) = error_rx.next() => { - println!("Error: {:?}", error); - } - Some(Ok(server)) = reconnect_rx.next() => { - println!("Reconnected to {}", server); - } - } - } - }); - - pool.init().await?; - - // ... - - pool.quit().await?; - Ok(()) -} diff --git a/examples/keyspace.rs b/examples/keyspace.rs deleted file mode 100644 index e118c991..00000000 --- a/examples/keyspace.rs +++ /dev/null @@ -1,127 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::prelude::*; -use std::time::Duration; -use tokio::time::sleep; - -/// Examples showing how to set up keyspace notifications with clustered or centralized/sentinel deployments. -/// -/// The most complicated part of this process involves safely handling reconnections. Keyspace events rely on the -/// pubsub interface, and clients are required to subscribe or resubscribe whenever a new connection is created. These -/// examples show how to manually handle reconnections, but the caller can also use the `SubscriberClient` interface -/// to remove some of the boilerplate. -/// -/// If callers do not need the keyspace subscriptions to survive reconnects then the process is more -/// straightforward. -/// -/// Both examples assume that the server has been configured to emit keyspace events (via `notify-keyspace-events`). -#[tokio::main] -async fn main() -> Result<(), RedisError> { - clustered_keyspace_events().await?; - centralized_keyspace_events().await?; - Ok(()) -} - -async fn fake_traffic(client: &RedisClient, amount: usize) -> Result<(), RedisError> { - // use a new client since the provided client is subscribed to keyspace events - let client = client.clone_new(); - client.init().await?; - - for idx in 0 .. amount { - let key: RedisKey = format!("foo-{}", idx).into(); - - client.set(&key, 1, None, None, false).await?; - client.incr(&key).await?; - client.del(&key).await?; - } - - client.quit().await?; - Ok(()) -} - -async fn centralized_keyspace_events() -> Result<(), RedisError> { - let subscriber = Builder::default_centralized().build()?; - - let reconnect_subscriber = subscriber.clone(); - // resubscribe to the foo- prefix whenever we reconnect to a server - let _reconnect_task = tokio::spawn(async move { - let mut reconnect_rx = reconnect_subscriber.reconnect_rx(); - - while let Ok(server) = reconnect_rx.recv().await { - println!("Reconnected to {}. Subscribing to keyspace events...", server); - reconnect_subscriber.psubscribe("__key__*:foo*").await?; - } - - Ok::<_, RedisError>(()) - }); - - // connect after setting up the reconnection logic - subscriber.init().await?; - - let mut keyspace_rx = subscriber.keyspace_event_rx(); - // set up a task that listens for keyspace events - let _keyspace_task = tokio::spawn(async move { - while let Ok(event) = keyspace_rx.recv().await { - println!( - "Recv: {} on {} in db {}", - event.operation, - event.key.as_str_lossy(), - event.db - ); - } - - Ok::<_, RedisError>(()) - }); - - // generate fake traffic and wait a second - fake_traffic(&subscriber, 1_000).await?; - sleep(Duration::from_secs(1)).await; - subscriber.quit().await?; - Ok(()) -} - -async fn clustered_keyspace_events() -> Result<(), RedisError> { - let subscriber = Builder::default_clustered().build()?; - - let reconnect_subscriber = subscriber.clone(); - // resubscribe to the foo- prefix whenever we reconnect to a server - let _reconnect_task = tokio::spawn(async move { - let mut reconnect_rx = reconnect_subscriber.reconnect_rx(); - - // in 7.x the reconnection interface added a `Server` struct to reconnect events to make this easier. - while let Ok(server) = reconnect_rx.recv().await { - println!("Reconnected to {}. Subscribing to keyspace events...", server); - reconnect_subscriber - .with_cluster_node(server) - .psubscribe("__key__*:foo*") - .await?; - } - - Ok::<_, RedisError>(()) - }); - - // connect after setting up the reconnection logic - subscriber.init().await?; - - let mut keyspace_rx = subscriber.keyspace_event_rx(); - // set up a task that listens for keyspace events - let _keyspace_task = tokio::spawn(async move { - while let Ok(event) = keyspace_rx.recv().await { - println!( - "Recv: {} on {} in db {}", - event.operation, - event.key.as_str_lossy(), - event.db - ); - } - - Ok::<_, RedisError>(()) - }); - - // generate fake traffic and wait a second - fake_traffic(&subscriber, 1_000).await?; - sleep(Duration::from_secs(1)).await; - subscriber.quit().await?; - Ok(()) -} diff --git a/examples/lua.rs b/examples/lua.rs deleted file mode 100644 index 72a6f0c7..00000000 --- a/examples/lua.rs +++ /dev/null @@ -1,64 +0,0 @@ -#![allow(clippy::disallowed_names)] - -use fred::{ - prelude::*, - types::{Library, Script}, - util as fred_utils, -}; - -static SCRIPT: &str = "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}"; - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let client = RedisClient::default(); - client.init().await?; - - let hash = fred_utils::sha1_hash(SCRIPT); - if !client.script_exists::(&hash).await? { - client.script_load(SCRIPT).await?; - } - - let results: RedisValue = client.evalsha(&hash, vec!["foo", "bar"], vec![1, 2]).await?; - println!("Script result for {hash}: {results:?}"); - - // or use `EVAL` - let results: RedisValue = client.eval(SCRIPT, vec!["foo", "bar"], vec![1, 2]).await?; - println!("Script result: {results:?}"); - - client.quit().await?; - Ok(()) -} - -// or use the `Script` utility types -#[allow(dead_code)] -async fn scripts() -> Result<(), RedisError> { - let client = RedisClient::default(); - client.init().await?; - - let script = Script::from_lua(SCRIPT); - script.load(&client).await?; - script.evalsha(&client, vec!["foo", "bar"], vec![1, 2]).await?; - // retry after calling SCRIPT LOAD, if needed - let (key1, key2, arg1, arg2): (String, String, i64, i64) = script - .evalsha_with_reload(&client, vec!["foo", "bar"], vec![1, 2]) - .await?; - println!("Script result: [{key1}, {key2}, {arg1}, {arg2}]"); - - Ok(()) -} - -// use the `Function` and `Library` utility types -#[allow(dead_code)] -async fn functions() -> Result<(), RedisError> { - let client = RedisClient::default(); - client.init().await?; - - let echo_lua = include_str!("../tests/scripts/lua/echo.lua"); - let lib = Library::from_code(&client, echo_lua).await?; - let func = lib.functions().get("echo").expect("Failed to read echo function"); - - let result: Vec = func.fcall(&client, vec!["foo{1}", "bar{1}"], vec!["3", "4"]).await?; - assert_eq!(result, vec!["foo{1}", "bar{1}", "3", "4"]); - - Ok(()) -} diff --git a/examples/misc.rs b/examples/misc.rs deleted file mode 100644 index 23d88a72..00000000 --- a/examples/misc.rs +++ /dev/null @@ -1,117 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::{ - prelude::*, - types::{BackpressureConfig, BackpressurePolicy, UnresponsiveConfig}, -}; -use std::time::Duration; - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let client = Builder::default_centralized() - .with_performance_config(|config| { - config.max_feed_count = 100; - config.auto_pipeline = true; - // change the buffer size behind the event interface functions (`on_message`, etc.) - config.broadcast_channel_capacity = 48; - // allow up to 25000 in-flight commands per connection - config.backpressure = BackpressureConfig { - disable_auto_backpressure: false, - max_in_flight_commands: 25_000, - policy: BackpressurePolicy::Drain, - } - }) - .with_connection_config(|config| { - config.tcp = TcpConfig { - nodelay: Some(true), - ..Default::default() - }; - config.max_command_attempts = 5; - config.max_redirections = 5; - config.internal_command_timeout = Duration::from_secs(2); - config.connection_timeout = Duration::from_secs(10); - // check every 3 seconds for connections that have been waiting on a response for more than 10 seconds - config.unresponsive = UnresponsiveConfig { - max_timeout: Some(Duration::from_secs(10)), - interval: Duration::from_secs(3) - }; - config.auto_client_setname = true; - config.reconnect_on_auth_error = true; - }) - // use exponential backoff, starting at 100 ms and doubling on each failed attempt up to 30 sec - .set_policy(ReconnectPolicy::new_exponential(0, 100, 30_000, 2)) - .build()?; - client.init().await?; - - // run all event listener functions in one task - let _events_task = client.on_any( - |error| { - println!("Connection error: {:?}", error); - Ok(()) - }, - |server| { - println!("Reconnected to {:?}", server); - Ok(()) - }, - |changes| { - println!("Cluster changed: {:?}", changes); - Ok(()) - }, - ); - - // update performance config options - let mut perf_config = client.perf_config(); - perf_config.max_feed_count = 1000; - client.update_perf_config(perf_config); - - // overwrite configuration options on individual commands - let options = Options { - max_attempts: Some(5), - max_redirections: Some(5), - timeout: Some(Duration::from_secs(10)), - ..Default::default() - }; - let _: Option = client.with_options(&options).get("foo").await?; - - // apply custom options to a pipeline - let pipeline = client.pipeline().with_options(&options); - pipeline.get("foo").await?; - pipeline.get("bar").await?; - let (_, _): (Option, Option) = pipeline.all().await?; - - // reuse pipelines - let pipeline = client.pipeline(); - pipeline.incr("foo").await?; - pipeline.incr("foo").await?; - assert_eq!(pipeline.last::().await?, 2); - assert_eq!(pipeline.last::().await?, 4); - assert_eq!(pipeline.last::().await?, 6); - - // interact with specific cluster nodes without creating new connections - if client.is_clustered() { - // discover connections via the active connection map - let _connections = client.active_connections().await?; - // or use the cached cluster state from `CLUSTER SLOTS` - let connections = client - .cached_cluster_state() - .map(|state| state.unique_primary_nodes()) - .unwrap_or_default(); - - for server in connections.into_iter() { - let info: String = client.with_cluster_node(&server).client_info().await?; - println!("Client info for {}: {}", server, info); - } - } - - // the `RedisValue` type also works as quick way to discover the type signature of a complicated response: - println!( - "{:?}", - client - .xreadgroup::("foo", "bar", None, None, false, "baz", ">") - .await? - ); - - client.quit().await?; - Ok(()) -} diff --git a/examples/monitor.rs b/examples/monitor.rs deleted file mode 100644 index 5809d656..00000000 --- a/examples/monitor.rs +++ /dev/null @@ -1,35 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::{monitor, prelude::*}; -use futures::stream::StreamExt; -use std::time::Duration; -use tokio::time::sleep; - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let monitor_jh = tokio::spawn(async move { - let config = RedisConfig::default(); - let mut monitor_stream = monitor::run(config).await?; - - while let Some(command) = monitor_stream.next().await { - // the Display implementation prints results in the same format as redis-cli - println!("{}", command); - } - - Ok::<(), RedisError>(()) - }); - - let client = RedisClient::default(); - client.init().await?; - - for idx in 0 .. 50 { - client.set("foo", idx, Some(Expiration::EX(10)), None, false).await?; - } - client.quit().await?; - - // wait a bit for the monitor stream to catch up - sleep(Duration::from_secs(1)).await; - monitor_jh.abort(); - Ok(()) -} diff --git a/examples/pipeline.rs b/examples/pipeline.rs deleted file mode 100644 index 16dc9267..00000000 --- a/examples/pipeline.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::prelude::*; - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - // the `auto_pipeline` config option determines whether the client will pipeline commands across tasks. - // this example shows how to pipeline commands within one task. - let client = RedisClient::default(); - client.init().await?; - - let pipeline = client.pipeline(); - // commands are queued in memory - let result: RedisValue = pipeline.incr("foo").await?; - assert!(result.is_queued()); - let result: RedisValue = pipeline.incr("foo").await?; - assert!(result.is_queued()); - - // send the pipeline and return all the results in order - let (first, second): (i64, i64) = pipeline.all().await?; - assert_eq!((first, second), (1, 2)); - - client.del("foo").await?; - // or send the pipeline and only return the last result - let pipeline = client.pipeline(); - pipeline.incr("foo").await?; - pipeline.incr("foo").await?; - assert_eq!(pipeline.last::().await?, 2); - - client.del("foo").await?; - // or handle each command result individually - let pipeline = client.pipeline(); - pipeline.incr("foo").await?; - pipeline.hgetall("foo").await?; // this will result in a `WRONGTYPE` error - let results = pipeline.try_all::().await; - assert_eq!(results[0].clone().unwrap(), 1); - assert!(results[1].is_err()); - - client.quit().await?; - Ok(()) -} diff --git a/examples/pool.rs b/examples/pool.rs deleted file mode 100644 index bc1d6289..00000000 --- a/examples/pool.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::prelude::*; - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let pool = Builder::default_centralized().build_pool(5)?; - pool.init().await?; - - // all client types, including `RedisPool`, implement the same command interface traits so callers can often use - // them interchangeably. in this example each command below will be sent round-robin to the underlying 5 clients. - assert!(pool.get::, _>("foo").await?.is_none()); - pool.set("foo", "bar", None, None, false).await?; - assert_eq!(pool.get::("foo").await?, "bar"); - - pool.del("foo").await?; - // interact with specific clients via next(), last(), or clients() - let pipeline = pool.next().pipeline(); - pipeline.incr("foo").await?; - pipeline.incr("foo").await?; - assert_eq!(pipeline.last::().await?, 2); - - for client in pool.clients() { - println!("{} connected to {:?}", client.id(), client.active_connections().await?); - } - - pool.quit().await?; - Ok(()) -} diff --git a/examples/pubsub.rs b/examples/pubsub.rs deleted file mode 100644 index 17a5ec45..00000000 --- a/examples/pubsub.rs +++ /dev/null @@ -1,80 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -#[allow(unused_imports)] -use fred::clients::SubscriberClient; -use fred::prelude::*; -use std::time::Duration; -use tokio::time::sleep; - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let publisher_client = Builder::default_centralized() - .with_performance_config(|config| { - // change the buffer size of the broadcast channels used by the EventInterface - config.broadcast_channel_capacity = 64; - }) - .build()?; - let subscriber_client = publisher_client.clone_new(); - publisher_client.init().await?; - subscriber_client.init().await?; - - // or use `message_rx()` to use the underlying `BroadcastReceiver` directly without spawning a new task - let _message_task = subscriber_client.on_message(|message| { - println!("{}: {}", message.channel, message.value.convert::()?); - Ok::<_, RedisError>(()) - }); - - for idx in 0 .. 50 { - publisher_client.publish("foo", idx).await?; - sleep(Duration::from_secs(1)).await; - } - - publisher_client.quit().await?; - subscriber_client.quit().await?; - Ok(()) -} - -#[cfg(feature = "subscriber-client")] -#[allow(dead_code)] -async fn subscriber_example() -> Result<(), RedisError> { - let subscriber = Builder::default_centralized() - .with_performance_config(|config| { - // tune the size of the buffer behind the pubsub broadcast channels - config.broadcast_channel_capacity = 64; - }) - .build_subscriber_client()?; - subscriber.init().await?; - - // or use the `on_message` shorthand - let mut message_stream = subscriber.message_rx(); - let _subscriber_task = tokio::spawn(async move { - while let Ok(message) = message_stream.recv().await { - println!("Recv {:?} on channel {}", message.value, message.channel); - } - - Ok::<_, RedisError>(()) - }); - - // spawn a task to sync subscriptions whenever the client reconnects - let _resubscribe_task = subscriber.manage_subscriptions(); - - subscriber.subscribe("foo").await?; - subscriber.psubscribe(vec!["bar*", "baz*"]).await?; - subscriber.ssubscribe("abc{123}").await?; - // after reconnecting the client will automatically re-subscribe to the above channels and patterns - println!("Subscriber channels: {:?}", subscriber.tracked_channels()); // "foo" - println!("Subscriber patterns: {:?}", subscriber.tracked_patterns()); // "bar*", "baz*" - println!("Subscriber shard channels: {:?}", subscriber.tracked_shard_channels()); // "abc{123}" - - subscriber.unsubscribe("foo").await?; - // now it will only re-subscribe to "bar*", "baz*", and "abc{123}" after reconnecting - - // force a re-subscription call to all channels or patterns - subscriber.resubscribe_all().await?; - // unsubscribe from all channels and patterns - subscriber.unsubscribe_all().await?; - - subscriber.quit().await?; - Ok(()) -} diff --git a/examples/redis_json.rs b/examples/redis_json.rs deleted file mode 100644 index 399b5642..00000000 --- a/examples/redis_json.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::{interfaces::RedisJsonInterface, json_quote, prelude::*, util::NONE}; -use serde_json::{json, Value}; - -// see the serde-json example for more information on deserializing responses -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let client = Builder::default_centralized().build()?; - client.init().await?; - - // operate on objects - let value = json!({ - "a": "b", - "c": 1, - "d": true - }); - let _: () = client.json_set("foo", "$", value.clone(), None).await?; - let result: Value = client.json_get("foo", NONE, NONE, NONE, "$").await?; - assert_eq!(value, result[0]); - let count: i64 = client.json_del("foo", "$..c").await?; - assert_eq!(count, 1); - let result: Value = client.json_get("foo", NONE, NONE, NONE, "$").await?; - assert_eq!(result[0], json!({ "a": "b", "d": true })); - - // operate on arrays - let _: () = client.json_set("foo", "$", json!(["a", "b"]), None).await?; - let size: i64 = client - .json_arrappend("foo", "$", vec![json_quote!("c"), json_quote!("d")]) - .await?; - assert_eq!(size, 4); - let size: i64 = client.json_arrappend("foo", "$", vec![json!({"e": "f"})]).await?; - assert_eq!(size, 5); - let len: i64 = client.json_arrlen("foo", NONE).await?; - assert_eq!(len, 5); - - let result: Value = client.json_get("foo", NONE, NONE, NONE, "$").await?; - assert_eq!(result[0], json!(["a", "b", "c", "d", { "e": "f" }])); - - // or see the redis-json integration tests for more - client.quit().await?; - Ok(()) -} diff --git a/examples/replicas.rs b/examples/replicas.rs deleted file mode 100644 index 7acf6ce3..00000000 --- a/examples/replicas.rs +++ /dev/null @@ -1,96 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] -#![allow(clippy::mutable_key_type)] - -use fred::{ - prelude::*, - types::{ClusterDiscoveryPolicy, ClusterHash, ReplicaConfig, RespVersion}, - util::redis_keyslot, -}; -use futures::future::try_join_all; -use log::info; -use std::collections::HashSet; - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - pretty_env_logger::init(); - - let config = RedisConfig::from_url("redis-cluster://foo:bar@redis-cluster-1:30001")?; - let pool = Builder::from_config(config) - .with_config(|config| { - config.version = RespVersion::RESP3; - config - .server - .set_cluster_discovery_policy(ClusterDiscoveryPolicy::ConfigEndpoint) - .expect("Failed to set discovery policy."); - }) - .with_connection_config(|config| { - config.replica = ReplicaConfig { - lazy_connections: true, - primary_fallback: true, - ..Default::default() - }; - }) - .set_policy(ReconnectPolicy::new_exponential(0, 100, 30_000, 2)) - .build_pool(5)?; - - pool.init().await?; - info!("Connected to redis."); - lazy_connection_example(pool.next()).await?; - - // use pipelines and WAIT to concurrently SET then GET a value from replica nodes - let mut ops = Vec::with_capacity(1000); - for idx in 0 .. 1000 { - let pool = pool.clone(); - ops.push(async move { - let key: RedisKey = format!("foo-{}", idx).into(); - let cluster_hash = ClusterHash::Custom(redis_keyslot(key.as_bytes())); - - // send WAIT to the cluster node that received SET - let pipeline = pool.next().pipeline(); - pipeline.set(&key, idx, None, None, false).await?; - pipeline - .with_options(&Options { - cluster_hash: Some(cluster_hash), - ..Default::default() - }) - .wait(1, 10_000) - .await?; - pipeline.all().await?; - - assert_eq!(pool.replicas().get::(&key).await?, idx); - Ok::<_, RedisError>(()) - }); - } - try_join_all(ops).await?; - - Ok(()) -} - -// use one client to demonstrate how lazy connections are created. in this case each primary node is expected to have -// one replica. -async fn lazy_connection_example(client: &RedisClient) -> Result<(), RedisError> { - let replica_routing = client.replicas().nodes(); - let cluster_routing = client - .cached_cluster_state() - .expect("Failed to read cached cluster state."); - let expected_primary = cluster_routing - .get_server(redis_keyslot(b"foo")) - .expect("Failed to read primary node owner for 'foo'"); - let old_connections: HashSet<_> = client.active_connections().await?.into_iter().collect(); - - // if `lazy_connections: true` the client creates the connection here - client.replicas().get("foo").await?; - let new_connections: HashSet<_> = client.active_connections().await?.into_iter().collect(); - let new_servers: Vec<_> = new_connections.difference(&old_connections).collect(); - // verify that 1 new connection was created, and that it's in the replica map as a replica of the expected primary - // node - assert_eq!(new_servers.len(), 1); - assert_eq!(replica_routing.get(new_servers[0]), Some(expected_primary)); - - // update the replica routing table and reset replica connections - client.replicas().sync(true).await?; - assert_eq!(old_connections.len(), client.active_connections().await?.len()); - - Ok(()) -} diff --git a/examples/scan.rs b/examples/scan.rs deleted file mode 100644 index a4119fb2..00000000 --- a/examples/scan.rs +++ /dev/null @@ -1,89 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] -#![allow(dead_code)] - -use fred::{prelude::*, types::Scanner}; -use futures::stream::TryStreamExt; - -async fn create_fake_data(client: &RedisClient) -> Result<(), RedisError> { - let values: Vec<(String, i64)> = (0 .. 50).map(|i| (format!("foo-{}", i), i)).collect(); - client.mset(values).await -} - -async fn delete_fake_data(client: &RedisClient) -> Result<(), RedisError> { - let keys: Vec<_> = (0 .. 50).map(|i| format!("foo-{}", i)).collect(); - client.del(keys).await?; - Ok(()) -} - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let client = RedisClient::default(); - client.init().await?; - create_fake_data(&client).await?; - - // scan all keys in the keyspace, returning 10 keys per page - let mut scan_stream = client.scan("foo*", Some(10), None); - while let Some(mut page) = scan_stream.try_next().await? { - if let Some(keys) = page.take_results() { - // create a client from the scan result, reusing the existing connection(s) - let client = page.create_client(); - - for key in keys.into_iter() { - let value: RedisValue = client.get(&key).await?; - println!("Scanned {} -> {:?}", key.as_str_lossy(), value); - } - } - - // **important:** move on to the next page now that we're done reading the values - let _ = page.next(); - } - - delete_fake_data(&client).await?; - client.quit().await?; - Ok(()) -} - -/// Example showing how to print the memory usage of all keys in a cluster with a `RedisPool`. -/// -/// When using a clustered deployment the keyspace will be spread across multiple nodes. However, the cursor in each -/// SCAN command is used to iterate over keys within a single node. There are several ways to concurrently scan -/// all keys on all nodes: -/// -/// 1. Use `scan_cluster`. -/// 2. Use `split_cluster` and `scan`. -/// 3. Use `with_cluster_node` and `scan`. -/// -/// The best option depends on several factors, but `scan_cluster` is often the easiest approach for most use -/// cases. -async fn pool_scan_cluster_memory_example(pool: &RedisPool) -> Result<(), RedisError> { - // the majority of the client traffic in this scenario comes from the MEMORY USAGE call on each key, so we'll use a - // pool to round-robin these commands among multiple clients. a clustered client with `auto_pipeline: true` can scan - // all nodes in the cluster concurrently, so we use a single client rather than a pool to issue the SCAN calls. - let mut total_size = 0; - // if the pattern contains a hash tag then callers can use `scan` instead of `scan_cluster` - let mut scanner = pool.next().scan_cluster("*", Some(100), None); - - while let Some(mut page) = scanner.try_next().await? { - if let Some(page) = page.take_results() { - // pipeline the `MEMORY USAGE` calls - let pipeline = pool.next().pipeline(); - for key in page.iter() { - pipeline.memory_usage(key, Some(0)).await?; - } - let sizes: Vec> = pipeline.all().await?; - assert_eq!(page.len(), sizes.len()); - - for (idx, key) in page.into_iter().enumerate() { - let size = sizes[idx].unwrap_or(0); - println!("{}: {}", key.as_str_lossy(), size); - total_size += size; - } - } - - let _ = page.next(); - } - - println!("Total size: {}", total_size); - Ok(()) -} diff --git a/examples/sentinel.rs b/examples/sentinel.rs deleted file mode 100644 index a5afc644..00000000 --- a/examples/sentinel.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::{prelude::*, types::Server}; - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let config = RedisConfig { - server: ServerConfig::Sentinel { - // the name of the service, as configured in the sentinel configuration - service_name: "my-service-name".into(), - // the known host/port tuples for the sentinel nodes - // the client will automatically update these if sentinels are added or removed - hosts: vec![ - Server::new("localhost", 26379), - Server::new("localhost", 26380), - Server::new("localhost", 26381), - ], - // callers can also use the `sentinel-auth` feature to use different credentials to sentinel nodes - #[cfg(feature = "sentinel-auth")] - username: None, - #[cfg(feature = "sentinel-auth")] - password: None, - }, - // sentinels should use the same TLS settings as the Redis servers - ..Default::default() - }; - - let client = Builder::from_config(config).build()?; - client.init().await?; - - // ... - - client.quit().await?; - Ok(()) -} diff --git a/examples/serde_json.rs b/examples/serde_json.rs deleted file mode 100644 index 2cfc6637..00000000 --- a/examples/serde_json.rs +++ /dev/null @@ -1,51 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::prelude::*; -use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; - -// from the serde json docs -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -struct Person { - name: String, - age: u8, - phones: Vec, -} - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let client = RedisClient::default(); - client.init().await?; - - let value = json!({ - "foo": "a", - "bar": "b" - }); - client.set("wibble", value.to_string(), None, None, false).await?; - - // converting back to a json `Value` will also try to parse nested json strings. if a value looks like json, but - // cannot be parsed as json, then it will be returned as a string. - assert_eq!(value, client.get::("wibble").await?); - - // or store types as json strings via Serialize and Deserialize - let person = Person { - name: "Foo".into(), - age: 42, - phones: vec!["abc".into(), "123".into()], - }; - - let serialized = serde_json::to_string(&person)?; - client.set("foo", serialized, None, None, false).await?; - // deserialize as a json value - let person_json: Value = client.get("foo").await?; - let deserialized: Person = serde_json::from_value(person_json)?; - assert_eq!(person, deserialized); - // or as a json string - let person_string: String = client.get("foo").await?; - let deserialized: Person = serde_json::from_str(&person_string)?; - assert_eq!(person, deserialized); - - client.quit().await?; - Ok(()) -} diff --git a/examples/streams.rs b/examples/streams.rs deleted file mode 100644 index bc9ac905..00000000 --- a/examples/streams.rs +++ /dev/null @@ -1,91 +0,0 @@ -#![allow(clippy::mutable_key_type)] - -use bytes_utils::Str; -use fred::{prelude::*, types::XReadResponse}; -use futures::future::try_join_all; -use std::time::Duration; -use tokio::time::sleep; - -static VALUES: &[&str] = &["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]; - -/// Example showing how to use streams to communicate between Tokio tasks. -#[tokio::main] -async fn main() { - pretty_env_logger::init(); - - let reader_task = tokio::spawn(async move { - let client = Builder::default_centralized() - .with_config(|config| { - config.password = Some("bar".into()); - }) - .build()?; - client.init().await?; - - // initialize the stream first - client.del("foo").await?; - client.xgroup_create("foo", "group", "$", true).await?; - - let mut count = 0; - loop { - // call XREAD for new records in a loop, blocking up to 10 sec each time - let entry: XReadResponse = client.xread_map(Some(1), Some(10_000), "foo", "$").await?; - count += 1; - - for (key, records) in entry.into_iter() { - for (id, fields) in records.into_iter() { - println!("Reader recv {} - {}: {:?}", key, id, fields); - } - } - - if count * 2 >= VALUES.len() { - break; - } - } - - client.quit().await?; - Ok::<_, RedisError>(()) - }); - - let writer_task = tokio::spawn(async move { - // give the reader a chance to call XREAD first - sleep(Duration::from_secs(1)).await; - - let client = Builder::default_centralized() - .with_config(|config| { - config.password = Some("bar".into()); - }) - .build()?; - client.init().await?; - - // add values in groups of 2. this should create the following stream contents: - // [{"field1":"a","field2":"b"}, {"field1":"c","field2":"d"}, {"field1":"e","field2":"f"}, ...] - for values in VALUES.chunks(2) { - let id: Str = client - .xadd("foo", false, None, "*", vec![ - ("field1", values[0]), - ("field2", values[1]), - ]) - .await?; - - println!("Writer added stream entry with ID: {}", id); - sleep(Duration::from_secs(1)).await; - } - - client.quit().await?; - Ok::<_, RedisError>(()) - }); - - try_join_all([writer_task, reader_task]).await.unwrap(); -} - -// example output: -// Writer added stream entry with ID: 1704862102584-0 -// Reader recv foo - 1704862102584-0: {"field2": "b", "field1": "a"} -// Writer added stream entry with ID: 1704862103589-0 -// Reader recv foo - 1704862103589-0: {"field1": "c", "field2": "d"} -// Writer added stream entry with ID: 1704862104594-0 -// Reader recv foo - 1704862104594-0: {"field1": "e", "field2": "f"} -// Writer added stream entry with ID: 1704862105598-0 -// Reader recv foo - 1704862105598-0: {"field1": "g", "field2": "h"} -// Writer added stream entry with ID: 1704862106603-0 -// Reader recv foo - 1704862106603-0: {"field1": "i", "field2": "j"} diff --git a/examples/tls.rs b/examples/tls.rs deleted file mode 100644 index ce99c2f3..00000000 --- a/examples/tls.rs +++ /dev/null @@ -1,59 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::prelude::*; - -#[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" -))] -use fred::types::TlsConnector; - -#[cfg(feature = "enable-native-tls")] -fn create_tls_config() -> TlsConnector { - use fred::native_tls::TlsConnector as NativeTlsConnector; - - // or use `TlsConnector::default_native_tls()` - NativeTlsConnector::builder() - .use_sni(true) - .danger_accept_invalid_certs(false) - .danger_accept_invalid_certs(false) - .build() - .expect("Failed to create TLS config") - .into() -} - -#[cfg(all( - any(feature = "enable-rustls", feature = "enable-rustls-ring"), - not(feature = "enable-native-tls") -))] -fn create_tls_config() -> TlsConnector { - use fred::rustls::{ClientConfig, RootCertStore}; - - // or use `TlsConnector::default_rustls()` - ClientConfig::builder() - .with_root_certificates(RootCertStore::empty()) - .with_no_client_auth() - .into() -} - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let config = RedisConfig { - #[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" - ))] - tls: Some(create_tls_config().into()), - ..RedisConfig::default() - }; - let client = Builder::from_config(config).build()?; - client.init().await?; - - // ... - - client.quit().await?; - Ok(()) -} diff --git a/examples/transactions.rs b/examples/transactions.rs deleted file mode 100644 index 0f83c83a..00000000 --- a/examples/transactions.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![allow(clippy::disallowed_names)] -#![allow(clippy::let_underscore_future)] - -use fred::prelude::*; - -#[tokio::main] -async fn main() -> Result<(), RedisError> { - let client = RedisClient::default(); - client.init().await?; - - // transactions are buffered in memory before calling `exec` - let trx = client.multi(); - let result: RedisValue = trx.get("foo").await?; - assert!(result.is_queued()); - let result: RedisValue = trx.set("foo", "bar", None, None, false).await?; - assert!(result.is_queued()); - let result: RedisValue = trx.get("foo").await?; - assert!(result.is_queued()); - - // automatically send `WATCH ...` before `MULTI` - trx.watch_before(vec!["foo", "bar"]); - let values: (Option, (), String) = trx.exec(true).await?; - println!("Transaction results: {:?}", values); - - client.quit().await?; - Ok(()) -} diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index 08e76d74..00000000 --- a/rustfmt.toml +++ /dev/null @@ -1,52 +0,0 @@ -binop_separator = "Front" -blank_lines_upper_bound = 1 -brace_style = "SameLineWhere" -combine_control_expr = true -comment_width = 125 -condense_wildcard_suffixes = false -control_brace_style = "AlwaysSameLine" -edition = "2021" -empty_item_single_line = true -enum_discrim_align_threshold = 0 -error_on_line_overflow = false -error_on_unformatted = false -fn_params_layout = "Tall" -fn_single_line = false -force_explicit_abi = true -force_multiline_blocks = false -format_code_in_doc_comments = true -format_macro_bodies = true -format_macro_matchers = true -format_strings = true -hard_tabs = false -imports_granularity = "Crate" -imports_indent = "Block" -imports_layout = "HorizontalVertical" -indent_style = "Block" -inline_attribute_width = 0 -match_arm_blocks = true -match_block_trailing_comma = true -max_width = 118 -merge_derives = true -newline_style = "Auto" -normalize_comments = true -normalize_doc_attributes = true -overflow_delimited_expr = true -remove_nested_parens = true -reorder_impl_items = true -reorder_imports = true -reorder_modules = true -show_parse_errors = true -space_after_colon = true -space_before_colon = false -spaces_around_ranges = true -struct_field_align_threshold = 50 -struct_lit_single_line = true -tab_spaces = 2 -trailing_semicolon = true -type_punctuation_density = "Wide" -use_field_init_shorthand = true -use_small_heuristics = "Default" -use_try_shorthand = true -where_single_line = false -wrap_comments = true diff --git a/src/_tokio.rs b/src/_tokio.rs deleted file mode 100644 index 8720a234..00000000 --- a/src/_tokio.rs +++ /dev/null @@ -1,395 +0,0 @@ -use crate::{ - clients::WithOptions, - commands, - error::RedisError, - interfaces::{default_send_command, RedisResult}, - modules::inner::RedisClientInner, - protocol::command::RedisCommand, - router::commands as router_commands, - types::{ - ClientState, - ConnectHandle, - ConnectionConfig, - CustomCommand, - FromRedis, - InfoKind, - Options, - PerformanceConfig, - ReconnectPolicy, - RedisConfig, - RedisValue, - Resp3Frame, - RespVersion, - Server, - Version, - }, - utils, -}; -use arc_swap::ArcSwapAny; -use futures::Stream; -use std::{future::Future, sync::Arc}; -use tokio::sync::broadcast::{Receiver, Sender}; -pub use tokio::{ - spawn, - sync::{ - broadcast::{self, error::SendError as BroadcastSendError}, - mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, - oneshot::{channel as oneshot_channel, Receiver as OneshotReceiver, Sender as OneshotSender}, - RwLock as AsyncRwLock, - }, - task::JoinHandle, - time::sleep, -}; -use tokio_stream::wrappers::UnboundedReceiverStream; - -#[cfg(any(feature = "dns", feature = "trust-dns-resolver"))] -use crate::protocol::types::Resolve; - -#[cfg(feature = "i-server")] -use crate::types::ShutdownFlags; - -/// The reference counting container type. -/// -/// This type may change based on the runtime feature flags used. -pub type RefCount = Arc; - -pub type AtomicBool = std::sync::atomic::AtomicBool; -pub type AtomicUsize = std::sync::atomic::AtomicUsize; -pub type Mutex = parking_lot::Mutex; -pub type RwLock = parking_lot::RwLock; -pub type RefSwap = ArcSwapAny; -pub type BroadcastSender = Sender; -pub type BroadcastReceiver = Receiver; - -pub fn broadcast_send(tx: &BroadcastSender, msg: &T, func: F) { - if let Err(BroadcastSendError(val)) = tx.send(msg.clone()) { - func(&val); - } -} - -pub fn broadcast_channel(capacity: usize) -> (BroadcastSender, BroadcastReceiver) { - broadcast::channel(capacity) -} - -pub fn rx_stream(rx: UnboundedReceiver) -> impl Stream { - UnboundedReceiverStream::new(rx) -} - -/// Any Redis client that implements any part of the Redis interface. -pub trait ClientLike: Clone + Send + Sync + Sized { - #[doc(hidden)] - fn inner(&self) -> &Arc; - - /// Helper function to intercept and modify a command without affecting how it is sent to the connection layer. - #[doc(hidden)] - fn change_command(&self, _: &mut RedisCommand) {} - - /// Helper function to intercept and customize how a command is sent to the connection layer. - #[doc(hidden)] - fn send_command(&self, command: C) -> Result<(), RedisError> - where - C: Into, - { - let mut command: RedisCommand = command.into(); - self.change_command(&mut command); - default_send_command(self.inner(), command) - } - - /// The unique ID identifying this client and underlying connections. - fn id(&self) -> &str { - &self.inner().id - } - - /// Read the config used to initialize the client. - fn client_config(&self) -> RedisConfig { - self.inner().config.as_ref().clone() - } - - /// Read the reconnect policy used to initialize the client. - fn client_reconnect_policy(&self) -> Option { - self.inner().policy.read().clone() - } - - /// Read the connection config used to initialize the client. - fn connection_config(&self) -> &ConnectionConfig { - self.inner().connection.as_ref() - } - - /// Read the RESP version used by the client when communicating with the server. - fn protocol_version(&self) -> RespVersion { - if self.inner().is_resp3() { - RespVersion::RESP3 - } else { - RespVersion::RESP2 - } - } - - /// Whether the client has a reconnection policy. - fn has_reconnect_policy(&self) -> bool { - self.inner().policy.read().is_some() - } - - /// Whether the client will automatically pipeline commands. - fn is_pipelined(&self) -> bool { - self.inner().is_pipelined() - } - - /// Whether the client is connected to a cluster. - fn is_clustered(&self) -> bool { - self.inner().config.server.is_clustered() - } - - /// Whether the client uses the sentinel interface. - fn uses_sentinels(&self) -> bool { - self.inner().config.server.is_sentinel() - } - - /// Update the internal [PerformanceConfig](crate::types::PerformanceConfig) in place with new values. - fn update_perf_config(&self, config: PerformanceConfig) { - self.inner().update_performance_config(config); - } - - /// Read the [PerformanceConfig](crate::types::PerformanceConfig) associated with this client. - fn perf_config(&self) -> PerformanceConfig { - self.inner().performance_config() - } - - /// Read the state of the underlying connection(s). - /// - /// If running against a cluster the underlying state will reflect the state of the least healthy connection. - fn state(&self) -> ClientState { - self.inner().state.read().clone() - } - - /// Whether all underlying connections are healthy. - fn is_connected(&self) -> bool { - *self.inner().state.read() == ClientState::Connected - } - - /// Read the set of active connections managed by the client. - fn active_connections(&self) -> impl Future, RedisError>> + Send { - commands::server::active_connections(self) - } - - /// Read the server version, if known. - fn server_version(&self) -> Option { - self.inner().server_state.read().kind.server_version() - } - - /// Override the DNS resolution logic for the client. - #[cfg(feature = "dns")] - #[cfg_attr(docsrs, doc(cfg(feature = "dns")))] - fn set_resolver(&self, resolver: Arc) -> impl Future + Send { - async move { self.inner().set_resolver(resolver).await } - } - - /// Connect to the server. - /// - /// This function returns a `JoinHandle` to a task that drives the connection. It will not resolve until the - /// connection closes, or if a reconnection policy with unlimited attempts is provided then it will - /// run until `QUIT` is called. Callers should avoid calling [abort](tokio::task::JoinHandle::abort) on the returned - /// `JoinHandle` unless the client will no longer be used. - /// - /// **Calling this function more than once will drop all state associated with the previous connection(s).** Any - /// pending commands on the old connection(s) will either finish or timeout, but they will not be retried on the - /// new connection(s). - /// - /// See [init](Self::init) for an alternative shorthand. - fn connect(&self) -> ConnectHandle { - let inner = self.inner().clone(); - utils::reset_router_task(&inner); - - tokio::spawn(async move { - utils::clear_backchannel_state(&inner).await; - let result = router_commands::start(&inner).await; - // a canceled error means we intentionally closed the client - _trace!(inner, "Ending connection task with {:?}", result); - - if let Err(ref error) = result { - if !error.is_canceled() { - inner.notifications.broadcast_connect(Err(error.clone())); - } - } - - utils::check_and_set_client_state(&inner.state, ClientState::Disconnecting, ClientState::Disconnected); - result - }) - } - - /// Force a reconnection to the server(s). - /// - /// When running against a cluster this function will also refresh the cached cluster routing table. - fn force_reconnection(&self) -> impl Future> + Send { - async move { commands::server::force_reconnection(self.inner()).await } - } - - /// Wait for the result of the next connection attempt. - /// - /// This can be used with `on_reconnect` to separate initialization logic that needs to occur only on the next - /// connection attempt vs all subsequent attempts. - fn wait_for_connect(&self) -> impl Future> + Send { - async move { - if utils::read_locked(&self.inner().state) == ClientState::Connected { - debug!("{}: Client is already connected.", self.inner().id); - Ok(()) - } else { - self.inner().notifications.connect.load().subscribe().recv().await? - } - } - } - - /// Initialize a new routing and connection task and wait for it to connect successfully. - /// - /// The returned [ConnectHandle](crate::types::ConnectHandle) refers to the task that drives the routing and - /// connection layer. It will not finish until the max reconnection count is reached. Callers should avoid calling - /// [abort](tokio::task::JoinHandle::abort) on the returned `JoinHandle` unless the client will no longer be used. - /// - /// Callers can also use [connect](Self::connect) and [wait_for_connect](Self::wait_for_connect) separately if - /// needed. - /// - /// ```rust - /// use fred::prelude::*; - /// - /// #[tokio::main] - /// async fn main() -> Result<(), RedisError> { - /// let client = RedisClient::default(); - /// let connection_task = client.init().await?; - /// - /// // ... - /// - /// client.quit().await?; - /// connection_task.await? - /// } - /// ``` - fn init(&self) -> impl Future> + Send { - async move { - let mut rx = { self.inner().notifications.connect.load().subscribe() }; - let task = self.connect(); - let error = rx.recv().await.map_err(RedisError::from).and_then(|r| r).err(); - - if let Some(error) = error { - // the initial connection failed, so we should gracefully close the routing task - utils::reset_router_task(self.inner()); - Err(error) - } else { - Ok(task) - } - } - } - - /// Close the connection to the Redis server. The returned future resolves when the command has been written to the - /// socket, not when the connection has been fully closed. Some time after this future resolves the future - /// returned by [connect](Self::connect) will resolve which indicates that the connection has been fully closed. - /// - /// This function will also close all error, pubsub message, and reconnection event streams. - fn quit(&self) -> impl Future> + Send { - async move { commands::server::quit(self).await } - } - - /// Shut down the server and quit the client. - /// - /// - #[cfg(feature = "i-server")] - #[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] - fn shutdown(&self, flags: Option) -> impl Future> + Send { - async move { commands::server::shutdown(self, flags).await } - } - - /// Delete the keys in all databases. - /// - /// - fn flushall(&self, r#async: bool) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::server::flushall(self, r#async).await?.convert() } - } - - /// Delete the keys on all nodes in the cluster. This is a special function that does not map directly to the Redis - /// interface. - fn flushall_cluster(&self) -> impl Future> + Send { - async move { commands::server::flushall_cluster(self).await } - } - - /// Ping the Redis server. - /// - /// - fn ping(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::server::ping(self).await?.convert() } - } - - /// Read info about the server. - /// - /// - fn info(&self, section: Option) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::server::info(self, section).await?.convert() } - } - - /// Run a custom command that is not yet supported via another interface on this client. This is most useful when - /// interacting with third party modules or extensions. - /// - /// Callers should use the re-exported [redis_keyslot](crate::util::redis_keyslot) function to hash the command's - /// key, if necessary. - /// - /// This interface should be used with caution as it may break the automatic pipeline features in the client if - /// command flags are not properly configured. - fn custom(&self, cmd: CustomCommand, args: Vec) -> impl Future> + Send - where - R: FromRedis, - T: TryInto + Send, - T::Error: Into + Send, - { - async move { - let args = utils::try_into_vec(args)?; - commands::server::custom(self, cmd, args).await?.convert() - } - } - - /// Run a custom command similar to [custom](Self::custom), but return the response frame directly without any - /// parsing. - /// - /// Note: RESP2 frames from the server are automatically converted to the RESP3 format when parsed by the client. - fn custom_raw(&self, cmd: CustomCommand, args: Vec) -> impl Future> + Send - where - T: TryInto + Send, - T::Error: Into + Send, - { - async move { - let args = utils::try_into_vec(args)?; - commands::server::custom_raw(self, cmd, args).await - } - } - - /// Customize various configuration options on commands. - fn with_options(&self, options: &Options) -> WithOptions { - WithOptions { - client: self.clone(), - options: options.clone(), - } - } -} - -pub fn spawn_event_listener(mut rx: BroadcastReceiver, func: F) -> JoinHandle> -where - T: Clone + Send + 'static, - F: Fn(T) -> RedisResult<()> + Send + 'static, -{ - tokio::spawn(async move { - let mut result = Ok(()); - - while let Ok(val) = rx.recv().await { - if let Err(err) = func(val) { - result = Err(err); - break; - } - } - - result - }) -} diff --git a/src/clients/mod.rs b/src/clients/mod.rs deleted file mode 100644 index 2906a02e..00000000 --- a/src/clients/mod.rs +++ /dev/null @@ -1,36 +0,0 @@ -mod options; -mod pipeline; -mod pool; -mod redis; - -pub use options::WithOptions; -pub use pipeline::Pipeline; -pub use pool::RedisPool; -pub use redis::RedisClient; - -#[cfg(not(feature = "glommio"))] -pub use pool::ExclusivePool; - -#[cfg(feature = "sentinel-client")] -mod sentinel; -#[cfg(feature = "sentinel-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))] -pub use sentinel::SentinelClient; - -#[cfg(feature = "subscriber-client")] -mod pubsub; -#[cfg(feature = "subscriber-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "subscriber-client")))] -pub use pubsub::SubscriberClient; - -#[cfg(feature = "replicas")] -mod replica; -#[cfg(feature = "replicas")] -#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] -pub use replica::Replicas; - -#[cfg(feature = "transactions")] -mod transaction; -#[cfg(feature = "transactions")] -#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))] -pub use transaction::Transaction; diff --git a/src/clients/options.rs b/src/clients/options.rs deleted file mode 100644 index 19a89fed..00000000 --- a/src/clients/options.rs +++ /dev/null @@ -1,160 +0,0 @@ -use crate::{ - error::RedisError, - interfaces::*, - modules::inner::RedisClientInner, - protocol::command::RedisCommand, - runtime::RefCount, - types::Options, -}; -use std::{fmt, ops::Deref}; - -/// A client interface used to customize command configuration options. -/// -/// See [Options](crate::types::Options) for more information. -/// -/// ```rust -/// # use fred::prelude::*; -/// # use std::time::Duration; -/// async fn example() -> Result<(), RedisError> { -/// let client = RedisClient::default(); -/// client.init().await?; -/// -/// let options = Options { -/// max_redirections: Some(3), -/// max_attempts: Some(1), -/// timeout: Some(Duration::from_secs(10)), -/// ..Default::default() -/// }; -/// let foo: Option = client.with_options(&options).get("foo").await?; -/// -/// // reuse the options bindings -/// let with_options = client.with_options(&options); -/// let foo: () = with_options.get("foo").await?; -/// let bar: () = with_options.get("bar").await?; -/// -/// // combine with other client types -/// let pipeline = client.pipeline().with_options(&options); -/// let _: () = pipeline.get("foo").await?; -/// let _: () = pipeline.get("bar").await?; -/// // custom options will be applied to each command -/// println!("results: {:?}", pipeline.all::().await?); -/// -/// Ok(()) -/// } -/// ``` -#[derive(Clone)] -pub struct WithOptions { - pub(crate) client: C, - pub(crate) options: Options, -} - -impl WithOptions { - /// Read the options that will be applied to commands. - pub fn options(&self) -> &Options { - &self.options - } -} - -impl Deref for WithOptions { - type Target = C; - - fn deref(&self) -> &Self::Target { - &self.client - } -} - -impl fmt::Debug for WithOptions { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("WithOptions") - .field("client", &self.client.id()) - .field("options", &self.options) - .finish() - } -} - -impl ClientLike for WithOptions { - #[doc(hidden)] - fn inner(&self) -> &RefCount { - self.client.inner() - } - - #[doc(hidden)] - fn change_command(&self, command: &mut RedisCommand) { - self.client.change_command(command); - self.options.apply(command); - } - - #[doc(hidden)] - fn send_command(&self, command: T) -> Result<(), RedisError> - where - T: Into, - { - let mut command: RedisCommand = command.into(); - self.options.apply(&mut command); - self.client.send_command(command) - } -} - -#[cfg(feature = "i-acl")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))] -impl AclInterface for WithOptions {} -#[cfg(feature = "i-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))] -impl ClientInterface for WithOptions {} -#[cfg(feature = "i-cluster")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))] -impl ClusterInterface for WithOptions {} -#[cfg(feature = "i-pubsub")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))] -impl PubsubInterface for WithOptions {} -#[cfg(feature = "i-config")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))] -impl ConfigInterface for WithOptions {} -#[cfg(feature = "i-geo")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))] -impl GeoInterface for WithOptions {} -#[cfg(feature = "i-hashes")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))] -impl HashesInterface for WithOptions {} -#[cfg(feature = "i-hyperloglog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))] -impl HyperloglogInterface for WithOptions {} -#[cfg(feature = "i-keys")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))] -impl KeysInterface for WithOptions {} -#[cfg(feature = "i-lists")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))] -impl ListInterface for WithOptions {} -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -impl MemoryInterface for WithOptions {} -#[cfg(feature = "i-server")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] -impl AuthInterface for WithOptions {} -#[cfg(feature = "i-server")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] -impl ServerInterface for WithOptions {} -#[cfg(feature = "i-slowlog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))] -impl SlowlogInterface for WithOptions {} -#[cfg(feature = "i-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))] -impl SetsInterface for WithOptions {} -#[cfg(feature = "i-sorted-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))] -impl SortedSetsInterface for WithOptions {} -#[cfg(feature = "i-streams")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))] -impl StreamsInterface for WithOptions {} -#[cfg(feature = "i-scripts")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] -impl FunctionInterface for WithOptions {} -#[cfg(feature = "i-redis-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))] -impl RedisJsonInterface for WithOptions {} -#[cfg(feature = "i-time-series")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -impl TimeSeriesInterface for WithOptions {} -#[cfg(feature = "i-redisearch")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))] -impl RediSearchInterface for WithOptions {} diff --git a/src/clients/pipeline.rs b/src/clients/pipeline.rs deleted file mode 100644 index 952092b7..00000000 --- a/src/clients/pipeline.rs +++ /dev/null @@ -1,336 +0,0 @@ -use crate::{ - error::RedisError, - interfaces::{self, *}, - modules::{inner::RedisClientInner, response::FromRedis}, - prelude::{RedisResult, RedisValue}, - protocol::{ - command::{RedisCommand, RouterCommand}, - responders::ResponseKind, - utils as protocol_utils, - }, - runtime::{oneshot_channel, Mutex, OneshotReceiver, RefCount}, - utils, -}; -use std::{collections::VecDeque, fmt, fmt::Formatter}; - -fn clone_buffered_commands(buffer: &Mutex>) -> VecDeque { - let guard = buffer.lock(); - let mut out = VecDeque::with_capacity(guard.len()); - - for command in guard.iter() { - out.push_back(command.duplicate(ResponseKind::Skip)); - } - - out -} - -fn prepare_all_commands( - commands: VecDeque, - error_early: bool, -) -> (RouterCommand, OneshotReceiver>) { - let (tx, rx) = oneshot_channel(); - let expected_responses = commands - .iter() - .fold(0, |count, cmd| count + cmd.response.expected_response_frames()); - - let mut response = ResponseKind::new_buffer_with_size(expected_responses, tx); - response.set_error_early(error_early); - - let commands: Vec = commands - .into_iter() - .enumerate() - .map(|(idx, mut cmd)| { - cmd.response = response.duplicate().unwrap_or(ResponseKind::Skip); - cmd.response.set_expected_index(idx); - cmd - }) - .collect(); - let command = RouterCommand::Pipeline { commands }; - - (command, rx) -} - -/// Send a series of commands in a [pipeline](https://redis.io/docs/manual/pipelining/). -/// -/// See the [all](Self::all), [last](Self::last), and [try_all](Self::try_all) functions for more information. -pub struct Pipeline { - commands: RefCount>>, - client: C, -} - -#[doc(hidden)] -impl Clone for Pipeline { - fn clone(&self) -> Self { - Pipeline { - commands: self.commands.clone(), - client: self.client.clone(), - } - } -} - -impl fmt::Debug for Pipeline { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Pipeline") - .field("client", &self.client.inner().id) - .field("length", &self.commands.lock().len()) - .finish() - } -} - -#[doc(hidden)] -impl From for Pipeline { - fn from(client: C) -> Self { - Pipeline { - client, - commands: RefCount::new(Mutex::new(VecDeque::new())), - } - } -} - -impl ClientLike for Pipeline { - #[doc(hidden)] - fn inner(&self) -> &RefCount { - self.client.inner() - } - - #[doc(hidden)] - fn change_command(&self, command: &mut RedisCommand) { - self.client.change_command(command); - } - - #[doc(hidden)] - #[allow(unused_mut)] - fn send_command(&self, command: T) -> Result<(), RedisError> - where - T: Into, - { - let mut command: RedisCommand = command.into(); - self.change_command(&mut command); - - if let Some(mut tx) = command.take_responder() { - trace!( - "{}: Respond early to {} command in pipeline.", - &self.client.inner().id, - command.kind.to_str_debug() - ); - let _ = tx.send(Ok(protocol_utils::queued_frame())); - } - - self.commands.lock().push_back(command); - Ok(()) - } -} - -#[cfg(feature = "i-acl")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))] -impl AclInterface for Pipeline {} -#[cfg(feature = "i-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))] -impl ClientInterface for Pipeline {} -#[cfg(feature = "i-cluster")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))] -impl ClusterInterface for Pipeline {} -#[cfg(feature = "i-pubsub")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))] -impl PubsubInterface for Pipeline {} -#[cfg(feature = "i-config")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))] -impl ConfigInterface for Pipeline {} -#[cfg(feature = "i-geo")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))] -impl GeoInterface for Pipeline {} -#[cfg(feature = "i-hashes")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))] -impl HashesInterface for Pipeline {} -#[cfg(feature = "i-hyperloglog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))] -impl HyperloglogInterface for Pipeline {} -#[cfg(feature = "i-keys")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))] -impl KeysInterface for Pipeline {} -#[cfg(feature = "i-lists")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))] -impl ListInterface for Pipeline {} -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -impl MemoryInterface for Pipeline {} -#[cfg(feature = "i-server")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] -impl AuthInterface for Pipeline {} -#[cfg(feature = "i-server")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] -impl ServerInterface for Pipeline {} -#[cfg(feature = "i-slowlog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))] -impl SlowlogInterface for Pipeline {} -#[cfg(feature = "i-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))] -impl SetsInterface for Pipeline {} -#[cfg(feature = "i-sorted-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))] -impl SortedSetsInterface for Pipeline {} -#[cfg(feature = "i-streams")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))] -impl StreamsInterface for Pipeline {} -#[cfg(feature = "i-scripts")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] -impl FunctionInterface for Pipeline {} -#[cfg(feature = "i-redis-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))] -impl RedisJsonInterface for Pipeline {} -#[cfg(feature = "i-time-series")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -impl TimeSeriesInterface for Pipeline {} -#[cfg(feature = "i-redisearch")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))] -impl RediSearchInterface for Pipeline {} - -impl Pipeline { - /// Send the pipeline and respond with an array of all responses. - /// - /// ```rust no_run - /// # use fred::prelude::*; - /// async fn example(client: &RedisClient) -> Result<(), RedisError> { - /// let _ = client.mset(vec![("foo", 1), ("bar", 2)]).await?; - /// - /// let pipeline = client.pipeline(); - /// let _: () = pipeline.get("foo").await?; // returns when the command is queued in memory - /// let _: () = pipeline.get("bar").await?; // returns when the command is queued in memory - /// - /// let results: Vec = pipeline.all().await?; - /// assert_eq!(results, vec![1, 2]); - /// Ok(()) - /// } - /// ``` - pub async fn all(&self) -> Result - where - R: FromRedis, - { - let commands = clone_buffered_commands(&self.commands); - send_all(self.client.inner(), commands).await?.convert() - } - - /// Send the pipeline and respond with each individual result. - /// - /// Note: use `RedisValue` as the return type (and [convert](crate::types::RedisValue::convert) as needed) to - /// support an array of different return types. - /// - /// ```rust no_run - /// # use fred::prelude::*; - /// async fn example(client: &RedisClient) -> Result<(), RedisError> { - /// let _ = client.mset(vec![("foo", 1), ("bar", 2)]).await?; - /// - /// let pipeline = client.pipeline(); - /// let _: () = pipeline.get("foo").await?; - /// let _: () = pipeline.hgetall("bar").await?; // this will error since `bar` is an integer - /// - /// let results = pipeline.try_all::().await; - /// assert_eq!(results[0].clone()?.convert::()?, 1); - /// assert!(results[1].is_err()); - /// - /// Ok(()) - /// } - /// ``` - pub async fn try_all(&self) -> Vec> - where - R: FromRedis, - { - let commands = clone_buffered_commands(&self.commands); - try_send_all(self.client.inner(), commands) - .await - .into_iter() - .map(|v| v.and_then(|v| v.convert())) - .collect() - } - - /// Send the pipeline and respond with only the result of the last command. - /// - /// ```rust no_run - /// # use fred::prelude::*; - /// async fn example(client: &RedisClient) -> Result<(), RedisError> { - /// let pipeline = client.pipeline(); - /// let _: () = pipeline.incr("foo").await?; // returns when the command is queued in memory - /// let _: () = pipeline.incr("foo").await?; // returns when the command is queued in memory - /// - /// assert_eq!(pipeline.last::().await?, 2); - /// // pipelines can also be reused - /// assert_eq!(pipeline.last::().await?, 4); - /// Ok(()) - /// } - /// ``` - pub async fn last(&self) -> Result - where - R: FromRedis, - { - let commands = clone_buffered_commands(&self.commands); - send_last(self.client.inner(), commands).await?.convert() - } -} - -async fn try_send_all( - inner: &RefCount, - commands: VecDeque, -) -> Vec> { - if commands.is_empty() { - return Vec::new(); - } - - let (mut command, rx) = prepare_all_commands(commands, false); - command.inherit_options(inner); - let timeout_dur = command.timeout_dur().unwrap_or_else(|| inner.default_command_timeout()); - - if let Err(e) = interfaces::send_to_router(inner, command) { - return vec![Err(e)]; - }; - let frame = match utils::timeout(rx, timeout_dur).await { - Ok(result) => match result { - Ok(f) => f, - Err(e) => return vec![Err(e)], - }, - Err(e) => return vec![Err(e)], - }; - - if let Resp3Frame::Array { data, .. } = frame { - data.into_iter().map(protocol_utils::frame_to_results).collect() - } else { - vec![protocol_utils::frame_to_results(frame)] - } -} - -async fn send_all( - inner: &RefCount, - commands: VecDeque, -) -> Result { - if commands.is_empty() { - return Ok(RedisValue::Array(Vec::new())); - } - - let (mut command, rx) = prepare_all_commands(commands, true); - command.inherit_options(inner); - let timeout_dur = command.timeout_dur().unwrap_or_else(|| inner.default_command_timeout()); - - interfaces::send_to_router(inner, command)?; - let frame = utils::timeout(rx, timeout_dur).await??; - protocol_utils::frame_to_results(frame) -} - -async fn send_last( - inner: &RefCount, - commands: VecDeque, -) -> Result { - if commands.is_empty() { - return Ok(RedisValue::Null); - } - - let len = commands.len(); - let (tx, rx) = oneshot_channel(); - let mut commands: Vec = commands.into_iter().collect(); - commands[len - 1].response = ResponseKind::Respond(Some(tx)); - let mut command = RouterCommand::Pipeline { commands }; - command.inherit_options(inner); - let timeout_dur = command.timeout_dur().unwrap_or_else(|| inner.default_command_timeout()); - - interfaces::send_to_router(inner, command)?; - let frame = utils::timeout(rx, timeout_dur).await??; - protocol_utils::frame_to_results(frame) -} diff --git a/src/clients/pool.rs b/src/clients/pool.rs deleted file mode 100644 index 00b961d8..00000000 --- a/src/clients/pool.rs +++ /dev/null @@ -1,678 +0,0 @@ -use crate::{ - clients::RedisClient, - error::{RedisError, RedisErrorKind}, - interfaces::*, - modules::inner::RedisClientInner, - runtime::{sleep, spawn, AtomicBool, AtomicUsize, RefCount}, - types::{ConnectHandle, ConnectionConfig, PerformanceConfig, ReconnectPolicy, RedisConfig, Server}, - utils, -}; -use futures::future::{join_all, try_join_all}; -use rm_send_macros::rm_send_if; -use std::{fmt, future::Future, time::Duration}; - -#[cfg(feature = "replicas")] -use crate::clients::Replicas; -#[cfg(feature = "dns")] -use crate::protocol::types::Resolve; -#[cfg(not(feature = "glommio"))] -pub use tokio::sync::{Mutex as AsyncMutex, OwnedMutexGuard}; - -struct RedisPoolInner { - clients: Vec, - counter: AtomicUsize, - prefer_connected: AtomicBool, -} - -/// A cheaply cloneable round-robin client pool. -/// -/// ### Restrictions -/// -/// The following interfaces are not implemented on `RedisPool`: -/// * [MetricsInterface](crate::interfaces::MetricsInterface) -/// * [PubsubInterface](crate::interfaces::PubsubInterface) -/// * [EventInterface](crate::interfaces::EventInterface) -/// * [ClientInterface](crate::interfaces::ClientInterface) -/// * [AuthInterface](crate::interfaces::AuthInterface) -/// -/// In many cases, such as [publish](crate::interfaces::PubsubInterface::publish), callers can work around this by -/// adding a call to [next](Self::next), but in some scenarios this may not work. As a general rule, any commands -/// that change or depend on local connection state will not be implemented directly on `RedisPool`. Callers can use -/// [clients](Self::clients), [next](Self::next), or [last](Self::last) to operate on individual clients if needed. -#[derive(Clone)] -pub struct RedisPool { - inner: RefCount, -} - -impl fmt::Debug for RedisPool { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("RedisPool") - .field("size", &self.inner.clients.len()) - .field( - "prefer_connected", - &utils::read_bool_atomic(&self.inner.prefer_connected), - ) - .finish() - } -} - -impl RedisPool { - /// Create a new pool from an existing set of clients. - pub fn from_clients(clients: Vec) -> Result { - if clients.is_empty() { - Err(RedisError::new(RedisErrorKind::Config, "Pool cannot be empty.")) - } else { - Ok(RedisPool { - inner: RefCount::new(RedisPoolInner { - clients, - counter: AtomicUsize::new(0), - prefer_connected: AtomicBool::new(true), - }), - }) - } - } - - /// Create a new pool without connecting to the server. - /// - /// See the [builder](crate::types::Builder) interface for more information. - pub fn new( - config: RedisConfig, - perf: Option, - connection: Option, - policy: Option, - size: usize, - ) -> Result { - if size == 0 { - Err(RedisError::new(RedisErrorKind::Config, "Pool cannot be empty.")) - } else { - let mut clients = Vec::with_capacity(size); - for _ in 0 .. size { - clients.push(RedisClient::new( - config.clone(), - perf.clone(), - connection.clone(), - policy.clone(), - )); - } - - Ok(RedisPool { - inner: RefCount::new(RedisPoolInner { - clients, - counter: AtomicUsize::new(0), - prefer_connected: AtomicBool::new(true), - }), - }) - } - } - - /// Set whether the client will use [next_connected](Self::next_connected) or [next](Self::next) when routing - /// commands among the pooled clients. - pub fn prefer_connected(&self, val: bool) -> bool { - utils::set_bool_atomic(&self.inner.prefer_connected, val) - } - - /// Read the individual clients in the pool. - pub fn clients(&self) -> &[RedisClient] { - &self.inner.clients - } - - /// Connect each client to the server, returning the task driving each connection. - /// - /// Use the base [connect](Self::connect) function to return one handle that drives all connections via [join](https://docs.rs/futures/latest/futures/macro.join.html). - pub fn connect_pool(&self) -> Vec { - self.inner.clients.iter().map(|c| c.connect()).collect() - } - - /// Read the size of the pool. - pub fn size(&self) -> usize { - self.inner.clients.len() - } - - /// Read the next connected client that should run the next command. - pub fn next_connected(&self) -> &RedisClient { - let mut idx = utils::incr_atomic(&self.inner.counter) % self.inner.clients.len(); - - for _ in 0 .. self.inner.clients.len() { - let client = &self.inner.clients[idx]; - if client.is_connected() { - return client; - } - idx = (idx + 1) % self.inner.clients.len(); - } - - &self.inner.clients[idx] - } - - /// Read the client that should run the next command. - pub fn next(&self) -> &RedisClient { - &self.inner.clients[utils::incr_atomic(&self.inner.counter) % self.inner.clients.len()] - } - - /// Read the client that ran the last command. - pub fn last(&self) -> &RedisClient { - &self.inner.clients[utils::read_atomic(&self.inner.counter) % self.inner.clients.len()] - } - - /// Create a client that interacts with the replica nodes associated with the [next](Self::next) client. - #[cfg(feature = "replicas")] - #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] - pub fn replicas(&self) -> Replicas { - Replicas::from(self.inner()) - } -} - -#[rm_send_if(feature = "glommio")] -impl ClientLike for RedisPool { - #[doc(hidden)] - fn inner(&self) -> &RefCount { - if utils::read_bool_atomic(&self.inner.prefer_connected) { - &self.next_connected().inner - } else { - &self.next().inner - } - } - - /// Update the internal [PerformanceConfig](crate::types::PerformanceConfig) on each client in place with new - /// values. - fn update_perf_config(&self, config: PerformanceConfig) { - for client in self.inner.clients.iter() { - client.update_perf_config(config.clone()); - } - } - - /// Read the set of active connections across all clients in the pool. - fn active_connections(&self) -> impl Future, RedisError>> + Send { - async move { - let all_connections = try_join_all(self.inner.clients.iter().map(|c| c.active_connections())).await?; - let total_size = if all_connections.is_empty() { - return Ok(Vec::new()); - } else { - all_connections.len() * all_connections[0].len() - }; - let mut out = Vec::with_capacity(total_size); - - for connections in all_connections.into_iter() { - out.extend(connections); - } - Ok(out) - } - } - - /// Override the DNS resolution logic for all clients in the pool. - #[cfg(feature = "dns")] - #[cfg_attr(docsrs, doc(cfg(feature = "dns")))] - #[allow(refining_impl_trait)] - fn set_resolver(&self, resolver: RefCount) -> impl Future + Send { - async move { - for client in self.inner.clients.iter() { - client.set_resolver(resolver.clone()).await; - } - } - } - - /// Connect each client to the server. - /// - /// This function returns a `JoinHandle` to a task that drives **all** connections via [join](https://docs.rs/futures/latest/futures/macro.join.html). - /// - /// See [connect_pool](crate::clients::RedisPool::connect_pool) for a variation of this function that separates the - /// connection tasks. - /// - /// See [init](Self::init) for an alternative shorthand. - fn connect(&self) -> ConnectHandle { - let clients = self.inner.clients.clone(); - spawn(async move { - let tasks: Vec<_> = clients.iter().map(|c| c.connect()).collect(); - for result in join_all(tasks).await.into_iter() { - result??; - } - - Ok::<(), RedisError>(()) - }) - } - - /// Force a reconnection to the server(s) for each client. - /// - /// When running against a cluster this function will also refresh the cached cluster routing table. - fn force_reconnection(&self) -> impl Future> + Send { - async move { - try_join_all(self.inner.clients.iter().map(|c| c.force_reconnection())).await?; - Ok(()) - } - } - - /// Wait for all the clients to connect to the server. - fn wait_for_connect(&self) -> impl Future> + Send { - async move { - try_join_all(self.inner.clients.iter().map(|c| c.wait_for_connect())).await?; - Ok(()) - } - } - - /// Initialize a new routing and connection task for each client and wait for them to connect successfully. - /// - /// The returned [ConnectHandle](crate::types::ConnectHandle) refers to the task that drives the routing and - /// connection layer for each client via [join](https://docs.rs/futures/latest/futures/macro.join.html). It will not finish until the max reconnection count is reached. - /// - /// Callers can also use [connect](Self::connect) and [wait_for_connect](Self::wait_for_connect) separately if - /// needed. - /// - /// ```rust - /// use fred::prelude::*; - /// - /// #[tokio::main] - /// async fn main() -> Result<(), RedisError> { - /// let pool = Builder::default_centralized().build_pool(5)?; - /// let connection_task = pool.init().await?; - /// - /// // ... - /// - /// pool.quit().await?; - /// connection_task.await? - /// } - /// ``` - fn init(&self) -> impl Future> + Send { - async move { - let rxs: Vec<_> = self.inner.clients.iter().map(|c| c.wait_for_connect()).collect(); - - let connect_task = self.connect(); - let init_err = futures::future::join_all(rxs).await.into_iter().find_map(|r| r.err()); - - if let Some(err) = init_err { - for client in self.inner.clients.iter() { - utils::reset_router_task(client.inner()); - } - - Err(err) - } else { - Ok(connect_task) - } - } - } - - /// Close the connection to the Redis server for each client. The returned future resolves when the command has been - /// written to the socket, not when the connection has been fully closed. Some time after this future resolves the - /// future returned by [connect](Self::connect) will resolve which indicates that the connection has been fully - /// closed. - /// - /// This function will also close all error, pubsub message, and reconnection event streams on all clients in the - /// pool. - fn quit(&self) -> impl Future> + Send { - async move { - join_all(self.inner.clients.iter().map(|c| c.quit())).await; - - Ok(()) - } - } -} - -#[rm_send_if(feature = "glommio")] -impl HeartbeatInterface for RedisPool { - fn enable_heartbeat( - &self, - interval: Duration, - break_on_error: bool, - ) -> impl Future> + Send { - async move { - loop { - sleep(interval).await; - - if let Err(error) = try_join_all(self.inner.clients.iter().map(|c| c.ping::<()>())).await { - if break_on_error { - return Err(error); - } - } - } - } - } -} - -#[cfg(feature = "i-acl")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))] -impl AclInterface for RedisPool {} -#[cfg(feature = "i-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))] -impl ClientInterface for RedisPool {} -#[cfg(feature = "i-cluster")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))] -impl ClusterInterface for RedisPool {} -#[cfg(feature = "i-config")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))] -impl ConfigInterface for RedisPool {} -#[cfg(feature = "i-geo")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))] -impl GeoInterface for RedisPool {} -#[cfg(feature = "i-hashes")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))] -impl HashesInterface for RedisPool {} -#[cfg(feature = "i-hyperloglog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))] -impl HyperloglogInterface for RedisPool {} -#[cfg(feature = "transactions")] -#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))] -impl TransactionInterface for RedisPool {} -#[cfg(feature = "i-keys")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))] -impl KeysInterface for RedisPool {} -#[cfg(feature = "i-scripts")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] -impl LuaInterface for RedisPool {} -#[cfg(feature = "i-lists")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))] -impl ListInterface for RedisPool {} -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -impl MemoryInterface for RedisPool {} -#[cfg(feature = "i-server")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] -impl ServerInterface for RedisPool {} -#[cfg(feature = "i-slowlog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))] -impl SlowlogInterface for RedisPool {} -#[cfg(feature = "i-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))] -impl SetsInterface for RedisPool {} -#[cfg(feature = "i-sorted-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))] -impl SortedSetsInterface for RedisPool {} -#[cfg(feature = "i-streams")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))] -impl StreamsInterface for RedisPool {} -#[cfg(feature = "i-scripts")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] -impl FunctionInterface for RedisPool {} -#[cfg(feature = "i-redis-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))] -impl RedisJsonInterface for RedisPool {} -#[cfg(feature = "i-time-series")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -impl TimeSeriesInterface for RedisPool {} -#[cfg(feature = "i-redisearch")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))] -impl RediSearchInterface for RedisPool {} - -#[cfg(not(feature = "glommio"))] -struct PoolInner { - clients: Vec>>, - counter: AtomicUsize, -} - -/// A cheaply cloneable round-robin client pool that provides exclusive ownership over the inner clients. -/// -/// This interface can be used when callers require exclusive ownership over the connection. For example, -/// -/// ```no_run no_compile -/// WATCH foo -/// foo = GET foo -/// if foo > 1: -/// MULTI -/// INCR foo -/// INCR bar -/// INCR baz -/// EXEC -/// ``` -/// -/// Unlike [RedisPool](crate::clients::RedisPool), this pooling interface does not directly implement -/// [ClientLike](crate::interfaces::ClientLike). Callers acquire and release clients via the returned -/// [MutexGuard](OwnedMutexGuard). -/// -/// ```rust -/// use fred::{ -/// clients::{ExclusivePool, RedisPool}, -/// prelude::*, -/// }; -/// -/// async fn example() -> Result<(), RedisError> { -/// let builder = Builder::default_centralized(); -/// let shared_pool = builder.build_pool(5)?; -/// let exclusive_pool = builder.build_exclusive_pool(5)?; -/// shared_pool.init().await?; -/// exclusive_pool.init().await?; -/// -/// // since `RedisPool` implements `ClientLike` we can use most command interfaces directly -/// let foo: Option = shared_pool.set("foo", 1, None, None, false).await?; -/// -/// // with an `ExclusivePool` callers acquire and release clients with an async lock guard -/// let results: Option<(i64, i64, i64)> = { -/// let client = exclusive_pool.acquire().await; -/// -/// client.watch("foo").await?; -/// if let Some(1) = client.get::, _>("foo").await? { -/// let trx = client.multi(); -/// trx.incr("foo").await?; -/// trx.incr("bar").await?; -/// trx.incr("baz").await?; -/// Some(trx.exec(true).await?) -/// } else { -/// None -/// } -/// }; -/// assert_eq!(results, Some((2, 1, 1))); -/// -/// Ok(()) -/// } -/// ``` -/// -/// Callers should avoid cloning the inner clients, if possible. -#[cfg(not(feature = "glommio"))] -#[derive(Clone)] -pub struct ExclusivePool { - inner: RefCount, -} - -#[cfg(not(feature = "glommio"))] -impl fmt::Debug for ExclusivePool { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ExclusivePool") - .field("size", &self.inner.clients.len()) - .finish() - } -} - -#[cfg(not(feature = "glommio"))] -impl ExclusivePool { - /// Create a new pool without connecting to the server. - /// - /// See the [builder](crate::types::Builder) interface for more information. - pub fn new( - config: RedisConfig, - perf: Option, - connection: Option, - policy: Option, - size: usize, - ) -> Result { - if size == 0 { - Err(RedisError::new(RedisErrorKind::Config, "Pool cannot be empty.")) - } else { - let mut clients = Vec::with_capacity(size); - for _ in 0 .. size { - clients.push(RefCount::new(AsyncMutex::new(RedisClient::new( - config.clone(), - perf.clone(), - connection.clone(), - policy.clone(), - )))); - } - - Ok(ExclusivePool { - inner: RefCount::new(PoolInner { - clients, - counter: AtomicUsize::new(0), - }), - }) - } - } - - /// Read the clients in the pool. - pub fn clients(&self) -> &[RefCount>] { - &self.inner.clients - } - - /// Connect each client to the server, returning the task driving each connection. - /// - /// Use the base [connect](Self::connect) function to return one handle that drives all connections via [join](https://docs.rs/futures/latest/futures/macro.join.html). - pub async fn connect_pool(&self) -> Vec { - let mut connect_tasks = Vec::with_capacity(self.inner.clients.len()); - for locked_client in self.inner.clients.iter() { - connect_tasks.push(locked_client.lock().await.connect()); - } - connect_tasks - } - - /// Connect each client to the server. - /// - /// This function returns a `JoinHandle` to a task that drives **all** connections via [join](https://docs.rs/futures/latest/futures/macro.join.html). - /// - /// See [connect_pool](crate::clients::RedisPool::connect_pool) for a variation of this function that separates the - /// connection tasks. - /// - /// See [init](Self::init) for an alternative shorthand. - pub async fn connect(&self) -> ConnectHandle { - let tasks = self.connect_pool().await; - tokio::spawn(async move { - for result in join_all(tasks).await.into_iter() { - result??; - } - - Ok(()) - }) - } - - /// Force a reconnection to the server(s) for each client. - /// - /// When running against a cluster this function will also refresh the cached cluster routing table. - pub async fn force_reconnection(&self) -> RedisResult<()> { - let mut fts = Vec::with_capacity(self.inner.clients.len()); - for locked_client in self.inner.clients.iter() { - let client = locked_client.clone(); - fts.push(async move { client.lock_owned().await.force_reconnection().await }); - } - - try_join_all(fts).await?; - Ok(()) - } - - /// Wait for all the clients to connect to the server. - pub async fn wait_for_connect(&self) -> RedisResult<()> { - let mut fts = Vec::with_capacity(self.inner.clients.len()); - for locked_client in self.inner.clients.iter() { - let client = locked_client.clone(); - fts.push(async move { client.lock().await.wait_for_connect().await }); - } - - try_join_all(fts).await?; - Ok(()) - } - - /// Initialize a new routing and connection task for each client and wait for them to connect successfully. - /// - /// The returned [ConnectHandle](crate::types::ConnectHandle) refers to the task that drives the routing and - /// connection layer for each client. It will not finish until the max reconnection count is reached. - /// - /// Callers can also use [connect](Self::connect) and [wait_for_connect](Self::wait_for_connect) separately if - /// needed. - /// - /// ```rust - /// use fred::prelude::*; - /// - /// #[tokio::main] - /// async fn main() -> Result<(), RedisError> { - /// let pool = Builder::default_centralized().build_exclusive_pool(5)?; - /// let connection_task = pool.init().await?; - /// - /// // ... - /// - /// pool.quit().await?; - /// connection_task.await? - /// } - /// ``` - pub async fn init(&self) -> RedisResult { - let mut rxs = Vec::with_capacity(self.inner.clients.len()); - for locked_client in self.inner.clients.iter() { - let mut rx = { - locked_client - .lock() - .await - .inner - .notifications - .connect - .load() - .subscribe() - }; - - rxs.push(async move { rx.recv().await }); - } - - let connect_task = self.connect().await; - let init_err = join_all(rxs).await.into_iter().find_map(|r| match r { - Ok(Err(e)) => Some(e), - Err(e) => Some(e.into()), - _ => None, - }); - - if let Some(err) = init_err { - for client in self.inner.clients.iter() { - utils::reset_router_task(client.lock().await.inner()); - } - - Err(err) - } else { - Ok(connect_task) - } - } - - /// Read the size of the pool. - pub fn size(&self) -> usize { - self.inner.clients.len() - } - - /// Read the client that should run the next command. - pub async fn acquire(&self) -> OwnedMutexGuard { - let mut idx = utils::incr_atomic(&self.inner.counter) % self.inner.clients.len(); - - for _ in 0 .. self.inner.clients.len() { - if let Ok(client) = self.inner.clients[idx].clone().try_lock_owned() { - return client; - } - - idx = (idx + 1) % self.inner.clients.len(); - } - - self.inner.clients[idx].clone().lock_owned().await - } - - /// Update the internal [PerformanceConfig](crate::types::PerformanceConfig) on each client in place with new - /// values. - pub async fn update_perf_config(&self, config: PerformanceConfig) { - for client in self.inner.clients.iter() { - client.lock().await.update_perf_config(config.clone()); - } - } - - /// Override the DNS resolution logic for all clients in the pool. - #[cfg(feature = "dns")] - #[cfg_attr(docsrs, doc(cfg(feature = "dns")))] - #[allow(refining_impl_trait)] - pub async fn set_resolver(&self, resolver: RefCount) { - for client in self.inner.clients.iter() { - client.lock().await.set_resolver(resolver.clone()).await; - } - } - - /// Close the connection to the Redis server for each client. The returned future resolves when the command has been - /// written to the socket, not when the connection has been fully closed. Some time after this future resolves the - /// future returned by [connect](Self::connect) will resolve which indicates that the connection has been fully - /// closed. - /// - /// This function will also close all error, pubsub message, and reconnection event streams on all clients in the - /// pool. - pub async fn quit(&self) -> RedisResult<()> { - let mut fts = Vec::with_capacity(self.inner.clients.len()); - for locked_client in self.inner.clients.iter() { - let client = locked_client.clone(); - fts.push(async move { client.lock().await.quit().await }); - } - - join_all(fts).await; - Ok(()) - } -} diff --git a/src/clients/pubsub.rs b/src/clients/pubsub.rs deleted file mode 100644 index c16dcffd..00000000 --- a/src/clients/pubsub.rs +++ /dev/null @@ -1,425 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::*, - modules::inner::RedisClientInner, - prelude::RedisClient, - runtime::{spawn, JoinHandle, RefCount, RwLock}, - types::{ConnectionConfig, MultipleStrings, PerformanceConfig, ReconnectPolicy, RedisConfig, RedisKey}, - util::group_by_hash_slot, -}; -use bytes_utils::Str; -use rm_send_macros::rm_send_if; -use std::{collections::BTreeSet, fmt, fmt::Formatter, future::Future, mem}; - -type ChannelSet = RefCount>>; - -/// A subscriber client that will manage subscription state to any [pubsub](https://redis.io/docs/manual/pubsub/) channels or patterns for the caller. -/// -/// If the connection to the server closes for any reason this struct can automatically re-subscribe to channels, -/// patterns, and sharded channels. -/// -/// **Note: most non-pubsub commands are only supported when using RESP3.** -/// -/// ```rust no_run -/// use fred::clients::SubscriberClient; -/// use fred::prelude::*; -/// -/// async fn example() -> Result<(), RedisError> { -/// let subscriber = Builder::default_centralized().build_subscriber_client()?; -/// subscriber.init().await?; -/// -/// // spawn a task that will re-subscribe to channels and patterns after reconnecting -/// let _ = subscriber.manage_subscriptions(); -/// -/// let mut message_rx = subscriber.message_rx(); -/// let jh = tokio::spawn(async move { -/// while let Ok(message) = message_rx.recv().await { -/// println!("Recv message {:?} on channel {}", message.value, message.channel); -/// } -/// }); -/// -/// let _ = subscriber.subscribe("foo").await?; -/// let _ = subscriber.psubscribe("bar*").await?; -/// println!("Tracking channels: {:?}", subscriber.tracked_channels()); // foo -/// println!("Tracking patterns: {:?}", subscriber.tracked_patterns()); // bar* -/// -/// // force a re-subscription -/// subscriber.resubscribe_all().await?; -/// // clear all the local state and unsubscribe -/// subscriber.unsubscribe_all().await?; -/// subscriber.quit().await?; -/// Ok(()) -/// } -/// ``` -#[derive(Clone)] -#[cfg_attr(docsrs, doc(cfg(feature = "subscriber-client")))] -pub struct SubscriberClient { - channels: ChannelSet, - patterns: ChannelSet, - shard_channels: ChannelSet, - inner: RefCount, -} - -impl fmt::Debug for SubscriberClient { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("SubscriberClient") - .field("id", &self.inner.id) - .field("channels", &self.tracked_channels()) - .field("patterns", &self.tracked_patterns()) - .field("shard_channels", &self.tracked_shard_channels()) - .finish() - } -} - -impl ClientLike for SubscriberClient { - #[doc(hidden)] - fn inner(&self) -> &RefCount { - &self.inner - } -} - -impl EventInterface for SubscriberClient {} -#[cfg(feature = "i-acl")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))] -impl AclInterface for SubscriberClient {} -#[cfg(feature = "i-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))] -impl ClientInterface for SubscriberClient {} -#[cfg(feature = "i-cluster")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))] -impl ClusterInterface for SubscriberClient {} -#[cfg(feature = "i-config")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))] -impl ConfigInterface for SubscriberClient {} -#[cfg(feature = "i-geo")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))] -impl GeoInterface for SubscriberClient {} -#[cfg(feature = "i-hashes")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))] -impl HashesInterface for SubscriberClient {} -#[cfg(feature = "i-hyperloglog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))] -impl HyperloglogInterface for SubscriberClient {} -impl MetricsInterface for SubscriberClient {} -#[cfg(feature = "transactions")] -#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))] -impl TransactionInterface for SubscriberClient {} -#[cfg(feature = "i-keys")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))] -impl KeysInterface for SubscriberClient {} -#[cfg(feature = "i-scripts")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] -impl LuaInterface for SubscriberClient {} -#[cfg(feature = "i-lists")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))] -impl ListInterface for SubscriberClient {} -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -impl MemoryInterface for SubscriberClient {} -impl AuthInterface for SubscriberClient {} -#[cfg(feature = "i-server")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] -impl ServerInterface for SubscriberClient {} -#[cfg(feature = "i-slowlog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))] -impl SlowlogInterface for SubscriberClient {} -#[cfg(feature = "i-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))] -impl SetsInterface for SubscriberClient {} -#[cfg(feature = "i-sorted-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))] -impl SortedSetsInterface for SubscriberClient {} -#[cfg(feature = "i-server")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] -impl HeartbeatInterface for SubscriberClient {} -#[cfg(feature = "i-streams")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))] -impl StreamsInterface for SubscriberClient {} -#[cfg(feature = "i-scripts")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] -impl FunctionInterface for SubscriberClient {} -#[cfg(feature = "i-redis-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))] -impl RedisJsonInterface for SubscriberClient {} -#[cfg(feature = "i-time-series")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -impl TimeSeriesInterface for SubscriberClient {} -#[cfg(feature = "i-tracking")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] -impl TrackingInterface for SubscriberClient {} -#[cfg(feature = "i-redisearch")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))] -impl RediSearchInterface for SubscriberClient {} - -#[cfg(feature = "i-pubsub")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))] -#[rm_send_if(feature = "glommio")] -impl PubsubInterface for SubscriberClient { - fn subscribe(&self, channels: S) -> impl Future> + Send - where - S: Into + Send, - { - into!(channels); - - async move { - let result = commands::pubsub::subscribe(self, channels.clone()).await; - if result.is_ok() { - let mut guard = self.channels.write(); - - for channel in channels.inner().into_iter() { - if let Some(channel) = channel.as_bytes_str() { - guard.insert(channel); - } - } - } - - result - } - } - - fn unsubscribe(&self, channels: S) -> impl Future> + Send - where - S: Into + Send, - { - into!(channels); - - async move { - let result = commands::pubsub::unsubscribe(self, channels.clone()).await; - if result.is_ok() { - let mut guard = self.channels.write(); - - if channels.len() == 0 { - guard.clear(); - } else { - for channel in channels.inner().into_iter() { - if let Some(channel) = channel.as_bytes_str() { - let _ = guard.remove(&channel); - } - } - } - } - result - } - } - - fn psubscribe(&self, patterns: S) -> impl Future> + Send - where - S: Into + Send, - { - into!(patterns); - - async move { - let result = commands::pubsub::psubscribe(self, patterns.clone()).await; - if result.is_ok() { - let mut guard = self.patterns.write(); - - for pattern in patterns.inner().into_iter() { - if let Some(pattern) = pattern.as_bytes_str() { - guard.insert(pattern); - } - } - } - result - } - } - - fn punsubscribe(&self, patterns: S) -> impl Future> + Send - where - S: Into + Send, - { - into!(patterns); - - async move { - let result = commands::pubsub::punsubscribe(self, patterns.clone()).await; - if result.is_ok() { - let mut guard = self.patterns.write(); - - if patterns.len() == 0 { - guard.clear(); - } else { - for pattern in patterns.inner().into_iter() { - if let Some(pattern) = pattern.as_bytes_str() { - let _ = guard.remove(&pattern); - } - } - } - } - result - } - } - - fn ssubscribe(&self, channels: C) -> impl Future> + Send - where - C: Into + Send, - { - into!(channels); - - async move { - let result = commands::pubsub::ssubscribe(self, channels.clone()).await; - if result.is_ok() { - let mut guard = self.shard_channels.write(); - - for channel in channels.inner().into_iter() { - if let Some(channel) = channel.as_bytes_str() { - guard.insert(channel); - } - } - } - result - } - } - - fn sunsubscribe(&self, channels: C) -> impl Future> + Send - where - C: Into + Send, - { - into!(channels); - - async move { - let result = commands::pubsub::sunsubscribe(self, channels.clone()).await; - if result.is_ok() { - let mut guard = self.shard_channels.write(); - - if channels.len() == 0 { - guard.clear(); - } else { - for channel in channels.inner().into_iter() { - if let Some(channel) = channel.as_bytes_str() { - let _ = guard.remove(&channel); - } - } - } - } - result - } - } -} - -impl SubscriberClient { - /// Create a new client instance without connecting to the server. - /// - /// See the [builder](crate::types::Builder) interface for more information. - pub fn new( - config: RedisConfig, - perf: Option, - connection: Option, - policy: Option, - ) -> SubscriberClient { - SubscriberClient { - channels: RefCount::new(RwLock::new(BTreeSet::new())), - patterns: RefCount::new(RwLock::new(BTreeSet::new())), - shard_channels: RefCount::new(RwLock::new(BTreeSet::new())), - inner: RedisClientInner::new(config, perf.unwrap_or_default(), connection.unwrap_or_default(), policy), - } - } - - /// Create a new `SubscriberClient` from the config provided to this client. - /// - /// The returned client will not be connected to the server, and it will use new connections after connecting. - /// However, it will manage the same channel subscriptions as the original client. - pub fn clone_new(&self) -> Self { - let inner = RedisClientInner::new( - self.inner.config.as_ref().clone(), - self.inner.performance_config(), - self.inner.connection.as_ref().clone(), - self.inner.reconnect_policy(), - ); - - SubscriberClient { - inner, - channels: RefCount::new(RwLock::new(self.channels.read().clone())), - patterns: RefCount::new(RwLock::new(self.patterns.read().clone())), - shard_channels: RefCount::new(RwLock::new(self.shard_channels.read().clone())), - } - } - - /// Spawn a task that will automatically re-subscribe to any channels or channel patterns used by the client. - pub fn manage_subscriptions(&self) -> JoinHandle<()> { - let _self = self.clone(); - spawn(async move { - #[allow(unused_mut)] - let mut stream = _self.reconnect_rx(); - - while let Ok(_) = stream.recv().await { - if let Err(error) = _self.resubscribe_all().await { - error!( - "{}: Failed to resubscribe to channels or patterns: {:?}", - _self.id(), - error - ); - } - } - }) - } - - /// Read the set of channels that this client will manage. - pub fn tracked_channels(&self) -> BTreeSet { - self.channels.read().clone() - } - - /// Read the set of channel patterns that this client will manage. - pub fn tracked_patterns(&self) -> BTreeSet { - self.patterns.read().clone() - } - - /// Read the set of shard channels that this client will manage. - pub fn tracked_shard_channels(&self) -> BTreeSet { - self.shard_channels.read().clone() - } - - /// Re-subscribe to any tracked channels and patterns. - /// - /// This can be used to sync the client's subscriptions with the server after calling `QUIT`, then `connect`, etc. - pub async fn resubscribe_all(&self) -> Result<(), RedisError> { - let channels: Vec = self.tracked_channels().into_iter().map(|s| s.into()).collect(); - let patterns: Vec = self.tracked_patterns().into_iter().map(|s| s.into()).collect(); - let shard_channels: Vec = self.tracked_shard_channels().into_iter().map(|s| s.into()).collect(); - - self.subscribe(channels).await?; - self.psubscribe(patterns).await?; - - let shard_channel_groups = group_by_hash_slot(shard_channels)?; - for (_, keys) in shard_channel_groups.into_iter() { - // the client never pipelines this so no point in using join! or a pipeline here - self.ssubscribe(keys).await?; - } - - Ok(()) - } - - /// Unsubscribe from all tracked channels and patterns, and remove them from the client cache. - pub async fn unsubscribe_all(&self) -> Result<(), RedisError> { - let channels: Vec = mem::take(&mut *self.channels.write()) - .into_iter() - .map(|s| s.into()) - .collect(); - let patterns: Vec = mem::take(&mut *self.patterns.write()) - .into_iter() - .map(|s| s.into()) - .collect(); - let shard_channels: Vec = mem::take(&mut *self.shard_channels.write()) - .into_iter() - .map(|s| s.into()) - .collect(); - - self.unsubscribe(channels).await?; - self.punsubscribe(patterns).await?; - - let shard_channel_groups = group_by_hash_slot(shard_channels)?; - let shard_subscriptions: Vec<_> = shard_channel_groups - .into_iter() - .map(|(_, keys)| async { self.sunsubscribe(keys).await }) - .collect(); - - futures::future::try_join_all(shard_subscriptions).await?; - Ok(()) - } - - /// Create a new `RedisClient`, reusing the existing connection(s). - /// - /// Note: most non-pubsub commands are only supported when using RESP3. - pub fn to_client(&self) -> RedisClient { - RedisClient::from(&self.inner) - } -} diff --git a/src/clients/redis.rs b/src/clients/redis.rs deleted file mode 100644 index 1057e212..00000000 --- a/src/clients/redis.rs +++ /dev/null @@ -1,366 +0,0 @@ -use crate::{ - clients::{Pipeline, WithOptions}, - commands, - error::{RedisError, RedisErrorKind}, - interfaces::*, - modules::inner::RedisClientInner, - prelude::ClientLike, - runtime::RefCount, - types::*, -}; -use bytes_utils::Str; -use futures::Stream; -use std::{fmt, fmt::Formatter}; - -#[cfg(feature = "replicas")] -use crate::clients::Replicas; -#[cfg(feature = "i-tracking")] -use crate::interfaces::TrackingInterface; - -/// A cheaply cloneable Redis client struct. -#[derive(Clone)] -pub struct RedisClient { - pub(crate) inner: RefCount, -} - -impl Default for RedisClient { - fn default() -> Self { - RedisClient::new(RedisConfig::default(), None, None, None) - } -} - -impl fmt::Debug for RedisClient { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RedisClient") - .field("id", &self.inner.id) - .field("state", &self.state()) - .finish() - } -} - -impl fmt::Display for RedisClient { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.inner.id) - } -} - -#[doc(hidden)] -impl<'a> From<&'a RefCount> for RedisClient { - fn from(inner: &'a RefCount) -> RedisClient { - RedisClient { inner: inner.clone() } - } -} - -impl ClientLike for RedisClient { - #[doc(hidden)] - fn inner(&self) -> &RefCount { - &self.inner - } -} - -impl EventInterface for RedisClient {} -#[cfg(feature = "i-redis-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))] -impl RedisJsonInterface for RedisClient {} -#[cfg(feature = "i-time-series")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -impl TimeSeriesInterface for RedisClient {} -#[cfg(feature = "i-acl")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))] -impl AclInterface for RedisClient {} -#[cfg(feature = "i-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))] -impl ClientInterface for RedisClient {} -#[cfg(feature = "i-cluster")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))] -impl ClusterInterface for RedisClient {} -#[cfg(feature = "i-config")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))] -impl ConfigInterface for RedisClient {} -#[cfg(feature = "i-geo")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))] -impl GeoInterface for RedisClient {} -#[cfg(feature = "i-hashes")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))] -impl HashesInterface for RedisClient {} -#[cfg(feature = "i-hyperloglog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))] -impl HyperloglogInterface for RedisClient {} -impl MetricsInterface for RedisClient {} -#[cfg(feature = "transactions")] -#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))] -impl TransactionInterface for RedisClient {} -#[cfg(feature = "i-keys")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))] -impl KeysInterface for RedisClient {} -#[cfg(feature = "i-scripts")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] -impl LuaInterface for RedisClient {} -#[cfg(feature = "i-lists")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))] -impl ListInterface for RedisClient {} -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -impl MemoryInterface for RedisClient {} -impl AuthInterface for RedisClient {} -#[cfg(feature = "i-server")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] -impl ServerInterface for RedisClient {} -#[cfg(feature = "i-slowlog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))] -impl SlowlogInterface for RedisClient {} -#[cfg(feature = "i-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))] -impl SetsInterface for RedisClient {} -#[cfg(feature = "i-sorted-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))] -impl SortedSetsInterface for RedisClient {} -#[cfg(feature = "i-server")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] -impl HeartbeatInterface for RedisClient {} -#[cfg(feature = "i-streams")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))] -impl StreamsInterface for RedisClient {} -#[cfg(feature = "i-scripts")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] -impl FunctionInterface for RedisClient {} -#[cfg(feature = "i-tracking")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] -impl TrackingInterface for RedisClient {} -#[cfg(feature = "i-pubsub")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))] -impl PubsubInterface for RedisClient {} -#[cfg(feature = "i-redisearch")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))] -impl RediSearchInterface for RedisClient {} - -impl RedisClient { - /// Create a new client instance without connecting to the server. - /// - /// See the [builder](crate::types::Builder) interface for more information. - pub fn new( - config: RedisConfig, - perf: Option, - connection: Option, - policy: Option, - ) -> RedisClient { - RedisClient { - inner: RedisClientInner::new(config, perf.unwrap_or_default(), connection.unwrap_or_default(), policy), - } - } - - /// Create a new `RedisClient` from the config provided to this client. - /// - /// The returned client will **not** be connected to the server. - pub fn clone_new(&self) -> Self { - let mut policy = self.inner.policy.read().clone(); - if let Some(policy) = policy.as_mut() { - policy.reset_attempts(); - } - - RedisClient::new( - self.inner.config.as_ref().clone(), - Some(self.inner.performance_config()), - Some(self.inner.connection_config()), - policy, - ) - } - - /// Split a clustered Redis client into a set of centralized clients - one for each primary node in the cluster. - /// - /// Alternatively, callers can use [with_cluster_node](crate::clients::RedisClient::with_cluster_node) to avoid - /// creating new connections. - /// - /// The clients returned by this function will not be connected to their associated servers. The caller needs to - /// call `connect` on each client before sending any commands. - pub fn split_cluster(&self) -> Result, RedisError> { - if self.inner.config.server.is_clustered() { - commands::server::split(&self.inner) - } else { - Err(RedisError::new( - RedisErrorKind::Unknown, - "Client is not using a clustered deployment.", - )) - } - } - - // --------------- SCANNING --------------- - - /// Incrementally iterate over a set of keys matching the `pattern` argument, returning `count` results per page, if - /// specified. - /// - /// The scan operation can be canceled by dropping the returned stream. - /// - /// - pub fn scan

( - &self, - pattern: P, - count: Option, - r#type: Option, - ) -> impl Stream> - where - P: Into, - { - commands::scan::scan(&self.inner, pattern.into(), count, r#type, None) - } - - /// Run the `SCAN` command on each primary/main node in a cluster concurrently. - /// - /// In order for this function to work reliably the cluster state must not change while scanning. If nodes are added - /// or removed, or hash slots are rebalanced, it may result in missing keys or duplicate keys in the result - /// stream. See [split_cluster](Self::split_cluster) for use cases that require scanning to work while the cluster - /// state changes. - /// - /// Unlike `SCAN`, `HSCAN`, etc, the returned stream may continue even if - /// [has_more](crate::types::ScanResult::has_more) returns false on a given page of keys. - pub fn scan_cluster

( - &self, - pattern: P, - count: Option, - r#type: Option, - ) -> impl Stream> - where - P: Into, - { - commands::scan::scan_cluster(&self.inner, pattern.into(), count, r#type) - } - - /// Incrementally iterate over pages of the hash map stored at `key`, returning `count` results per page, if - /// specified. - /// - /// - pub fn hscan( - &self, - key: K, - pattern: P, - count: Option, - ) -> impl Stream> - where - K: Into, - P: Into, - { - commands::scan::hscan(&self.inner, key.into(), pattern.into(), count) - } - - /// Incrementally iterate over pages of the set stored at `key`, returning `count` results per page, if specified. - /// - /// - pub fn sscan( - &self, - key: K, - pattern: P, - count: Option, - ) -> impl Stream> - where - K: Into, - P: Into, - { - commands::scan::sscan(&self.inner, key.into(), pattern.into(), count) - } - - /// Incrementally iterate over pages of the sorted set stored at `key`, returning `count` results per page, if - /// specified. - /// - /// - pub fn zscan( - &self, - key: K, - pattern: P, - count: Option, - ) -> impl Stream> - where - K: Into, - P: Into, - { - commands::scan::zscan(&self.inner, key.into(), pattern.into(), count) - } - - /// Send a series of commands in a [pipeline](https://redis.io/docs/manual/pipelining/). - pub fn pipeline(&self) -> Pipeline { - Pipeline::from(self.clone()) - } - - /// Shorthand to route subsequent commands to the provided server. - /// - /// See [with_options](crate::interfaces::ClientLike::with_options) for more information. - /// - /// ```rust - /// # use fred::prelude::*; - /// async fn example(client: &RedisClient) -> Result<(), RedisError> { - /// // discover servers via the `RedisConfig` or active connections - /// let connections = client.active_connections().await?; - /// - /// // ping each node in the cluster individually - /// for server in connections.into_iter() { - /// let _: () = client.with_cluster_node(server).ping().await?; - /// } - /// - /// // or use the cached cluster routing table to discover servers - /// let servers = client - /// .cached_cluster_state() - /// .expect("Failed to read cached cluster state") - /// .unique_primary_nodes(); - /// - /// for server in servers.into_iter() { - /// // verify the server address with `CLIENT INFO` - /// let server_addr = client - /// .with_cluster_node(&server) - /// .client_info::() - /// .await? - /// .split(" ") - /// .find_map(|s| { - /// let parts: Vec<&str> = s.split("=").collect(); - /// if parts[0] == "laddr" { - /// Some(parts[1].to_owned()) - /// } else { - /// None - /// } - /// }) - /// .expect("Failed to read or parse client info."); - /// - /// assert_eq!(server_addr, server.to_string()); - /// } - /// - /// Ok(()) - /// } - /// ``` - pub fn with_cluster_node(&self, server: S) -> WithOptions - where - S: Into, - { - WithOptions { - client: self.clone(), - options: Options { - cluster_node: Some(server.into()), - ..Default::default() - }, - } - } - - /// Create a client that interacts with replica nodes. - #[cfg(feature = "replicas")] - #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] - pub fn replicas(&self) -> Replicas { - Replicas::from(&self.inner) - } -} - -#[cfg(test)] -mod tests { - #[cfg(feature = "sha-1")] - use crate::util; - - #[test] - #[cfg(feature = "sha-1")] - fn should_correctly_sha1_hash() { - assert_eq!( - &util::sha1_hash("foobarbaz"), - "5f5513f8822fdbe5145af33b64d8d970dcf95c6e" - ); - assert_eq!(&util::sha1_hash("abc123"), "6367c48dd193d56ea7b0baad25b19455e529f5ee"); - assert_eq!( - &util::sha1_hash("jakdjfkldajfklej8a4tjkaldsnvkl43kjakljdvk42"), - "45c118f5de7c3fd3a4022135dc6acfb526f3c225" - ); - } -} diff --git a/src/clients/replica.rs b/src/clients/replica.rs deleted file mode 100644 index 5a07590b..00000000 --- a/src/clients/replica.rs +++ /dev/null @@ -1,132 +0,0 @@ -use crate::{ - clients::{Pipeline, RedisClient}, - error::RedisError, - interfaces::{self, *}, - modules::inner::RedisClientInner, - protocol::command::{RedisCommand, RouterCommand}, - runtime::{oneshot_channel, RefCount}, - types::Server, -}; -use std::{collections::HashMap, fmt, fmt::Formatter}; - -/// A struct for interacting with cluster replica nodes. -/// -/// All commands sent via this interface will use a replica node, if possible. The underlying connections are shared -/// with the main client in order to maintain an up-to-date view of the system in the event that replicas change or -/// are promoted. The cached replica routing table will be updated on the client when following cluster redirections -/// or when any connection closes. -/// -/// [Redis replication is asynchronous](https://redis.io/docs/management/replication/). -#[derive(Clone)] -#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] -pub struct Replicas { - inner: RefCount, -} - -impl fmt::Debug for Replicas { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Replicas").field("id", &self.inner.id).finish() - } -} - -#[doc(hidden)] -impl From<&RefCount> for Replicas { - fn from(inner: &RefCount) -> Self { - Replicas { inner: inner.clone() } - } -} - -impl ClientLike for Replicas { - #[doc(hidden)] - fn inner(&self) -> &RefCount { - &self.inner - } - - #[doc(hidden)] - fn change_command(&self, command: &mut RedisCommand) { - command.use_replica = true; - } -} - -#[cfg(feature = "i-redis-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))] -impl RedisJsonInterface for Replicas {} -#[cfg(feature = "i-time-series")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -impl TimeSeriesInterface for Replicas {} -#[cfg(feature = "i-cluster")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))] -impl ClusterInterface for Replicas {} -#[cfg(feature = "i-config")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))] -impl ConfigInterface for Replicas {} -#[cfg(feature = "i-geo")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))] -impl GeoInterface for Replicas {} -#[cfg(feature = "i-hashes")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))] -impl HashesInterface for Replicas {} -#[cfg(feature = "i-hyperloglog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))] -impl HyperloglogInterface for Replicas {} -#[cfg(feature = "i-keys")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))] -impl KeysInterface for Replicas {} -#[cfg(feature = "i-scripts")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] -impl LuaInterface for Replicas {} -#[cfg(feature = "i-lists")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))] -impl ListInterface for Replicas {} -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -impl MemoryInterface for Replicas {} -#[cfg(feature = "i-server")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] -impl ServerInterface for Replicas {} -#[cfg(feature = "i-slowlog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))] -impl SlowlogInterface for Replicas {} -#[cfg(feature = "i-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))] -impl SetsInterface for Replicas {} -#[cfg(feature = "i-sorted-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))] -impl SortedSetsInterface for Replicas {} -#[cfg(feature = "i-streams")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))] -impl StreamsInterface for Replicas {} -#[cfg(feature = "i-scripts")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] -impl FunctionInterface for Replicas {} -#[cfg(feature = "i-redisearch")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))] -impl RediSearchInterface for Replicas {} - -impl Replicas { - /// Read a mapping of replica server IDs to primary server IDs. - pub fn nodes(&self) -> HashMap { - self.inner.server_state.read().replicas.clone() - } - - /// Send a series of commands in a [pipeline](https://redis.io/docs/manual/pipelining/). - pub fn pipeline(&self) -> Pipeline { - Pipeline::from(self.clone()) - } - - /// Read the underlying [RedisClient](crate::clients::RedisClient) that interacts with primary nodes. - pub fn client(&self) -> RedisClient { - RedisClient::from(&self.inner) - } - - /// Sync the cached replica routing table with the server(s). - /// - /// If `reset: true` the client will forcefully disconnect from replicas even if the connections could otherwise be - /// reused. - pub async fn sync(&self, reset: bool) -> Result<(), RedisError> { - let (tx, rx) = oneshot_channel(); - let cmd = RouterCommand::SyncReplicas { tx, reset }; - interfaces::send_to_router(&self.inner, cmd)?; - rx.await? - } -} diff --git a/src/clients/sentinel.rs b/src/clients/sentinel.rs deleted file mode 100644 index a11f15a7..00000000 --- a/src/clients/sentinel.rs +++ /dev/null @@ -1,82 +0,0 @@ -use crate::{ - interfaces::*, - modules::inner::RedisClientInner, - runtime::RefCount, - types::{ConnectionConfig, PerformanceConfig, ReconnectPolicy, SentinelConfig}, -}; -use std::fmt; - -/// A struct for interacting directly with Sentinel nodes. -/// -/// This struct **will not** communicate with Redis servers behind the sentinel interface, but rather with the -/// sentinel nodes themselves. Callers should use the [RedisClient](crate::clients::RedisClient) interface with a -/// [ServerConfig::Sentinel](crate::types::ServerConfig::Sentinel) for interacting with Redis services behind a -/// sentinel layer. -/// -/// See the [sentinel API docs](https://redis.io/topics/sentinel#sentinel-api) for more information. -#[derive(Clone)] -#[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))] -pub struct SentinelClient { - inner: RefCount, -} - -impl ClientLike for SentinelClient { - #[doc(hidden)] - fn inner(&self) -> &RefCount { - &self.inner - } -} - -impl fmt::Debug for SentinelClient { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SentinelClient") - .field("id", &self.inner.id) - .field("state", &self.state()) - .finish() - } -} - -#[doc(hidden)] -impl<'a> From<&'a RefCount> for SentinelClient { - fn from(inner: &'a RefCount) -> Self { - SentinelClient { inner: inner.clone() } - } -} - -impl EventInterface for SentinelClient {} -impl SentinelInterface for SentinelClient {} -impl MetricsInterface for SentinelClient {} -#[cfg(feature = "i-acl")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))] -impl AclInterface for SentinelClient {} -#[cfg(feature = "i-pubsub")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))] -impl PubsubInterface for SentinelClient {} -#[cfg(feature = "i-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))] -impl ClientInterface for SentinelClient {} -impl AuthInterface for SentinelClient {} -#[cfg(feature = "i-server")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] -impl HeartbeatInterface for SentinelClient {} - -impl SentinelClient { - /// Create a new client instance without connecting to the sentinel node. - /// - /// See the [builder](crate::types::Builder) interface for more information. - pub fn new( - config: SentinelConfig, - perf: Option, - connection: Option, - policy: Option, - ) -> SentinelClient { - SentinelClient { - inner: RedisClientInner::new( - config.into(), - perf.unwrap_or_default(), - connection.unwrap_or_default(), - policy, - ), - } - } -} diff --git a/src/clients/transaction.rs b/src/clients/transaction.rs deleted file mode 100644 index cca5c138..00000000 --- a/src/clients/transaction.rs +++ /dev/null @@ -1,347 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - interfaces, - interfaces::*, - modules::inner::RedisClientInner, - prelude::RedisValue, - protocol::{ - command::{RedisCommand, RedisCommandKind, RouterCommand}, - hashers::ClusterHash, - responders::ResponseKind, - utils as protocol_utils, - }, - runtime::{oneshot_channel, Mutex, RefCount}, - types::{FromRedis, MultipleKeys, Options, RedisKey, Server}, - utils, -}; -use std::{collections::VecDeque, fmt}; - -/// A cheaply cloneable transaction block. -#[derive(Clone)] -#[cfg(feature = "transactions")] -#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))] -pub struct Transaction { - id: u64, - inner: RefCount, - commands: RefCount>>, - watched: RefCount>>, - hash_slot: RefCount>>, -} - -impl fmt::Debug for Transaction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Transaction") - .field("client", &self.inner.id) - .field("id", &self.id) - .field("length", &self.commands.lock().len()) - .field("hash_slot", &self.hash_slot.lock()) - .finish() - } -} - -impl PartialEq for Transaction { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl Eq for Transaction {} - -impl ClientLike for Transaction { - #[doc(hidden)] - fn inner(&self) -> &RefCount { - &self.inner - } - - #[doc(hidden)] - fn send_command(&self, command: C) -> Result<(), RedisError> - where - C: Into, - { - let mut command: RedisCommand = command.into(); - self.disallow_all_cluster_commands(&command)?; - // check cluster slot mappings as commands are added - self.update_hash_slot(&command)?; - - #[allow(unused_mut)] - if let Some(mut tx) = command.take_responder() { - trace!( - "{}: Respond early to {} command in transaction.", - &self.inner.id, - command.kind.to_str_debug() - ); - let _ = tx.send(Ok(protocol_utils::queued_frame())); - } - - self.commands.lock().push_back(command); - Ok(()) - } -} - -#[cfg(feature = "i-acl")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))] -impl AclInterface for Transaction {} -#[cfg(feature = "i-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))] -impl ClientInterface for Transaction {} -#[cfg(feature = "i-pubsub")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))] -impl PubsubInterface for Transaction {} -#[cfg(feature = "i-config")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))] -impl ConfigInterface for Transaction {} -#[cfg(feature = "i-geo")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))] -impl GeoInterface for Transaction {} -#[cfg(feature = "i-hashes")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))] -impl HashesInterface for Transaction {} -#[cfg(feature = "i-hyperloglog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))] -impl HyperloglogInterface for Transaction {} -#[cfg(feature = "i-keys")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))] -impl KeysInterface for Transaction {} -#[cfg(feature = "i-lists")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))] -impl ListInterface for Transaction {} -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -impl MemoryInterface for Transaction {} -impl AuthInterface for Transaction {} -#[cfg(feature = "i-server")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] -impl ServerInterface for Transaction {} -#[cfg(feature = "i-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))] -impl SetsInterface for Transaction {} -#[cfg(feature = "i-sorted-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))] -impl SortedSetsInterface for Transaction {} -#[cfg(feature = "i-streams")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))] -impl StreamsInterface for Transaction {} -#[cfg(feature = "i-scripts")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] -impl FunctionInterface for Transaction {} -#[cfg(feature = "i-redis-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))] -impl RedisJsonInterface for Transaction {} -#[cfg(feature = "i-time-series")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -impl TimeSeriesInterface for Transaction {} -#[cfg(feature = "i-redisearch")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))] -impl RediSearchInterface for Transaction {} - -impl Transaction { - /// Create a new transaction. - pub(crate) fn from_inner(inner: &RefCount) -> Self { - Transaction { - inner: inner.clone(), - commands: RefCount::new(Mutex::new(VecDeque::new())), - watched: RefCount::new(Mutex::new(VecDeque::new())), - hash_slot: RefCount::new(Mutex::new(None)), - id: utils::random_u64(u64::MAX), - } - } - - /// Check and update the hash slot for the transaction. - pub(crate) fn update_hash_slot(&self, command: &RedisCommand) -> Result<(), RedisError> { - if !self.inner.config.server.is_clustered() { - return Ok(()); - } - - if let Some(slot) = command.cluster_hash() { - if let Some(old_slot) = utils::read_mutex(&self.hash_slot) { - let (old_server, server) = self.inner.with_cluster_state(|state| { - debug!( - "{}: Checking transaction hash slots: {}, {}", - &self.inner.id, old_slot, slot - ); - - Ok((state.get_server(old_slot).cloned(), state.get_server(slot).cloned())) - })?; - - if old_server != server { - return Err(RedisError::new( - RedisErrorKind::Cluster, - "All transaction commands must use the same cluster node.", - )); - } - } else { - utils::set_mutex(&self.hash_slot, Some(slot)); - } - } - - Ok(()) - } - - pub(crate) fn disallow_all_cluster_commands(&self, command: &RedisCommand) -> Result<(), RedisError> { - if command.is_all_cluster_nodes() { - Err(RedisError::new( - RedisErrorKind::Cluster, - "Cannot use concurrent cluster commands inside a transaction.", - )) - } else { - Ok(()) - } - } - - /// An ID identifying the underlying transaction state. - pub fn id(&self) -> u64 { - self.id - } - - /// Clear the internal command buffer and watched keys. - pub fn reset(&self) { - self.commands.lock().clear(); - self.watched.lock().clear(); - self.hash_slot.lock().take(); - } - - /// Read the number of commands queued to run. - pub fn len(&self) -> usize { - self.commands.lock().len() - } - - /// Read the number of keys to `WATCH` before the starting the transaction. - pub fn watched_len(&self) -> usize { - self.watched.lock().len() - } - - /// Executes all previously queued commands in a transaction. - /// - /// If `abort_on_error` is `true` the client will automatically send `DISCARD` if an error is received from - /// any of the commands prior to `EXEC`. This does **not** apply to `MOVED` or `ASK` errors, which wll be followed - /// automatically. - /// - /// - /// - /// ```rust no_run - /// # use fred::prelude::*; - /// - /// async fn example(client: &RedisClient) -> Result<(), RedisError> { - /// let _ = client.mset(vec![("foo", 1), ("bar", 2)]).await?; - /// - /// let trx = client.multi(); - /// let _: () = trx.get("foo").await?; // returns QUEUED - /// let _: () = trx.get("bar").await?; // returns QUEUED - /// - /// let (foo, bar): (i64, i64) = trx.exec(false).await?; - /// assert_eq!((foo, bar), (1, 2)); - /// Ok(()) - /// } - /// ``` - pub async fn exec(&self, abort_on_error: bool) -> Result - where - R: FromRedis, - { - let commands = { - self - .commands - .lock() - .iter() - .map(|cmd| cmd.duplicate(ResponseKind::Skip)) - .collect() - }; - let watched = { self.watched.lock().iter().cloned().collect() }; - let hash_slot = utils::read_mutex(&self.hash_slot); - exec(&self.inner, commands, watched, hash_slot, abort_on_error, self.id) - .await? - .convert() - } - - /// Send the `WATCH` command with the provided keys before starting the transaction. - pub fn watch_before(&self, keys: K) - where - K: Into, - { - self.watched.lock().extend(keys.into().inner()); - } - - /// Read the hash slot against which this transaction will run, if known. - pub fn hash_slot(&self) -> Option { - utils::read_mutex(&self.hash_slot) - } - - /// Read the server ID against which this transaction will run, if known. - pub fn cluster_node(&self) -> Option { - utils::read_mutex(&self.hash_slot).and_then(|slot| { - self - .inner - .with_cluster_state(|state| Ok(state.get_server(slot).cloned())) - .ok() - .and_then(|server| server) - }) - } -} - -async fn exec( - inner: &RefCount, - commands: VecDeque, - watched: VecDeque, - hash_slot: Option, - abort_on_error: bool, - id: u64, -) -> Result { - if commands.is_empty() { - return Ok(RedisValue::Null); - } - let (tx, rx) = oneshot_channel(); - let trx_options = Options::from_command(&commands[0]); - - let mut multi = RedisCommand::new(RedisCommandKind::Multi, vec![]); - trx_options.apply(&mut multi); - - let commands: Vec = [multi] - .into_iter() - .chain(commands.into_iter()) - .map(|mut command| { - command.inherit_options(inner); - command.response = ResponseKind::Skip; - command.can_pipeline = false; - command.skip_backpressure = true; - command.transaction_id = Some(id); - command.use_replica = false; - if let Some(hash_slot) = hash_slot.as_ref() { - command.hasher = ClusterHash::Custom(*hash_slot); - } - command - }) - .collect(); - // collapse the watched keys into one command - let watched = if watched.is_empty() { - None - } else { - let args: Vec = watched.into_iter().map(|k| k.into()).collect(); - let mut watch_cmd = RedisCommand::new(RedisCommandKind::Watch, args); - watch_cmd.can_pipeline = false; - watch_cmd.skip_backpressure = true; - watch_cmd.transaction_id = Some(id); - if let Some(hash_slot) = hash_slot.as_ref() { - watch_cmd.hasher = ClusterHash::Custom(*hash_slot); - } - Some(watch_cmd) - }; - - _trace!( - inner, - "Sending transaction {} with {} commands ({} watched) to router.", - id, - commands.len(), - watched.as_ref().map(|c| c.args().len()).unwrap_or(0) - ); - let command = RouterCommand::Transaction { - id, - tx, - commands, - watched, - abort_on_error, - }; - let timeout_dur = trx_options.timeout.unwrap_or_else(|| inner.default_command_timeout()); - - interfaces::send_to_router(inner, command)?; - let frame = utils::timeout(rx, timeout_dur).await??; - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/acl.rs b/src/commands/impls/acl.rs deleted file mode 100644 index 32b36f6c..00000000 --- a/src/commands/impls/acl.rs +++ /dev/null @@ -1,83 +0,0 @@ -use super::*; -use crate::{ - protocol::{command::RedisCommandKind, utils as protocol_utils}, - types::*, - utils, -}; -use bytes_utils::Str; - -ok_cmd!(acl_load, AclLoad); -ok_cmd!(acl_save, AclSave); -values_cmd!(acl_list, AclList); -values_cmd!(acl_users, AclUsers); -value_cmd!(acl_whoami, AclWhoAmI); - -pub async fn acl_setuser(client: &C, username: Str, rules: MultipleValues) -> Result<(), RedisError> { - let frame = utils::request_response(client, move || { - let rules = rules.into_multiple_values(); - let mut args = Vec::with_capacity(rules.len() + 1); - args.push(username.into()); - - for rule in rules.into_iter() { - args.push(rule); - } - Ok((RedisCommandKind::AclSetUser, args)) - }) - .await?; - - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -pub async fn acl_getuser(client: &C, username: Str) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::AclGetUser, vec![username.into()])) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn acl_deluser(client: &C, usernames: MultipleKeys) -> Result { - let args: Vec = usernames.inner().into_iter().map(|k| k.into()).collect(); - let frame = utils::request_response(client, move || Ok((RedisCommandKind::AclDelUser, args))).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn acl_cat(client: &C, category: Option) -> Result { - let args: Vec = if let Some(cat) = category { - vec![cat.into()] - } else { - Vec::new() - }; - - let frame = utils::request_response(client, move || Ok((RedisCommandKind::AclCat, args))).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn acl_genpass(client: &C, bits: Option) -> Result { - let args: Vec = if let Some(bits) = bits { - vec![bits.into()] - } else { - Vec::new() - }; - - let frame = utils::request_response(client, move || Ok((RedisCommandKind::AclGenPass, args))).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn acl_log_reset(client: &C) -> Result<(), RedisError> { - let frame = utils::request_response(client, || Ok((RedisCommandKind::AclLog, vec![static_val!(RESET)]))).await?; - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -pub async fn acl_log_count(client: &C, count: Option) -> Result { - let args: Vec = if let Some(count) = count { - vec![count.into()] - } else { - Vec::new() - }; - - let frame = utils::request_response(client, move || Ok((RedisCommandKind::AclLog, args))).await?; - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/client.rs b/src/commands/impls/client.rs deleted file mode 100644 index c20b2033..00000000 --- a/src/commands/impls/client.rs +++ /dev/null @@ -1,132 +0,0 @@ -use super::*; -use crate::{ - protocol::{ - command::{RedisCommand, RedisCommandKind}, - utils as protocol_utils, - }, - types::*, - utils, -}; -use bytes_utils::Str; - -value_cmd!(client_id, ClientID); -value_cmd!(client_info, ClientInfo); - -pub async fn client_kill( - client: &C, - filters: Vec, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(filters.len() * 2); - - for filter in filters.into_iter() { - let (field, value) = filter.to_str(); - args.push(field.into()); - args.push(value.into()); - } - - Ok((RedisCommandKind::ClientKill, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn client_list( - client: &C, - r#type: Option, - ids: Option>, -) -> Result { - let ids: Option> = ids.map(|ids| ids.into_iter().map(|id| id.into()).collect()); - let frame = utils::request_response(client, move || { - let max_args = 2 + ids.as_ref().map(|i| i.len()).unwrap_or(0); - let mut args = Vec::with_capacity(max_args); - - if let Some(kind) = r#type { - args.push(static_val!(TYPE)); - args.push(kind.to_str().into()); - } - if let Some(ids) = ids { - if !ids.is_empty() { - args.push(static_val!(ID)); - - for id in ids.into_iter() { - args.push(id.into()); - } - } - } - - Ok((RedisCommandKind::ClientList, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn client_pause( - client: &C, - timeout: i64, - mode: Option, -) -> Result<(), RedisError> { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2); - args.push(timeout.into()); - - if let Some(mode) = mode { - args.push(mode.to_str().into()); - } - - Ok((RedisCommandKind::ClientPause, args)) - }) - .await?; - - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -value_cmd!(client_getname, ClientGetName); - -pub async fn client_setname(client: &C, name: Str) -> Result<(), RedisError> { - let frame = - utils::request_response(client, move || Ok((RedisCommandKind::ClientSetname, vec![name.into()]))).await?; - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -ok_cmd!(client_unpause, ClientUnpause); - -pub async fn client_reply(client: &C, flag: ClientReplyFlag) -> Result<(), RedisError> { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::ClientReply, vec![flag.to_str().into()])) - }) - .await?; - - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -pub async fn client_unblock( - client: &C, - id: RedisValue, - flag: Option, -) -> Result { - let inner = client.inner(); - - let mut args = Vec::with_capacity(2); - args.push(id); - if let Some(flag) = flag { - args.push(flag.to_str().into()); - } - let command = RedisCommand::new(RedisCommandKind::ClientUnblock, args); - - let frame = utils::backchannel_request_response(inner, command, false).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn unblock_self(client: &C, flag: Option) -> Result<(), RedisError> { - let inner = client.inner(); - let flag = flag.unwrap_or(ClientUnblockFlag::Error); - let result = utils::interrupt_blocked_connection(inner, flag).await; - inner.backchannel.write().await.set_unblocked(); - result -} diff --git a/src/commands/impls/cluster.rs b/src/commands/impls/cluster.rs deleted file mode 100644 index e0dc093c..00000000 --- a/src/commands/impls/cluster.rs +++ /dev/null @@ -1,183 +0,0 @@ -use super::*; -use crate::{ - interfaces, - protocol::{ - command::{RedisCommandKind, RouterCommand}, - utils as protocol_utils, - }, - runtime::oneshot_channel, - types::*, - utils, -}; -use bytes_utils::Str; -use std::convert::TryInto; - -value_cmd!(cluster_bumpepoch, ClusterBumpEpoch); -ok_cmd!(cluster_flushslots, ClusterFlushSlots); -value_cmd!(cluster_myid, ClusterMyID); -value_cmd!(cluster_nodes, ClusterNodes); -ok_cmd!(cluster_saveconfig, ClusterSaveConfig); -values_cmd!(cluster_slots, ClusterSlots); - -pub async fn cluster_info(client: &C) -> Result { - let frame = utils::request_response(client, || Ok((RedisCommandKind::ClusterInfo, vec![]))).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn cluster_add_slots(client: &C, slots: MultipleHashSlots) -> Result<(), RedisError> { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(slots.len()); - - for slot in slots.inner().into_iter() { - args.push(slot.into()); - } - - Ok((RedisCommandKind::ClusterAddSlots, args)) - }) - .await?; - - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -pub async fn cluster_count_failure_reports( - client: &C, - node_id: Str, -) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::ClusterCountFailureReports, vec![node_id.into()])) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn cluster_count_keys_in_slot(client: &C, slot: u16) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::ClusterCountKeysInSlot, vec![slot.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn cluster_del_slots(client: &C, slots: MultipleHashSlots) -> Result<(), RedisError> { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(slots.len()); - - for slot in slots.inner().into_iter() { - args.push(slot.into()); - } - - Ok((RedisCommandKind::ClusterDelSlots, args)) - }) - .await?; - - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -pub async fn cluster_failover( - client: &C, - flag: Option, -) -> Result<(), RedisError> { - let frame = utils::request_response(client, move || { - let args = if let Some(flag) = flag { - vec![flag.to_str().into()] - } else { - Vec::new() - }; - - Ok((RedisCommandKind::ClusterFailOver, args)) - }) - .await?; - - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -pub async fn cluster_forget(client: &C, node_id: Str) -> Result<(), RedisError> { - one_arg_ok_cmd(client, RedisCommandKind::ClusterForget, node_id.into()).await -} - -pub async fn cluster_get_keys_in_slot( - client: &C, - slot: u16, - count: u64, -) -> Result { - let count: RedisValue = count.try_into()?; - - let frame = utils::request_response(client, move || { - let args = vec![slot.into(), count]; - Ok((RedisCommandKind::ClusterGetKeysInSlot, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn cluster_keyslot(client: &C, key: RedisKey) -> Result { - one_arg_value_cmd(client, RedisCommandKind::ClusterKeySlot, key.into()).await -} - -pub async fn cluster_meet(client: &C, ip: Str, port: u16) -> Result<(), RedisError> { - args_ok_cmd(client, RedisCommandKind::ClusterMeet, vec![ip.into(), port.into()]).await -} - -pub async fn cluster_replicate(client: &C, node_id: Str) -> Result<(), RedisError> { - one_arg_ok_cmd(client, RedisCommandKind::ClusterReplicate, node_id.into()).await -} - -pub async fn cluster_replicas(client: &C, node_id: Str) -> Result { - one_arg_value_cmd(client, RedisCommandKind::ClusterReplicas, node_id.into()).await -} - -pub async fn cluster_reset(client: &C, mode: Option) -> Result<(), RedisError> { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1); - if let Some(flag) = mode { - args.push(flag.to_str().into()); - } - - Ok((RedisCommandKind::ClusterReset, args)) - }) - .await?; - - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -pub async fn cluster_set_config_epoch(client: &C, epoch: u64) -> Result<(), RedisError> { - let epoch: RedisValue = epoch.try_into()?; - one_arg_ok_cmd(client, RedisCommandKind::ClusterSetConfigEpoch, epoch).await -} - -pub async fn cluster_setslot( - client: &C, - slot: u16, - state: ClusterSetSlotState, -) -> Result<(), RedisError> { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(3); - args.push(slot.into()); - - let (state, arg) = state.to_str(); - args.push(state.into()); - if let Some(arg) = arg { - args.push(arg.into()); - } - - Ok((RedisCommandKind::ClusterSetSlot, args)) - }) - .await?; - - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -pub async fn sync_cluster(client: &C) -> Result<(), RedisError> { - let (tx, rx) = oneshot_channel(); - let command = RouterCommand::SyncCluster { tx }; - interfaces::send_to_router(client.inner(), command)?; - - rx.await? -} diff --git a/src/commands/impls/config.rs b/src/commands/impls/config.rs deleted file mode 100644 index 78f0e28e..00000000 --- a/src/commands/impls/config.rs +++ /dev/null @@ -1,14 +0,0 @@ -use super::*; -use crate::{protocol::command::RedisCommandKind, types::*}; -use bytes_utils::Str; - -ok_cmd!(config_resetstat, ConfigResetStat); -ok_cmd!(config_rewrite, ConfigRewrite); - -pub async fn config_get(client: &C, parameter: Str) -> Result { - one_arg_values_cmd(client, RedisCommandKind::ConfigGet, parameter.into()).await -} - -pub async fn config_set(client: &C, parameter: Str, value: RedisValue) -> Result<(), RedisError> { - args_ok_cmd(client, RedisCommandKind::ConfigSet, vec![parameter.into(), value]).await -} diff --git a/src/commands/impls/geo.rs b/src/commands/impls/geo.rs deleted file mode 100644 index 4a01479f..00000000 --- a/src/commands/impls/geo.rs +++ /dev/null @@ -1,350 +0,0 @@ -use super::*; -use crate::{ - error::RedisError, - protocol::{command::RedisCommandKind, utils as protocol_utils}, - types::*, - utils, -}; -use std::convert::TryInto; - -static WITH_COORD: &str = "WITHCOORD"; -static WITH_DIST: &str = "WITHDIST"; -static WITH_HASH: &str = "WITHHASH"; -static STORE_DIST: &str = "STOREDIST"; -static FROM_MEMBER: &str = "FROMMEMBER"; -static FROM_LONLAT: &str = "FROMLONLAT"; -static BY_RADIUS: &str = "BYRADIUS"; -static BY_BOX: &str = "BYBOX"; - -pub async fn geoadd( - client: &C, - key: RedisKey, - options: Option, - changed: bool, - values: MultipleGeoValues, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(3 + (values.len() * 3)); - args.push(key.into()); - - if let Some(options) = options { - args.push(options.to_str().into()); - } - if changed { - args.push(static_val!(CHANGED)); - } - - for value in values.inner().into_iter() { - args.push(value.coordinates.longitude.try_into()?); - args.push(value.coordinates.latitude.try_into()?); - args.push(value.member) - } - - Ok((RedisCommandKind::GeoAdd, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn geohash( - client: &C, - key: RedisKey, - members: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let members = members.into_multiple_values(); - let mut args = Vec::with_capacity(1 + members.len()); - args.push(key.into()); - - for member in members.into_iter() { - args.push(member); - } - - Ok((RedisCommandKind::GeoHash, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn geopos( - client: &C, - key: RedisKey, - members: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let members = members.into_multiple_values(); - let mut args = Vec::with_capacity(1 + members.len()); - args.push(key.into()); - - for member in members.into_iter() { - args.push(member); - } - - Ok((RedisCommandKind::GeoPos, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn geodist( - client: &C, - key: RedisKey, - src: RedisValue, - dest: RedisValue, - unit: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(4); - args.push(key.into()); - args.push(src); - args.push(dest); - - if let Some(unit) = unit { - args.push(unit.to_str().into()); - } - - Ok((RedisCommandKind::GeoDist, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn georadius( - client: &C, - key: RedisKey, - position: GeoPosition, - radius: f64, - unit: GeoUnit, - withcoord: bool, - withdist: bool, - withhash: bool, - count: Option<(u64, Any)>, - ord: Option, - store: Option, - storedist: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(16); - args.push(key.into()); - args.push(position.longitude.try_into()?); - args.push(position.latitude.try_into()?); - args.push(radius.try_into()?); - args.push(unit.to_str().into()); - - if withcoord { - args.push(static_val!(WITH_COORD)); - } - if withdist { - args.push(static_val!(WITH_DIST)); - } - if withhash { - args.push(static_val!(WITH_HASH)); - } - if let Some((count, any)) = count { - args.push(static_val!(COUNT)); - args.push(count.try_into()?); - if any { - args.push(static_val!(ANY)); - } - } - if let Some(ord) = ord { - args.push(ord.to_str().into()); - } - if let Some(store) = store { - args.push(static_val!(STORE)); - args.push(store.into()); - } - if let Some(store_dist) = storedist { - args.push(static_val!(STORE_DIST)); - args.push(store_dist.into()); - } - - Ok((RedisCommandKind::GeoRadius, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn georadiusbymember( - client: &C, - key: RedisKey, - member: RedisValue, - radius: f64, - unit: GeoUnit, - withcoord: bool, - withdist: bool, - withhash: bool, - count: Option<(u64, Any)>, - ord: Option, - store: Option, - storedist: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(15); - args.push(key.into()); - args.push(member); - args.push(radius.try_into()?); - args.push(unit.to_str().into()); - - if withcoord { - args.push(static_val!(WITH_COORD)); - } - if withdist { - args.push(static_val!(WITH_DIST)); - } - if withhash { - args.push(static_val!(WITH_HASH)); - } - if let Some((count, any)) = count { - args.push(static_val!(COUNT)); - args.push(count.try_into()?); - if any { - args.push(static_val!(ANY)); - } - } - if let Some(ord) = ord { - args.push(ord.to_str().into()); - } - if let Some(store) = store { - args.push(static_val!(STORE)); - args.push(store.into()); - } - if let Some(store_dist) = storedist { - args.push(static_val!(STORE_DIST)); - args.push(store_dist.into()); - } - - Ok((RedisCommandKind::GeoRadiusByMember, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn geosearch( - client: &C, - key: RedisKey, - from_member: Option, - from_lonlat: Option, - by_radius: Option<(f64, GeoUnit)>, - by_box: Option<(f64, f64, GeoUnit)>, - ord: Option, - count: Option<(u64, Any)>, - withcoord: bool, - withdist: bool, - withhash: bool, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(15); - args.push(key.into()); - - if let Some(member) = from_member { - args.push(static_val!(FROM_MEMBER)); - args.push(member); - } - if let Some(position) = from_lonlat { - args.push(static_val!(FROM_LONLAT)); - args.push(position.longitude.try_into()?); - args.push(position.latitude.try_into()?); - } - - if let Some((radius, unit)) = by_radius { - args.push(static_val!(BY_RADIUS)); - args.push(radius.try_into()?); - args.push(unit.to_str().into()); - } - if let Some((width, height, unit)) = by_box { - args.push(static_val!(BY_BOX)); - args.push(width.try_into()?); - args.push(height.try_into()?); - args.push(unit.to_str().into()); - } - if let Some(ord) = ord { - args.push(ord.to_str().into()); - } - if let Some((count, any)) = count { - args.push(static_val!(COUNT)); - args.push(count.try_into()?); - if any { - args.push(static_val!(ANY)); - } - } - if withcoord { - args.push(static_val!(WITH_COORD)); - } - if withdist { - args.push(static_val!(WITH_DIST)); - } - if withhash { - args.push(static_val!(WITH_HASH)); - } - - Ok((RedisCommandKind::GeoSearch, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn geosearchstore( - client: &C, - dest: RedisKey, - source: RedisKey, - from_member: Option, - from_lonlat: Option, - by_radius: Option<(f64, GeoUnit)>, - by_box: Option<(f64, f64, GeoUnit)>, - ord: Option, - count: Option<(u64, Any)>, - storedist: bool, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(14); - args.push(dest.into()); - args.push(source.into()); - - if let Some(member) = from_member { - args.push(static_val!(FROM_MEMBER)); - args.push(member); - } - if let Some(position) = from_lonlat { - args.push(static_val!(FROM_LONLAT)); - args.push(position.longitude.try_into()?); - args.push(position.latitude.try_into()?); - } - if let Some((radius, unit)) = by_radius { - args.push(static_val!(BY_RADIUS)); - args.push(radius.try_into()?); - args.push(unit.to_str().into()); - } - if let Some((width, height, unit)) = by_box { - args.push(static_val!(BY_BOX)); - args.push(width.try_into()?); - args.push(height.try_into()?); - args.push(unit.to_str().into()); - } - if let Some(ord) = ord { - args.push(ord.to_str().into()); - } - if let Some((count, any)) = count { - args.push(static_val!(COUNT)); - args.push(count.try_into()?); - if any { - args.push(static_val!(ANY)); - } - } - if storedist { - args.push(static_val!(STORE_DIST)); - } - - Ok((RedisCommandKind::GeoSearchStore, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/hashes.rs b/src/commands/impls/hashes.rs deleted file mode 100644 index 75290b97..00000000 --- a/src/commands/impls/hashes.rs +++ /dev/null @@ -1,191 +0,0 @@ -use super::*; -use crate::{ - protocol::{command::RedisCommandKind, utils as protocol_utils}, - types::*, - utils, -}; -use redis_protocol::resp3::types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame}; -use std::{convert::TryInto, str}; - -fn frame_is_queued(frame: &Resp3Frame) -> bool { - match frame { - Resp3Frame::SimpleString { ref data, .. } | Resp3Frame::BlobString { ref data, .. } => { - str::from_utf8(data).ok().map(|s| s == QUEUED).unwrap_or(false) - }, - _ => false, - } -} - -pub async fn hdel(client: &C, key: RedisKey, fields: MultipleKeys) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1 + fields.len()); - args.push(key.into()); - - for field in fields.inner().into_iter() { - args.push(field.into()); - } - - Ok((RedisCommandKind::HDel, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn hexists(client: &C, key: RedisKey, field: RedisKey) -> Result { - let args: Vec = vec![key.into(), field.into()]; - args_value_cmd(client, RedisCommandKind::HExists, args).await -} - -pub async fn hget(client: &C, key: RedisKey, field: RedisKey) -> Result { - let args: Vec = vec![key.into(), field.into()]; - args_value_cmd(client, RedisCommandKind::HGet, args).await -} - -pub async fn hgetall(client: &C, key: RedisKey) -> Result { - let frame = utils::request_response(client, move || Ok((RedisCommandKind::HGetAll, vec![key.into()]))).await?; - - if frame.as_str().map(|s| s == QUEUED).unwrap_or(false) { - protocol_utils::frame_to_results(frame) - } else { - Ok(RedisValue::Map(protocol_utils::frame_to_map(frame)?)) - } -} - -pub async fn hincrby( - client: &C, - key: RedisKey, - field: RedisKey, - increment: i64, -) -> Result { - let args: Vec = vec![key.into(), field.into(), increment.into()]; - args_value_cmd(client, RedisCommandKind::HIncrBy, args).await -} - -pub async fn hincrbyfloat( - client: &C, - key: RedisKey, - field: RedisKey, - increment: f64, -) -> Result { - let args: Vec = vec![key.into(), field.into(), increment.try_into()?]; - args_value_cmd(client, RedisCommandKind::HIncrByFloat, args).await -} - -pub async fn hkeys(client: &C, key: RedisKey) -> Result { - let frame = utils::request_response(client, move || Ok((RedisCommandKind::HKeys, vec![key.into()]))).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn hlen(client: &C, key: RedisKey) -> Result { - one_arg_value_cmd(client, RedisCommandKind::HLen, key.into()).await -} - -pub async fn hmget(client: &C, key: RedisKey, fields: MultipleKeys) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1 + fields.len()); - args.push(key.into()); - - for field in fields.inner().into_iter() { - args.push(field.into()); - } - Ok((RedisCommandKind::HMGet, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn hmset(client: &C, key: RedisKey, values: RedisMap) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1 + (values.len() * 2)); - args.push(key.into()); - - for (key, value) in values.inner().into_iter().filter(|x| !x.1.is_null()) { - args.push(key.into()); - args.push(value); - } - Ok((RedisCommandKind::HMSet, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn hset(client: &C, key: RedisKey, values: RedisMap) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1 + (values.len() * 2)); - args.push(key.into()); - - for (key, value) in values.inner().into_iter().filter(|x| !x.1.is_null()) { - args.push(key.into()); - args.push(value); - } - - Ok((RedisCommandKind::HSet, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn hsetnx( - client: &C, - key: RedisKey, - field: RedisKey, - value: RedisValue, -) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::HSetNx, vec![key.into(), field.into(), value])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn hrandfield( - client: &C, - key: RedisKey, - count: Option<(i64, bool)>, -) -> Result { - let (has_count, has_values) = count.as_ref().map(|(_c, b)| (true, *b)).unwrap_or((false, false)); - - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(3); - args.push(key.into()); - - if let Some((count, with_values)) = count { - args.push(count.into()); - if with_values { - args.push(static_val!(WITH_VALUES)); - } - } - - Ok((RedisCommandKind::HRandField, args)) - }) - .await?; - - if has_count { - if has_values && frame.as_str().map(|s| s != QUEUED).unwrap_or(true) { - let frame = protocol_utils::flatten_frame(frame); - protocol_utils::frame_to_map(frame).map(RedisValue::Map) - } else { - protocol_utils::frame_to_results(frame) - } - } else { - protocol_utils::frame_to_results(frame) - } -} - -pub async fn hstrlen(client: &C, key: RedisKey, field: RedisKey) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::HStrLen, vec![key.into(), field.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn hvals(client: &C, key: RedisKey) -> Result { - one_arg_values_cmd(client, RedisCommandKind::HVals, key.into()).await -} diff --git a/src/commands/impls/hyperloglog.rs b/src/commands/impls/hyperloglog.rs deleted file mode 100644 index 71793815..00000000 --- a/src/commands/impls/hyperloglog.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::*; -use crate::{ - protocol::{command::RedisCommandKind, utils as protocol_utils}, - types::*, - utils, -}; - -pub async fn pfadd( - client: &C, - key: RedisKey, - elements: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let elements = elements.into_multiple_values(); - let mut args = Vec::with_capacity(1 + elements.len()); - args.push(key.into()); - - for element in elements.into_iter() { - args.push(element); - } - Ok((RedisCommandKind::Pfadd, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn pfcount(client: &C, keys: MultipleKeys) -> Result { - let args: Vec = keys.inner().into_iter().map(|k| k.into()).collect(); - args_value_cmd(client, RedisCommandKind::Pfcount, args).await -} - -pub async fn pfmerge( - client: &C, - dest: RedisKey, - sources: MultipleKeys, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1 + sources.len()); - args.push(dest.into()); - - for source in sources.inner().into_iter() { - args.push(source.into()); - } - Ok((RedisCommandKind::Pfmerge, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/keys.rs b/src/commands/impls/keys.rs deleted file mode 100644 index 5376eb7d..00000000 --- a/src/commands/impls/keys.rs +++ /dev/null @@ -1,444 +0,0 @@ -use super::*; -use crate::{ - error::*, - protocol::{command::RedisCommandKind, utils as protocol_utils}, - types::*, - utils, -}; -use std::convert::TryInto; - -fn check_empty_keys(keys: &MultipleKeys) -> Result<(), RedisError> { - if keys.len() == 0 { - Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "At least one key is required.", - )) - } else { - Ok(()) - } -} - -value_cmd!(randomkey, Randomkey); - -pub async fn get(client: &C, key: RedisKey) -> Result { - one_arg_values_cmd(client, RedisCommandKind::Get, key.into()).await -} - -pub async fn set( - client: &C, - key: RedisKey, - value: RedisValue, - expire: Option, - options: Option, - get: bool, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(6); - args.push(key.into()); - args.push(value); - - if let Some(expire) = expire { - let (k, v) = expire.into_args(); - args.push(k.into()); - if let Some(v) = v { - args.push(v.into()); - } - } - if let Some(options) = options { - args.push(options.to_str().into()); - } - if get { - args.push(static_val!(GET)); - } - - Ok((RedisCommandKind::Set, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn del(client: &C, keys: MultipleKeys) -> Result { - check_empty_keys(&keys)?; - - let args: Vec = keys.inner().drain(..).map(|k| k.into()).collect(); - let frame = utils::request_response(client, move || Ok((RedisCommandKind::Del, args))).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn unlink(client: &C, keys: MultipleKeys) -> Result { - check_empty_keys(&keys)?; - - let args: Vec = keys.inner().drain(..).map(|k| k.into()).collect(); - let frame = utils::request_response(client, move || Ok((RedisCommandKind::Unlink, args))).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn append(client: &C, key: RedisKey, value: RedisValue) -> Result { - args_value_cmd(client, RedisCommandKind::Append, vec![key.into(), value]).await -} - -pub async fn incr(client: &C, key: RedisKey) -> Result { - one_arg_value_cmd(client, RedisCommandKind::Incr, key.into()).await -} - -pub async fn decr(client: &C, key: RedisKey) -> Result { - one_arg_value_cmd(client, RedisCommandKind::Decr, key.into()).await -} - -pub async fn incr_by(client: &C, key: RedisKey, val: i64) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::IncrBy, vec![key.into(), val.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn decr_by(client: &C, key: RedisKey, val: i64) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::DecrBy, vec![key.into(), val.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn incr_by_float(client: &C, key: RedisKey, val: f64) -> Result { - let val: RedisValue = val.try_into()?; - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::IncrByFloat, vec![key.into(), val])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ttl(client: &C, key: RedisKey) -> Result { - one_arg_value_cmd(client, RedisCommandKind::Ttl, key.into()).await -} - -pub async fn pttl(client: &C, key: RedisKey) -> Result { - one_arg_value_cmd(client, RedisCommandKind::Pttl, key.into()).await -} - -pub async fn persist(client: &C, key: RedisKey) -> Result { - one_arg_value_cmd(client, RedisCommandKind::Persist, key.into()).await -} - -pub async fn expire(client: &C, key: RedisKey, seconds: i64) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Expire, vec![key.into(), seconds.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn expire_at(client: &C, key: RedisKey, timestamp: i64) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::ExpireAt, vec![key.into(), timestamp.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn pexpire( - client: &C, - key: RedisKey, - milliseconds: i64, - options: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let args = if let Some(options) = options { - vec![key.into(), milliseconds.into(), options.to_str().into()] - } else { - vec![key.into(), milliseconds.into()] - }; - - Ok((RedisCommandKind::Pexpire, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn pexpire_at( - client: &C, - key: RedisKey, - timestamp: i64, - options: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let args = if let Some(options) = options { - vec![key.into(), timestamp.into(), options.to_str().into()] - } else { - vec![key.into(), timestamp.into()] - }; - - Ok((RedisCommandKind::Pexpireat, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn exists(client: &C, keys: MultipleKeys) -> Result { - check_empty_keys(&keys)?; - - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(keys.len()); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - - Ok((RedisCommandKind::Exists, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn dump(client: &C, key: RedisKey) -> Result { - one_arg_values_cmd(client, RedisCommandKind::Dump, key.into()).await -} - -pub async fn restore( - client: &C, - key: RedisKey, - ttl: i64, - serialized: RedisValue, - replace: bool, - absttl: bool, - idletime: Option, - frequency: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(9); - args.push(key.into()); - args.push(ttl.into()); - args.push(serialized); - - if replace { - args.push(static_val!(REPLACE)); - } - if absttl { - args.push(static_val!(ABSTTL)); - } - if let Some(idletime) = idletime { - args.push(static_val!(IDLE_TIME)); - args.push(idletime.into()); - } - if let Some(frequency) = frequency { - args.push(static_val!(FREQ)); - args.push(frequency.into()); - } - - Ok((RedisCommandKind::Restore, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn getrange( - client: &C, - key: RedisKey, - start: usize, - end: usize, -) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::GetRange, vec![ - key.into(), - start.try_into()?, - end.try_into()?, - ])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn setrange( - client: &C, - key: RedisKey, - offset: u32, - value: RedisValue, -) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Setrange, vec![key.into(), offset.into(), value])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn getset(client: &C, key: RedisKey, value: RedisValue) -> Result { - args_values_cmd(client, RedisCommandKind::GetSet, vec![key.into(), value]).await -} - -pub async fn rename( - client: &C, - source: RedisKey, - destination: RedisKey, -) -> Result { - args_values_cmd(client, RedisCommandKind::Rename, vec![ - source.into(), - destination.into(), - ]) - .await -} - -pub async fn renamenx( - client: &C, - source: RedisKey, - destination: RedisKey, -) -> Result { - args_values_cmd(client, RedisCommandKind::Renamenx, vec![ - source.into(), - destination.into(), - ]) - .await -} - -pub async fn getdel(client: &C, key: RedisKey) -> Result { - one_arg_values_cmd(client, RedisCommandKind::GetDel, key.into()).await -} - -pub async fn strlen(client: &C, key: RedisKey) -> Result { - one_arg_value_cmd(client, RedisCommandKind::Strlen, key.into()).await -} - -pub async fn mget(client: &C, keys: MultipleKeys) -> Result { - check_empty_keys(&keys)?; - - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(keys.len()); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - - Ok((RedisCommandKind::Mget, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn mset(client: &C, values: RedisMap) -> Result { - if values.len() == 0 { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Values cannot be empty.", - )); - } - - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(values.len() * 2); - - for (key, value) in values.inner().into_iter() { - args.push(key.into()); - args.push(value); - } - - Ok((RedisCommandKind::Mset, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn msetnx(client: &C, values: RedisMap) -> Result { - if values.len() == 0 { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Values cannot be empty.", - )); - } - - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(values.len() * 2); - - for (key, value) in values.inner().into_iter() { - args.push(key.into()); - args.push(value); - } - - Ok((RedisCommandKind::Msetnx, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn copy( - client: &C, - source: RedisKey, - destination: RedisKey, - db: Option, - replace: bool, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(5); - args.push(source.into()); - args.push(destination.into()); - - if let Some(db) = db { - args.push(static_val!(DB)); - args.push(db.into()); - } - if replace { - args.push(static_val!(REPLACE)); - } - - Ok((RedisCommandKind::Copy, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn watch(client: &C, keys: MultipleKeys) -> Result<(), RedisError> { - let args = keys.inner().into_iter().map(|k| k.into()).collect(); - args_ok_cmd(client, RedisCommandKind::Watch, args).await -} - -ok_cmd!(unwatch, Unwatch); - -pub async fn lcs( - client: &C, - key1: RedisKey, - key2: RedisKey, - len: bool, - idx: bool, - minmatchlen: Option, - withmatchlen: bool, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(7); - args.push(key1.into()); - args.push(key2.into()); - - if len { - args.push(static_val!(LEN)); - } - if idx { - args.push(static_val!(IDX)); - } - if let Some(minmatchlen) = minmatchlen { - args.push(static_val!(MINMATCHLEN)); - args.push(minmatchlen.into()); - } - if withmatchlen { - args.push(static_val!(WITHMATCHLEN)); - } - - Ok((RedisCommandKind::Lcs, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/lists.rs b/src/commands/impls/lists.rs deleted file mode 100644 index b2ba58e2..00000000 --- a/src/commands/impls/lists.rs +++ /dev/null @@ -1,481 +0,0 @@ -use super::*; -use crate::{ - protocol::{command::RedisCommandKind, utils as protocol_utils}, - types::*, - utils, -}; -use bytes_utils::Str; -use std::convert::TryInto; - -pub async fn sort_ro( - client: &C, - key: RedisKey, - by: Option, - limit: Option, - get: MultipleStrings, - order: Option, - alpha: bool, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(8 + get.len() * 2); - args.push(key.into()); - - if let Some(pattern) = by { - args.push(static_val!("BY")); - args.push(pattern.into()); - } - if let Some((offset, count)) = limit { - args.push(static_val!(LIMIT)); - args.push(offset.into()); - args.push(count.into()); - } - for pattern in get.inner().into_iter() { - args.push(static_val!(GET)); - args.push(pattern.into()); - } - if let Some(order) = order { - args.push(order.to_str().into()); - } - if alpha { - args.push(static_val!("ALPHA")); - } - - Ok((RedisCommandKind::SortRo, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn sort( - client: &C, - key: RedisKey, - by: Option, - limit: Option, - get: MultipleStrings, - order: Option, - alpha: bool, - store: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(10 + get.len() * 2); - args.push(key.into()); - - if let Some(pattern) = by { - args.push(static_val!("BY")); - args.push(pattern.into()); - } - if let Some((offset, count)) = limit { - args.push(static_val!(LIMIT)); - args.push(offset.into()); - args.push(count.into()); - } - for pattern in get.inner().into_iter() { - args.push(static_val!(GET)); - args.push(pattern.into()); - } - if let Some(order) = order { - args.push(order.to_str().into()); - } - if alpha { - args.push(static_val!("ALPHA")); - } - if let Some(dest) = store { - args.push(static_val!(STORE)); - args.push(dest.into()); - } - - Ok((RedisCommandKind::Sort, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn blmpop( - client: &C, - timeout: f64, - keys: MultipleKeys, - direction: LMoveDirection, - count: Option, -) -> Result { - let timeout: RedisValue = timeout.try_into()?; - - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(keys.len() + 4); - args.push(timeout); - args.push(keys.len().try_into()?); - for key in keys.inner().into_iter() { - args.push(key.into()); - } - args.push(direction.to_str().into()); - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.into()); - } - - Ok((RedisCommandKind::BlmPop, args)) - }) - .await?; - - protocol_utils::check_null_timeout(&frame)?; - protocol_utils::frame_to_results(frame) -} - -pub async fn blpop(client: &C, keys: MultipleKeys, timeout: f64) -> Result { - let timeout: RedisValue = timeout.try_into()?; - - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(keys.len() + 1); - for key in keys.inner().into_iter() { - args.push(key.into()); - } - args.push(timeout); - - Ok((RedisCommandKind::BlPop, args)) - }) - .await?; - - protocol_utils::check_null_timeout(&frame)?; - protocol_utils::frame_to_results(frame) -} - -pub async fn brpop(client: &C, keys: MultipleKeys, timeout: f64) -> Result { - let timeout: RedisValue = timeout.try_into()?; - - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(keys.len() + 1); - for key in keys.inner().into_iter() { - args.push(key.into()); - } - args.push(timeout); - - Ok((RedisCommandKind::BrPop, args)) - }) - .await?; - - protocol_utils::check_null_timeout(&frame)?; - protocol_utils::frame_to_results(frame) -} - -pub async fn brpoplpush( - client: &C, - source: RedisKey, - destination: RedisKey, - timeout: f64, -) -> Result { - let timeout: RedisValue = timeout.try_into()?; - - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::BrPopLPush, vec![ - source.into(), - destination.into(), - timeout, - ])) - }) - .await?; - - protocol_utils::check_null_timeout(&frame)?; - protocol_utils::frame_to_results(frame) -} - -pub async fn blmove( - client: &C, - source: RedisKey, - destination: RedisKey, - source_direction: LMoveDirection, - destination_direction: LMoveDirection, - timeout: f64, -) -> Result { - let timeout: RedisValue = timeout.try_into()?; - - let frame = utils::request_response(client, move || { - let args = vec![ - source.into(), - destination.into(), - source_direction.to_str().into(), - destination_direction.to_str().into(), - timeout, - ]; - - Ok((RedisCommandKind::BlMove, args)) - }) - .await?; - - protocol_utils::check_null_timeout(&frame)?; - protocol_utils::frame_to_results(frame) -} - -pub async fn lmpop( - client: &C, - keys: MultipleKeys, - direction: LMoveDirection, - count: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(keys.len() + 3); - args.push(keys.len().try_into()?); - for key in keys.inner().into_iter() { - args.push(key.into()); - } - args.push(direction.to_str().into()); - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.into()); - } - - Ok((RedisCommandKind::LMPop, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn lindex(client: &C, key: RedisKey, index: i64) -> Result { - let args: Vec = vec![key.into(), index.into()]; - args_value_cmd(client, RedisCommandKind::LIndex, args).await -} - -pub async fn linsert( - client: &C, - key: RedisKey, - location: ListLocation, - pivot: RedisValue, - element: RedisValue, -) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::LInsert, vec![ - key.into(), - location.to_str().into(), - pivot, - element, - ])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn llen(client: &C, key: RedisKey) -> Result { - one_arg_value_cmd(client, RedisCommandKind::LLen, key.into()).await -} - -pub async fn lpop(client: &C, key: RedisKey, count: Option) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2); - args.push(key.into()); - - if let Some(count) = count { - args.push(count.try_into()?); - } - - Ok((RedisCommandKind::LPop, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn lpos( - client: &C, - key: RedisKey, - element: RedisValue, - rank: Option, - count: Option, - maxlen: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(8); - args.push(key.into()); - args.push(element); - - if let Some(rank) = rank { - args.push(static_val!(RANK)); - args.push(rank.into()); - } - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.into()); - } - if let Some(maxlen) = maxlen { - args.push(static_val!(MAXLEN)); - args.push(maxlen.into()); - } - - Ok((RedisCommandKind::LPos, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn lpush( - client: &C, - key: RedisKey, - elements: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let elements = elements.into_multiple_values(); - let mut args = Vec::with_capacity(1 + elements.len()); - args.push(key.into()); - - for element in elements.into_iter() { - args.push(element); - } - - Ok((RedisCommandKind::LPush, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn lpushx( - client: &C, - key: RedisKey, - elements: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let elements = elements.into_multiple_values(); - let mut args = Vec::with_capacity(1 + elements.len()); - args.push(key.into()); - - for element in elements.into_iter() { - args.push(element); - } - - Ok((RedisCommandKind::LPushX, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn lrange( - client: &C, - key: RedisKey, - start: i64, - stop: i64, -) -> Result { - let (key, start, stop) = (key.into(), start.into(), stop.into()); - args_values_cmd(client, RedisCommandKind::LRange, vec![key, start, stop]).await -} - -pub async fn lrem( - client: &C, - key: RedisKey, - count: i64, - element: RedisValue, -) -> Result { - let (key, count) = (key.into(), count.into()); - args_value_cmd(client, RedisCommandKind::LRem, vec![key, count, element]).await -} - -pub async fn lset( - client: &C, - key: RedisKey, - index: i64, - element: RedisValue, -) -> Result { - let args = vec![key.into(), index.into(), element]; - args_value_cmd(client, RedisCommandKind::LSet, args).await -} - -pub async fn ltrim( - client: &C, - key: RedisKey, - start: i64, - stop: i64, -) -> Result { - let args = vec![key.into(), start.into(), stop.into()]; - args_value_cmd(client, RedisCommandKind::LTrim, args).await -} - -pub async fn rpop(client: &C, key: RedisKey, count: Option) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2); - args.push(key.into()); - - if let Some(count) = count { - args.push(count.try_into()?); - } - - Ok((RedisCommandKind::Rpop, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn rpoplpush( - client: &C, - source: RedisKey, - dest: RedisKey, -) -> Result { - let args = vec![source.into(), dest.into()]; - args_value_cmd(client, RedisCommandKind::Rpoplpush, args).await -} - -pub async fn lmove( - client: &C, - source: RedisKey, - dest: RedisKey, - source_direction: LMoveDirection, - dest_direction: LMoveDirection, -) -> Result { - let frame = utils::request_response(client, move || { - let args = vec![ - source.into(), - dest.into(), - source_direction.to_str().into(), - dest_direction.to_str().into(), - ]; - - Ok((RedisCommandKind::LMove, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn rpush( - client: &C, - key: RedisKey, - elements: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let elements = elements.into_multiple_values(); - let mut args = Vec::with_capacity(1 + elements.len()); - args.push(key.into()); - - for element in elements.into_iter() { - args.push(element); - } - - Ok((RedisCommandKind::Rpush, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn rpushx( - client: &C, - key: RedisKey, - elements: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let elements = elements.into_multiple_values(); - let mut args = Vec::with_capacity(1 + elements.len()); - args.push(key.into()); - - for element in elements.into_iter() { - args.push(element); - } - - Ok((RedisCommandKind::Rpushx, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/lua.rs b/src/commands/impls/lua.rs deleted file mode 100644 index 55815a42..00000000 --- a/src/commands/impls/lua.rs +++ /dev/null @@ -1,469 +0,0 @@ -use super::*; -#[cfg(feature = "sha-1")] -use crate::util::sha1_hash; -use crate::{ - error::*, - modules::inner::RedisClientInner, - protocol::{ - command::{RedisCommand, RedisCommandKind}, - hashers::ClusterHash, - responders::ResponseKind, - utils as protocol_utils, - }, - runtime::{oneshot_channel, RefCount}, - types::*, - utils, -}; -use bytes::Bytes; -use bytes_utils::Str; -use redis_protocol::resp3::types::BytesFrame as Resp3Frame; -use std::{convert::TryInto, str}; - -/// Check that all the keys in an EVAL* command belong to the same server, returning a key slot that maps to that -/// server. -pub fn check_key_slot(inner: &RefCount, keys: &[RedisKey]) -> Result, RedisError> { - if inner.config.server.is_clustered() { - inner.with_cluster_state(|state| { - let (mut cmd_server, mut cmd_slot) = (None, None); - for key in keys.iter() { - let key_slot = redis_protocol::redis_keyslot(key.as_bytes()); - - if let Some(server) = state.get_server(key_slot) { - if let Some(ref cmd_server) = cmd_server { - if cmd_server != server { - return Err(RedisError::new( - RedisErrorKind::Cluster, - "All keys must belong to the same cluster node.", - )); - } - } else { - cmd_server = Some(server.clone()); - cmd_slot = Some(key_slot); - } - } else { - return Err(RedisError::new( - RedisErrorKind::Cluster, - format!("Missing server for hash slot {}", key_slot), - )); - } - } - - Ok(cmd_slot) - }) - } else { - Ok(None) - } -} - -pub async fn script_load(client: &C, script: Str) -> Result { - one_arg_value_cmd(client, RedisCommandKind::ScriptLoad, script.into()).await -} - -#[cfg(feature = "sha-1")] -pub async fn script_load_cluster(client: &C, script: Str) -> Result { - if !client.inner().config.server.is_clustered() { - return script_load(client, script).await; - } - let hash = sha1_hash(&script); - - let (tx, rx) = oneshot_channel(); - let response = ResponseKind::new_buffer(tx); - let mut command: RedisCommand = (RedisCommandKind::_ScriptLoadCluster, vec![script.into()], response).into(); - - let timeout_dur = utils::prepare_command(client, &mut command); - client.send_command(command)?; - let _ = utils::timeout(rx, timeout_dur).await??; - Ok(hash.into()) -} - -ok_cmd!(script_kill, ScriptKill); - -pub async fn script_kill_cluster(client: &C) -> Result<(), RedisError> { - if !client.inner().config.server.is_clustered() { - return script_kill(client).await; - } - - let (tx, rx) = oneshot_channel(); - let response = ResponseKind::new_buffer(tx); - let mut command: RedisCommand = (RedisCommandKind::_ScriptKillCluster, vec![], response).into(); - - let timeout_dur = utils::prepare_command(client, &mut command); - client.send_command(command)?; - let _ = utils::timeout(rx, timeout_dur).await??; - Ok(()) -} - -pub async fn script_flush(client: &C, r#async: bool) -> Result<(), RedisError> { - let frame = utils::request_response(client, move || { - let arg = static_val!(if r#async { ASYNC } else { SYNC }); - Ok((RedisCommandKind::ScriptFlush, vec![arg])) - }) - .await?; - - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -pub async fn script_flush_cluster(client: &C, r#async: bool) -> Result<(), RedisError> { - if !client.inner().config.server.is_clustered() { - return script_flush(client, r#async).await; - } - - let (tx, rx) = oneshot_channel(); - let arg = static_val!(if r#async { ASYNC } else { SYNC }); - let response = ResponseKind::new_buffer(tx); - let mut command: RedisCommand = (RedisCommandKind::_ScriptFlushCluster, vec![arg], response).into(); - - let timeout_dur = utils::prepare_command(client, &mut command); - client.send_command(command)?; - - let _ = utils::timeout(rx, timeout_dur).await??; - Ok(()) -} - -pub async fn script_exists(client: &C, hashes: MultipleStrings) -> Result { - let frame = utils::request_response(client, move || { - let args = hashes.inner().into_iter().map(|s| s.into()).collect(); - Ok((RedisCommandKind::ScriptExists, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn script_debug(client: &C, flag: ScriptDebugFlag) -> Result<(), RedisError> { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::ScriptDebug, vec![flag.to_str().into()])) - }) - .await?; - - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -pub async fn evalsha( - client: &C, - hash: Str, - keys: MultipleKeys, - cmd_args: MultipleValues, -) -> Result { - let keys = keys.inner(); - let custom_key_slot = check_key_slot(client.inner(), &keys)?; - - let frame = utils::request_response(client, move || { - let cmd_args = cmd_args.into_multiple_values(); - let mut args = Vec::with_capacity(2 + keys.len() + cmd_args.len()); - args.push(hash.into()); - args.push(keys.len().try_into()?); - - for key in keys.into_iter() { - args.push(key.into()); - } - for arg in cmd_args.into_iter() { - args.push(arg); - } - - let mut command: RedisCommand = (RedisCommandKind::EvalSha, args).into(); - command.hasher = custom_key_slot.map(ClusterHash::Custom).unwrap_or(ClusterHash::Random); - command.can_pipeline = false; - Ok(command) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn eval( - client: &C, - script: Str, - keys: MultipleKeys, - cmd_args: MultipleValues, -) -> Result { - let keys = keys.inner(); - let custom_key_slot = check_key_slot(client.inner(), &keys)?; - - let frame = utils::request_response(client, move || { - let cmd_args = cmd_args.into_multiple_values(); - let mut args = Vec::with_capacity(2 + keys.len() + cmd_args.len()); - args.push(script.into()); - args.push(keys.len().try_into()?); - - for key in keys.into_iter() { - args.push(key.into()); - } - for arg in cmd_args.into_iter() { - args.push(arg); - } - - let mut command: RedisCommand = (RedisCommandKind::Eval, args).into(); - command.hasher = custom_key_slot.map(ClusterHash::Custom).unwrap_or(ClusterHash::Random); - command.can_pipeline = false; - Ok(command) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn fcall( - client: &C, - func: Str, - keys: MultipleKeys, - args: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let args = args.into_multiple_values(); - let mut arguments = Vec::with_capacity(keys.len() + args.len() + 2); - let mut custom_key_slot = None; - - arguments.push(func.into()); - arguments.push(keys.len().try_into()?); - - for key in keys.inner().into_iter() { - custom_key_slot = Some(key.cluster_hash()); - arguments.push(key.into()); - } - for arg in args.into_iter() { - arguments.push(arg); - } - - let mut command: RedisCommand = (RedisCommandKind::Fcall, arguments).into(); - command.hasher = custom_key_slot.map(ClusterHash::Custom).unwrap_or(ClusterHash::Random); - command.can_pipeline = false; - Ok(command) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn fcall_ro( - client: &C, - func: Str, - keys: MultipleKeys, - args: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let args = args.into_multiple_values(); - let mut arguments = Vec::with_capacity(keys.len() + args.len() + 2); - let mut custom_key_slot = None; - - arguments.push(func.into()); - arguments.push(keys.len().try_into()?); - - for key in keys.inner().into_iter() { - custom_key_slot = Some(key.cluster_hash()); - arguments.push(key.into()); - } - for arg in args.into_iter() { - arguments.push(arg); - } - - let mut command: RedisCommand = (RedisCommandKind::FcallRO, arguments).into(); - command.hasher = custom_key_slot.map(ClusterHash::Custom).unwrap_or(ClusterHash::Random); - command.can_pipeline = false; - Ok(command) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn function_delete(client: &C, library_name: Str) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::FunctionDelete, vec![library_name.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn function_delete_cluster(client: &C, library_name: Str) -> Result<(), RedisError> { - if !client.inner().config.server.is_clustered() { - return function_delete(client, library_name).await.map(|_| ()); - } - - let (tx, rx) = oneshot_channel(); - let args: Vec = vec![library_name.into()]; - - let response = ResponseKind::new_buffer(tx); - let mut command: RedisCommand = (RedisCommandKind::_FunctionDeleteCluster, args, response).into(); - - let timeout_dur = utils::prepare_command(client, &mut command); - client.send_command(command)?; - - let _ = utils::timeout(rx, timeout_dur).await??; - Ok(()) -} - -pub async fn function_flush(client: &C, r#async: bool) -> Result { - let frame = utils::request_response(client, move || { - let args = if r#async { - vec![static_val!(ASYNC)] - } else { - vec![static_val!(SYNC)] - }; - - Ok((RedisCommandKind::FunctionFlush, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn function_flush_cluster(client: &C, r#async: bool) -> Result<(), RedisError> { - if !client.inner().config.server.is_clustered() { - return function_flush(client, r#async).await.map(|_| ()); - } - - let (tx, rx) = oneshot_channel(); - let args = if r#async { - vec![static_val!(ASYNC)] - } else { - vec![static_val!(SYNC)] - }; - - let response = ResponseKind::new_buffer(tx); - let command: RedisCommand = (RedisCommandKind::_FunctionFlushCluster, args, response).into(); - client.send_command(command)?; - - let _ = rx.await??; - Ok(()) -} - -pub async fn function_kill(client: &C) -> Result { - let inner = client.inner(); - let command = RedisCommand::new(RedisCommandKind::FunctionKill, vec![]); - - let frame = utils::backchannel_request_response(inner, command, true).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn function_list( - client: &C, - library_name: Option, - withcode: bool, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(3); - - if let Some(library_name) = library_name { - args.push(static_val!(LIBRARYNAME)); - args.push(library_name.into()); - } - if withcode { - args.push(static_val!(WITHCODE)); - } - - Ok((RedisCommandKind::FunctionList, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn function_load(client: &C, replace: bool, code: Str) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2); - if replace { - args.push(static_val!(REPLACE)); - } - args.push(code.into()); - - Ok((RedisCommandKind::FunctionLoad, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn function_load_cluster( - client: &C, - replace: bool, - code: Str, -) -> Result { - if !client.inner().config.server.is_clustered() { - return function_load(client, replace, code).await; - } - - let (tx, rx) = oneshot_channel(); - let mut args: Vec = Vec::with_capacity(2); - if replace { - args.push(static_val!(REPLACE)); - } - args.push(code.into()); - - let response = ResponseKind::new_buffer(tx); - let mut command: RedisCommand = (RedisCommandKind::_FunctionLoadCluster, args, response).into(); - - let timeout_dur = utils::prepare_command(client, &mut command); - client.send_command(command)?; - - // each value in the response array is the response from a different primary node - match utils::timeout(rx, timeout_dur).await?? { - Resp3Frame::Array { mut data, .. } => { - if let Some(frame) = data.pop() { - protocol_utils::frame_to_results(frame) - } else { - Err(RedisError::new( - RedisErrorKind::Protocol, - "Missing library name response frame.", - )) - } - }, - Resp3Frame::SimpleError { data, .. } => Err(protocol_utils::pretty_error(&data)), - Resp3Frame::BlobError { data, .. } => { - let parsed = str::from_utf8(&data)?; - Err(protocol_utils::pretty_error(parsed)) - }, - _ => Err(RedisError::new(RedisErrorKind::Protocol, "Invalid response type.")), - } -} - -pub async fn function_restore( - client: &C, - serialized: Bytes, - policy: FnPolicy, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2); - args.push(serialized.into()); - args.push(policy.to_str().into()); - - Ok((RedisCommandKind::FunctionRestore, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn function_restore_cluster( - client: &C, - serialized: Bytes, - policy: FnPolicy, -) -> Result<(), RedisError> { - if !client.inner().config.server.is_clustered() { - return function_restore(client, serialized, policy).await.map(|_| ()); - } - - let (tx, rx) = oneshot_channel(); - let args: Vec = vec![serialized.into(), policy.to_str().into()]; - - let response = ResponseKind::new_buffer(tx); - let mut command: RedisCommand = (RedisCommandKind::_FunctionRestoreCluster, args, response).into(); - - let timeout_dur = utils::prepare_command(client, &mut command); - client.send_command(command)?; - let _ = utils::timeout(rx, timeout_dur).await??; - Ok(()) -} - -pub async fn function_stats(client: &C) -> Result { - let inner = client.inner(); - let command = RedisCommand::new(RedisCommandKind::FunctionStats, vec![]); - - let frame = utils::backchannel_request_response(inner, command, true).await?; - protocol_utils::frame_to_results(frame) -} - -value_cmd!(function_dump, FunctionDump); diff --git a/src/commands/impls/memory.rs b/src/commands/impls/memory.rs deleted file mode 100644 index 4ccfe500..00000000 --- a/src/commands/impls/memory.rs +++ /dev/null @@ -1,36 +0,0 @@ -use super::*; -use crate::{ - protocol::{command::RedisCommandKind, utils as protocol_utils}, - types::*, - utils, -}; - -value_cmd!(memory_doctor, MemoryDoctor); -value_cmd!(memory_malloc_stats, MemoryMallocStats); -ok_cmd!(memory_purge, MemoryPurge); - -pub async fn memory_stats(client: &C) -> Result { - let response = utils::request_response(client, || Ok((RedisCommandKind::MemoryStats, vec![]))).await?; - protocol_utils::frame_to_results(response) -} - -pub async fn memory_usage( - client: &C, - key: RedisKey, - samples: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(3); - args.push(key.into()); - - if let Some(samples) = samples { - args.push(static_val!(SAMPLES)); - args.push(samples.into()); - } - - Ok((RedisCommandKind::MemoryUsage, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/mod.rs b/src/commands/impls/mod.rs deleted file mode 100644 index 39e113b5..00000000 --- a/src/commands/impls/mod.rs +++ /dev/null @@ -1,211 +0,0 @@ -#![allow(unused_macros)] -#![allow(dead_code)] - -use crate::{ - error::RedisError, - interfaces::ClientLike, - protocol::{command::RedisCommandKind, utils as protocol_utils}, - types::RedisValue, - utils, -}; - -pub static MATCH: &str = "MATCH"; -pub static COUNT: &str = "COUNT"; -pub static TYPE: &str = "TYPE"; -#[cfg(any(feature = "i-geo", feature = "i-sorted-sets"))] -pub static CHANGED: &str = "CH"; -#[cfg(any(feature = "i-lists", feature = "i-sorted-sets", feature = "i-streams"))] -pub static LIMIT: &str = "LIMIT"; -pub static GET: &str = "GET"; -pub static RESET: &str = "RESET"; -pub static TO: &str = "TO"; -pub static FORCE: &str = "FORCE"; -pub static ABORT: &str = "ABORT"; -pub static TIMEOUT: &str = "TIMEOUT"; -pub static LEN: &str = "LEN"; -pub static DB: &str = "DB"; -pub static REPLACE: &str = "REPLACE"; -pub static ID: &str = "ID"; -pub static ANY: &str = "ANY"; -pub static STORE: &str = "STORE"; -pub static WITH_VALUES: &str = "WITHVALUES"; -pub static SYNC: &str = "SYNC"; -pub static ASYNC: &str = "ASYNC"; -pub static RANK: &str = "RANK"; -pub static MAXLEN: &str = "MAXLEN"; -pub static REV: &str = "REV"; -pub static ABSTTL: &str = "ABSTTL"; -pub static IDLE_TIME: &str = "IDLETIME"; -pub static FREQ: &str = "FREQ"; -pub static FULL: &str = "FULL"; -pub static NOMKSTREAM: &str = "NOMKSTREAM"; -pub static MINID: &str = "MINID"; -pub static BLOCK: &str = "BLOCK"; -pub static STREAMS: &str = "STREAMS"; -pub static MKSTREAM: &str = "MKSTREAM"; -pub static GROUP: &str = "GROUP"; -pub static NOACK: &str = "NOACK"; -pub static IDLE: &str = "IDLE"; -pub static TIME: &str = "TIME"; -pub static RETRYCOUNT: &str = "RETRYCOUNT"; -pub static JUSTID: &str = "JUSTID"; -pub static SAMPLES: &str = "SAMPLES"; -pub static LIBRARYNAME: &str = "LIBRARYNAME"; -pub static WITHCODE: &str = "WITHCODE"; -pub static IDX: &str = "IDX"; -pub static MINMATCHLEN: &str = "MINMATCHLEN"; -pub static WITHMATCHLEN: &str = "WITHMATCHLEN"; - -/// Macro to generate a command function that takes no arguments and expects an OK response - returning `()` to the -/// caller. -macro_rules! ok_cmd( - ($name:ident, $cmd:tt) => { - pub async fn $name(client: &C) -> Result<(), RedisError> { - let frame = crate::utils::request_response(client, || Ok((RedisCommandKind::$cmd, vec![]))).await?; - let response = crate::protocol::utils::frame_to_results(frame)?; - crate::protocol::utils::expect_ok(&response) - } - } -); - -// TODO clean this up -/// Macro to generate a command function that takes no arguments and returns a single `RedisValue` to the caller. -macro_rules! simple_cmd( - ($name:ident, $cmd:tt, $res:ty) => { - pub async fn $name(client: &C) -> Result<$res, RedisError> { - let frame = crate::utils::request_response(client, || Ok((RedisCommandKind::$cmd, vec![]))).await?; - crate::protocol::utils::frame_to_results(frame) - } - } -); - -/// Macro to generate a command function that takes no arguments and returns a single `RedisValue` to the caller. -macro_rules! value_cmd( - ($name:ident, $cmd:tt) => { - simple_cmd!($name, $cmd, RedisValue); - } -); - -/// Macro to generate a command function that takes no arguments and returns a potentially nested `RedisValue` to the -/// caller. -macro_rules! values_cmd( - ($name:ident, $cmd:tt) => { - pub async fn $name(client: &C) -> Result { - let frame = crate::utils::request_response(client, || Ok((RedisCommandKind::$cmd, vec![]))).await?; - crate::protocol::utils::frame_to_results(frame) - } - } -); - -/// A function that issues a command that only takes one argument and returns a single `RedisValue`. -pub async fn one_arg_value_cmd( - client: &C, - kind: RedisCommandKind, - arg: RedisValue, -) -> Result { - let frame = utils::request_response(client, move || Ok((kind, vec![arg]))).await?; - protocol_utils::frame_to_results(frame) -} - -/// A function that issues a command that only takes one argument and returns a potentially nested `RedisValue`. -pub async fn one_arg_values_cmd( - client: &C, - kind: RedisCommandKind, - arg: RedisValue, -) -> Result { - let frame = utils::request_response(client, move || Ok((kind, vec![arg]))).await?; - protocol_utils::frame_to_results(frame) -} - -/// A function that issues a command that only takes one argument and expects an OK response - returning `()` to the -/// caller. -pub async fn one_arg_ok_cmd( - client: &C, - kind: RedisCommandKind, - arg: RedisValue, -) -> Result<(), RedisError> { - let frame = utils::request_response(client, move || Ok((kind, vec![arg]))).await?; - - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -/// A function that issues a command that takes any number of arguments and returns a single `RedisValue` to the -/// caller. -pub async fn args_value_cmd( - client: &C, - kind: RedisCommandKind, - args: Vec, -) -> Result { - let frame = utils::request_response(client, move || Ok((kind, args))).await?; - protocol_utils::frame_to_results(frame) -} - -/// A function that issues a command that takes any number of arguments and returns a potentially nested `RedisValue` -/// to the caller. -pub async fn args_values_cmd( - client: &C, - kind: RedisCommandKind, - args: Vec, -) -> Result { - let frame = utils::request_response(client, move || Ok((kind, args))).await?; - protocol_utils::frame_to_results(frame) -} - -/// A function that issues a command that takes any number of arguments and expects an OK response - returning `()` to -/// the caller. -pub async fn args_ok_cmd( - client: &C, - kind: RedisCommandKind, - args: Vec, -) -> Result<(), RedisError> { - let frame = utils::request_response(client, move || Ok((kind, args))).await?; - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -#[cfg(feature = "i-acl")] -pub mod acl; -#[cfg(feature = "i-client")] -pub mod client; -#[cfg(feature = "i-cluster")] -pub mod cluster; -#[cfg(feature = "i-config")] -pub mod config; -#[cfg(feature = "i-geo")] -pub mod geo; -#[cfg(feature = "i-hashes")] -pub mod hashes; -#[cfg(feature = "i-hyperloglog")] -pub mod hyperloglog; -#[cfg(feature = "i-keys")] -pub mod keys; -#[cfg(feature = "i-lists")] -pub mod lists; -#[cfg(feature = "i-scripts")] -pub mod lua; -#[cfg(feature = "i-memory")] -pub mod memory; -#[cfg(feature = "i-pubsub")] -pub mod pubsub; -#[cfg(feature = "i-redis-json")] -pub mod redis_json; -#[cfg(feature = "i-redisearch")] -pub mod redisearch; -pub mod scan; -#[cfg(feature = "sentinel-client")] -pub mod sentinel; -pub mod server; -#[cfg(feature = "i-sets")] -pub mod sets; -#[cfg(feature = "i-slowlog")] -pub mod slowlog; -#[cfg(feature = "i-sorted-sets")] -pub mod sorted_sets; -#[cfg(feature = "i-streams")] -pub mod streams; -pub mod strings; -#[cfg(feature = "i-time-series")] -pub mod timeseries; -#[cfg(feature = "i-tracking")] -pub mod tracking; diff --git a/src/commands/impls/pubsub.rs b/src/commands/impls/pubsub.rs deleted file mode 100644 index 29489a16..00000000 --- a/src/commands/impls/pubsub.rs +++ /dev/null @@ -1,170 +0,0 @@ -use super::*; -use crate::{ - protocol::{ - command::{RedisCommand, RedisCommandKind}, - utils as protocol_utils, - }, - types::*, - utils, -}; -use bytes_utils::Str; -use redis_protocol::redis_keyslot; - -fn cluster_hash_legacy_command(client: &C, command: &mut RedisCommand) { - if client.is_clustered() { - // send legacy (non-sharded) pubsub commands to the same node in a cluster so that `UNSUBSCRIBE` (without args) - // works correctly. otherwise we'd have to send `UNSUBSCRIBE` to every node. - let hash_slot = redis_keyslot(client.id().as_bytes()); - command.hasher = ClusterHash::Custom(hash_slot); - } -} - -pub async fn subscribe(client: &C, channels: MultipleStrings) -> Result<(), RedisError> { - let args = channels.inner().into_iter().map(|c| c.into()).collect(); - let mut command = RedisCommand::new(RedisCommandKind::Subscribe, args); - cluster_hash_legacy_command(client, &mut command); - - let frame = utils::request_response(client, move || Ok(command)).await?; - protocol_utils::frame_to_results(frame).map(|_| ()) -} - -pub async fn unsubscribe(client: &C, channels: MultipleStrings) -> Result<(), RedisError> { - let args = channels.inner().into_iter().map(|c| c.into()).collect(); - let mut command = RedisCommand::new(RedisCommandKind::Unsubscribe, args); - cluster_hash_legacy_command(client, &mut command); - - let frame = utils::request_response(client, move || Ok(command)).await?; - protocol_utils::frame_to_results(frame).map(|_| ()) -} - -pub async fn publish(client: &C, channel: Str, message: RedisValue) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Publish, vec![channel.into(), message])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn psubscribe(client: &C, patterns: MultipleStrings) -> Result<(), RedisError> { - let args = patterns.inner().into_iter().map(|c| c.into()).collect(); - let mut command = RedisCommand::new(RedisCommandKind::Psubscribe, args); - cluster_hash_legacy_command(client, &mut command); - - let frame = utils::request_response(client, move || Ok(command)).await?; - protocol_utils::frame_to_results(frame).map(|_| ()) -} - -pub async fn punsubscribe(client: &C, patterns: MultipleStrings) -> Result<(), RedisError> { - let args = patterns.inner().into_iter().map(|c| c.into()).collect(); - let mut command = RedisCommand::new(RedisCommandKind::Punsubscribe, args); - cluster_hash_legacy_command(client, &mut command); - - let frame = utils::request_response(client, move || Ok(command)).await?; - protocol_utils::frame_to_results(frame).map(|_| ()) -} - -pub async fn spublish( - client: &C, - channel: Str, - message: RedisValue, -) -> Result { - let frame = utils::request_response(client, move || { - let mut command: RedisCommand = (RedisCommandKind::Spublish, vec![channel.into(), message]).into(); - command.hasher = ClusterHash::FirstKey; - - Ok(command) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ssubscribe(client: &C, channels: MultipleStrings) -> Result<(), RedisError> { - let args = channels.inner().into_iter().map(|c| c.into()).collect(); - let mut command = RedisCommand::new(RedisCommandKind::Ssubscribe, args); - command.hasher = ClusterHash::FirstKey; - - let frame = utils::request_response(client, move || Ok(command)).await?; - protocol_utils::frame_to_results(frame).map(|_| ()) -} - -pub async fn sunsubscribe(client: &C, channels: MultipleStrings) -> Result<(), RedisError> { - let args = channels.inner().into_iter().map(|c| c.into()).collect(); - let mut command = RedisCommand::new(RedisCommandKind::Sunsubscribe, args); - command.hasher = ClusterHash::FirstKey; - - let frame = utils::request_response(client, move || Ok(command)).await?; - protocol_utils::frame_to_results(frame).map(|_| ()) -} - -pub async fn pubsub_channels(client: &C, pattern: Str) -> Result { - let frame = utils::request_response(client, || { - let args = if pattern.is_empty() { - vec![] - } else { - vec![pattern.into()] - }; - - let mut command: RedisCommand = RedisCommand::new(RedisCommandKind::PubsubChannels, args); - cluster_hash_legacy_command(client, &mut command); - - Ok(command) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn pubsub_numpat(client: &C) -> Result { - let frame = utils::request_response(client, || { - let mut command: RedisCommand = RedisCommand::new(RedisCommandKind::PubsubNumpat, vec![]); - cluster_hash_legacy_command(client, &mut command); - - Ok(command) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn pubsub_numsub(client: &C, channels: MultipleStrings) -> Result { - let frame = utils::request_response(client, || { - let args: Vec = channels.inner().into_iter().map(|s| s.into()).collect(); - let mut command: RedisCommand = RedisCommand::new(RedisCommandKind::PubsubNumsub, args); - cluster_hash_legacy_command(client, &mut command); - - Ok(command) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn pubsub_shardchannels(client: &C, pattern: Str) -> Result { - let frame = utils::request_response(client, || { - Ok((RedisCommandKind::PubsubShardchannels, vec![pattern.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn pubsub_shardnumsub( - client: &C, - channels: MultipleStrings, -) -> Result { - let frame = utils::request_response(client, || { - let args: Vec = channels.inner().into_iter().map(|s| s.into()).collect(); - let has_args = !args.is_empty(); - let mut command: RedisCommand = RedisCommand::new(RedisCommandKind::PubsubShardnumsub, args); - if !has_args { - cluster_hash_legacy_command(client, &mut command); - } - - Ok(command) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/redis_json.rs b/src/commands/impls/redis_json.rs deleted file mode 100644 index e0ca7999..00000000 --- a/src/commands/impls/redis_json.rs +++ /dev/null @@ -1,358 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - interfaces::{ClientLike, RedisResult}, - protocol::{command::RedisCommandKind, utils as protocol_utils}, - types::{MultipleKeys, MultipleStrings, RedisKey, RedisValue, SetOptions}, - utils, -}; -use bytes_utils::Str; -use serde_json::Value; - -const INDENT: &str = "INDENT"; -const NEWLINE: &str = "NEWLINE"; -const SPACE: &str = "SPACE"; - -fn key_path_args(key: RedisKey, path: Option, extra: usize) -> Vec { - let mut out = Vec::with_capacity(2 + extra); - out.push(key.into()); - if let Some(path) = path { - out.push(path.into()); - } - out -} - -/// Convert the provided json value to a redis value by serializing into a json string. -fn value_to_bulk_str(value: &Value) -> Result { - Ok(match value { - Value::String(ref s) => RedisValue::String(Str::from(s)), - _ => RedisValue::String(Str::from(serde_json::to_string(value)?)), - }) -} - -/// Convert the provided json value to a redis value directly without serializing into a string. This only works with -/// scalar values. -fn json_to_redis(value: Value) -> Result { - let out = match value { - Value::String(s) => Some(RedisValue::String(Str::from(s))), - Value::Null => Some(RedisValue::Null), - Value::Number(n) => { - if n.is_f64() { - n.as_f64().map(RedisValue::Double) - } else { - n.as_i64().map(RedisValue::Integer) - } - }, - Value::Bool(b) => Some(RedisValue::Boolean(b)), - _ => None, - }; - - out.ok_or(RedisError::new( - RedisErrorKind::InvalidArgument, - "Expected string or number.", - )) -} - -fn values_to_bulk(values: &[Value]) -> Result, RedisError> { - values.iter().map(value_to_bulk_str).collect() -} - -pub async fn json_arrappend( - client: &C, - key: RedisKey, - path: Str, - values: Vec, -) -> RedisResult { - let frame = utils::request_response(client, || { - let mut args = key_path_args(key, Some(path), values.len()); - args.extend(values_to_bulk(&values)?); - - Ok((RedisCommandKind::JsonArrAppend, args)) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_arrindex( - client: &C, - key: RedisKey, - path: Str, - value: Value, - start: Option, - stop: Option, -) -> RedisResult { - let frame = utils::request_response(client, || { - let mut args = Vec::with_capacity(5); - args.extend([key.into(), path.into(), value_to_bulk_str(&value)?]); - if let Some(start) = start { - args.push(start.into()); - } - if let Some(stop) = stop { - args.push(stop.into()); - } - - Ok((RedisCommandKind::JsonArrIndex, args)) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_arrinsert( - client: &C, - key: RedisKey, - path: Str, - index: i64, - values: Vec, -) -> RedisResult { - let frame = utils::request_response(client, || { - let mut args = Vec::with_capacity(3 + values.len()); - args.extend([key.into(), path.into(), index.into()]); - args.extend(values_to_bulk(&values)?); - - Ok((RedisCommandKind::JsonArrInsert, args)) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_arrlen(client: &C, key: RedisKey, path: Option) -> RedisResult { - let frame = utils::request_response(client, || { - Ok((RedisCommandKind::JsonArrLen, key_path_args(key, path, 0))) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_arrpop( - client: &C, - key: RedisKey, - path: Option, - index: Option, -) -> RedisResult { - let frame = utils::request_response(client, || { - let mut args = key_path_args(key, path, 1); - if let Some(index) = index { - args.push(index.into()); - } - - Ok((RedisCommandKind::JsonArrPop, args)) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_arrtrim( - client: &C, - key: RedisKey, - path: Str, - start: i64, - stop: i64, -) -> RedisResult { - let frame = utils::request_response(client, || { - Ok((RedisCommandKind::JsonArrTrim, vec![ - key.into(), - path.into(), - start.into(), - stop.into(), - ])) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_clear(client: &C, key: RedisKey, path: Option) -> RedisResult { - let frame = utils::request_response(client, || { - Ok((RedisCommandKind::JsonClear, key_path_args(key, path, 0))) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_debug_memory( - client: &C, - key: RedisKey, - path: Option, -) -> RedisResult { - let frame = utils::request_response(client, || { - Ok((RedisCommandKind::JsonDebugMemory, key_path_args(key, path, 0))) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_del(client: &C, key: RedisKey, path: Str) -> RedisResult { - let frame = utils::request_response(client, || { - Ok((RedisCommandKind::JsonDel, key_path_args(key, Some(path), 0))) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_get( - client: &C, - key: RedisKey, - indent: Option, - newline: Option, - space: Option, - paths: MultipleStrings, -) -> RedisResult { - let frame = utils::request_response(client, || { - let mut args = Vec::with_capacity(7 + paths.len()); - args.push(key.into()); - if let Some(indent) = indent { - args.push(static_val!(INDENT)); - args.push(indent.into()); - } - if let Some(newline) = newline { - args.push(static_val!(NEWLINE)); - args.push(newline.into()); - } - if let Some(space) = space { - args.push(static_val!(SPACE)); - args.push(space.into()); - } - args.extend(paths.into_values()); - - Ok((RedisCommandKind::JsonGet, args)) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_merge( - client: &C, - key: RedisKey, - path: Str, - value: Value, -) -> RedisResult { - let frame = utils::request_response(client, || { - Ok((RedisCommandKind::JsonMerge, vec![ - key.into(), - path.into(), - value_to_bulk_str(&value)?, - ])) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_mget(client: &C, keys: MultipleKeys, path: Str) -> RedisResult { - let frame = utils::request_response(client, || { - let mut args = Vec::with_capacity(keys.len() + 1); - args.extend(keys.into_values()); - args.push(path.into()); - - Ok((RedisCommandKind::JsonMGet, args)) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_mset(client: &C, values: Vec<(RedisKey, Str, Value)>) -> RedisResult { - let frame = utils::request_response(client, || { - let mut args = Vec::with_capacity(values.len() * 3); - for (key, path, value) in values.into_iter() { - args.extend([key.into(), path.into(), value_to_bulk_str(&value)?]); - } - - Ok((RedisCommandKind::JsonMSet, args)) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_numincrby( - client: &C, - key: RedisKey, - path: Str, - value: Value, -) -> RedisResult { - let frame = utils::request_response(client, || { - Ok((RedisCommandKind::JsonNumIncrBy, vec![ - key.into(), - path.into(), - json_to_redis(value)?, - ])) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_objkeys(client: &C, key: RedisKey, path: Option) -> RedisResult { - let frame = utils::request_response(client, || { - Ok((RedisCommandKind::JsonObjKeys, key_path_args(key, path, 0))) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_objlen(client: &C, key: RedisKey, path: Option) -> RedisResult { - let frame = utils::request_response(client, || { - Ok((RedisCommandKind::JsonObjLen, key_path_args(key, path, 0))) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_resp(client: &C, key: RedisKey, path: Option) -> RedisResult { - let frame = - utils::request_response(client, || Ok((RedisCommandKind::JsonResp, key_path_args(key, path, 0)))).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_set( - client: &C, - key: RedisKey, - path: Str, - value: Value, - options: Option, -) -> RedisResult { - let frame = utils::request_response(client, || { - let mut args = key_path_args(key, Some(path), 2); - args.push(value_to_bulk_str(&value)?); - if let Some(options) = options { - args.push(options.to_str().into()); - } - - Ok((RedisCommandKind::JsonSet, args)) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_strappend( - client: &C, - key: RedisKey, - path: Option, - value: Value, -) -> RedisResult { - let frame = utils::request_response(client, || { - let mut args = key_path_args(key, path, 1); - args.push(value_to_bulk_str(&value)?); - - Ok((RedisCommandKind::JsonStrAppend, args)) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_strlen(client: &C, key: RedisKey, path: Option) -> RedisResult { - let frame = utils::request_response(client, || { - Ok((RedisCommandKind::JsonStrLen, key_path_args(key, path, 0))) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_toggle(client: &C, key: RedisKey, path: Str) -> RedisResult { - let frame = utils::request_response(client, || { - Ok((RedisCommandKind::JsonToggle, key_path_args(key, Some(path), 0))) - }) - .await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn json_type(client: &C, key: RedisKey, path: Option) -> RedisResult { - let frame = - utils::request_response(client, || Ok((RedisCommandKind::JsonType, key_path_args(key, path, 0)))).await?; - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/redisearch.rs b/src/commands/impls/redisearch.rs deleted file mode 100644 index 680a25f4..00000000 --- a/src/commands/impls/redisearch.rs +++ /dev/null @@ -1,868 +0,0 @@ -use crate::{ - commands::{args_values_cmd, one_arg_values_cmd, COUNT, LEN, LIMIT}, - error::RedisError, - interfaces::ClientLike, - protocol::{command::RedisCommandKind, utils as protocol_utils}, - types::{ - AggregateOperation, - FtAggregateOptions, - FtAlterOptions, - FtCreateOptions, - FtSearchOptions, - Load, - MultipleStrings, - RedisKey, - RedisValue, - SearchSchema, - SearchSchemaKind, - SpellcheckTerms, - }, - utils, -}; -use bytes::Bytes; -use bytes_utils::Str; - -static DD: &str = "DD"; -static DIALECT: &str = "DIALECT"; -static DISTANCE: &str = "DISTANCE"; -static INCLUDE: &str = "INCLUDE"; -static EXCLUDE: &str = "EXCLUDE"; -static TERMS: &str = "TERMS"; -static INCR: &str = "INCR"; -static PAYLOAD: &str = "PAYLOAD"; -static FUZZY: &str = "FUZZY"; -static WITHSCORES: &str = "WITHSCORES"; -static WITHPAYLOADS: &str = "WITHPAYLOADS"; -static MAX: &str = "MAX"; -static SKIPINITIALSCAN: &str = "SKIPINITIALSCAN"; -static NOCONTENT: &str = "NOCONTENT"; -static VERBATIM: &str = "VERBATIM"; -static NOSTOPWORDS: &str = "NOSTOPWORDS"; -static WITHSORTKEYS: &str = "WITHSORTKEYS"; -static FILTER: &str = "FILTER"; -static GEOFILTER: &str = "GEOFILTER"; -static INKEYS: &str = "INKEYS"; -static INFIELDS: &str = "INFIELDS"; -static _RETURN: &str = "RETURN"; -static AS: &str = "AS"; -static SUMMARIZE: &str = "SUMMARIZE"; -static FIELDS: &str = "FIELDS"; -static FRAGS: &str = "FRAGS"; -static SEPARATOR: &str = "SEPARATOR"; -static HIGHLIGHT: &str = "HIGHLIGHT"; -static TAGS: &str = "TAGS"; -static SLOP: &str = "SLOP"; -static TIMEOUT: &str = "TIMEOUT"; -static INORDER: &str = "INORDER"; -static LANGUAGE: &str = "LANGUAGE"; -static EXPANDER: &str = "EXPANDER"; -static SCORER: &str = "SCORER"; -static EXPLAINSCORE: &str = "EXPLAINSCORE"; -static SORTBY: &str = "SORTBY"; -static PARAMS: &str = "PARAMS"; -static WITHCOUNT: &str = "WITHCOUNT"; -static LOAD: &str = "LOAD"; -static WITHCURSOR: &str = "WITHCURSOR"; -static MAXIDLE: &str = "MAXIDLE"; -static APPLY: &str = "APPLY"; -static GROUPBY: &str = "GROUPBY"; -static REDUCE: &str = "REDUCE"; -static ON: &str = "ON"; -static HASH: &str = "HASH"; -static JSON: &str = "JSON"; -static PREFIX: &str = "PREFIX"; -static LANGUAGE_FIELD: &str = "LANGUAGE_FIELD"; -static SCORE: &str = "SCORE"; -static SCORE_FIELD: &str = "SCORE_FIELD"; -static PAYLOAD_FIELD: &str = "PAYLOAD_FIELD"; -static MAXTEXTFIELDS: &str = "MAXTEXTFIELDS"; -static TEMPORARY: &str = "TEMPORARY"; -static NOOFFSETS: &str = "NOOFFSETS"; -static NOHL: &str = "NOHL"; -static NOFIELDS: &str = "NOFIELDS"; -static NOFREQS: &str = "NOFREQS"; -static STOPWORDS: &str = "STOPWORDS"; -static SCHEMA: &str = "SCHEMA"; -static ADD: &str = "ADD"; -static SORTABLE: &str = "SORTABLE"; -static UNF: &str = "UNF"; -static NOINDEX: &str = "NOINDEX"; -static NOSTEM: &str = "NOSTEM"; -static PHONETIC: &str = "PHONETIC"; -static WEIGHT: &str = "WEIGHT"; -static CASESENSITIVE: &str = "CASESENSITIVE"; -static WITHSUFFIXTRIE: &str = "WITHSUFFIXTRIE"; -static TEXT: &str = "TEXT"; -static TAG: &str = "TAG"; -static NUMERIC: &str = "NUMERIC"; -static GEO: &str = "GEO"; -static VECTOR: &str = "VECTOR"; -static GEOSHAPE: &str = "GEOSHAPE"; - -fn gen_aggregate_op(args: &mut Vec, operation: AggregateOperation) -> Result<(), RedisError> { - match operation { - AggregateOperation::Filter { expression } => { - args.extend([static_val!(FILTER), expression.into()]); - }, - AggregateOperation::Limit { offset, num } => { - args.extend([static_val!(LIMIT), offset.try_into()?, num.try_into()?]); - }, - AggregateOperation::Apply { expression, name } => { - args.extend([static_val!(APPLY), expression.into(), static_val!(AS), name.into()]); - }, - AggregateOperation::SortBy { properties, max } => { - args.extend([static_val!(SORTBY), (properties.len() * 2).try_into()?]); - for (property, order) in properties.into_iter() { - args.extend([property.into(), order.to_str().into()]); - } - if let Some(max) = max { - args.extend([static_val!(MAX), max.try_into()?]); - } - }, - AggregateOperation::GroupBy { fields, reducers } => { - args.extend([static_val!(GROUPBY), fields.len().try_into()?]); - args.extend(fields.into_iter().map(|f| f.into())); - - for reducer in reducers.into_iter() { - args.extend([ - static_val!(REDUCE), - static_val!(reducer.func.to_str()), - reducer.args.len().try_into()?, - ]); - args.extend(reducer.args.into_iter().map(|a| a.into())); - if let Some(name) = reducer.name { - args.extend([static_val!(AS), name.into()]); - } - } - }, - }; - - Ok(()) -} - -fn gen_aggregate_options(args: &mut Vec, options: FtAggregateOptions) -> Result<(), RedisError> { - if options.verbatim { - args.push(static_val!(VERBATIM)); - } - if let Some(load) = options.load { - match load { - Load::All => { - args.extend([static_val!(LOAD), static_val!("*")]); - }, - Load::Some(fields) => { - if !fields.is_empty() { - args.extend([static_val!(LOAD), fields.len().try_into()?]); - for field in fields.into_iter() { - args.push(field.identifier.into()); - if let Some(property) = field.property { - args.extend([static_val!(AS), property.into()]); - } - } - } - }, - } - } - if let Some(timeout) = options.timeout { - args.extend([static_val!(TIMEOUT), timeout.into()]); - } - for operation in options.pipeline.into_iter() { - gen_aggregate_op(args, operation)?; - } - if let Some(cursor) = options.cursor { - args.push(static_val!(WITHCURSOR)); - if let Some(count) = cursor.count { - args.extend([static_val!(COUNT), count.try_into()?]); - } - if let Some(idle) = cursor.max_idle { - args.extend([static_val!(MAXIDLE), idle.try_into()?]); - } - } - if !options.params.is_empty() { - args.extend([static_val!(PARAMS), options.params.len().try_into()?]); - for param in options.params.into_iter() { - args.extend([param.name.into(), param.value.into()]); - } - } - if let Some(dialect) = options.dialect { - args.extend([static_val!(DIALECT), dialect.into()]); - } - - Ok(()) -} - -fn gen_search_options(args: &mut Vec, options: FtSearchOptions) -> Result<(), RedisError> { - if options.nocontent { - args.push(static_val!(NOCONTENT)); - } - if options.verbatim { - args.push(static_val!(VERBATIM)); - } - if options.nostopwords { - args.push(static_val!(NOSTOPWORDS)); - } - if options.withscores { - args.push(static_val!(WITHSCORES)); - } - if options.withpayloads { - args.push(static_val!(WITHPAYLOADS)); - } - if options.withsortkeys { - args.push(static_val!(WITHSORTKEYS)); - } - for filter in options.filters.into_iter() { - args.extend([ - static_val!(FILTER), - filter.attribute.into(), - filter.min.into_value()?, - filter.max.into_value()?, - ]); - } - for geo_filter in options.geofilters.into_iter() { - args.extend([ - static_val!(GEOFILTER), - geo_filter.attribute.into(), - geo_filter.position.longitude.try_into()?, - geo_filter.position.latitude.try_into()?, - geo_filter.radius, - geo_filter.units.to_str().into(), - ]); - } - if !options.inkeys.is_empty() { - args.push(static_val!(INKEYS)); - args.push(options.inkeys.len().try_into()?); - args.extend(options.inkeys.into_iter().map(|k| k.into())); - } - if !options.infields.is_empty() { - args.push(static_val!(INFIELDS)); - args.push(options.infields.len().try_into()?); - args.extend(options.infields.into_iter().map(|s| s.into())); - } - if !options.r#return.is_empty() { - args.extend([static_val!(_RETURN), options.r#return.len().try_into()?]); - for field in options.r#return.into_iter() { - args.push(field.identifier.into()); - if let Some(property) = field.property { - args.push(static_val!(AS)); - args.push(property.into()); - } - } - } - if let Some(summarize) = options.summarize { - args.push(static_val!(SUMMARIZE)); - if !summarize.fields.is_empty() { - args.push(static_val!(FIELDS)); - args.push(summarize.fields.len().try_into()?); - args.extend(summarize.fields.into_iter().map(|s| s.into())); - } - if let Some(frags) = summarize.frags { - args.push(static_val!(FRAGS)); - args.push(frags.try_into()?); - } - if let Some(len) = summarize.len { - args.push(static_val!(LEN)); - args.push(len.try_into()?); - } - if let Some(separator) = summarize.separator { - args.push(static_val!(SEPARATOR)); - args.push(separator.into()); - } - } - if let Some(highlight) = options.highlight { - args.push(static_val!(HIGHLIGHT)); - if !highlight.fields.is_empty() { - args.push(static_val!(FIELDS)); - args.push(highlight.fields.len().try_into()?); - args.extend(highlight.fields.into_iter().map(|s| s.into())); - } - if let Some((open, close)) = highlight.tags { - args.extend([static_val!(TAGS), open.into(), close.into()]); - } - } - if let Some(slop) = options.slop { - args.extend([static_val!(SLOP), slop.into()]); - } - if let Some(timeout) = options.timeout { - args.extend([static_val!(TIMEOUT), timeout.into()]); - } - if options.inorder { - args.push(static_val!(INORDER)); - } - if let Some(language) = options.language { - args.extend([static_val!(LANGUAGE), language.into()]); - } - if let Some(expander) = options.expander { - args.extend([static_val!(EXPANDER), expander.into()]); - } - if let Some(scorer) = options.scorer { - args.extend([static_val!(SCORER), scorer.into()]); - } - if options.explainscore { - args.push(static_val!(EXPLAINSCORE)); - } - if let Some(payload) = options.payload { - args.extend([static_val!(PAYLOAD), RedisValue::Bytes(payload)]); - } - if let Some(sort) = options.sortby { - args.push(static_val!(SORTBY)); - args.push(sort.attribute.into()); - if let Some(order) = sort.order { - args.push(order.to_str().into()); - } - if sort.withcount { - args.push(static_val!(WITHCOUNT)); - } - } - if let Some((offset, count)) = options.limit { - args.extend([static_val!(LIMIT), offset.into(), count.into()]); - } - if !options.params.is_empty() { - args.push(static_val!(PARAMS)); - args.push(options.params.len().try_into()?); - for param in options.params.into_iter() { - args.extend([param.name.into(), param.value.into()]); - } - } - if let Some(dialect) = options.dialect { - args.extend([static_val!(DIALECT), dialect.into()]); - } - - Ok(()) -} - -fn gen_schema_kind(args: &mut Vec, kind: SearchSchemaKind) -> Result<(), RedisError> { - match kind { - SearchSchemaKind::Custom { name, arguments } => { - args.push(name.into()); - args.extend(arguments); - }, - SearchSchemaKind::GeoShape { noindex } => { - args.push(static_val!(GEOSHAPE)); - if noindex { - args.push(static_val!(NOINDEX)); - } - }, - SearchSchemaKind::Vector { noindex } => { - args.push(static_val!(VECTOR)); - if noindex { - args.push(static_val!(NOINDEX)); - } - }, - SearchSchemaKind::Geo { sortable, unf, noindex } => { - args.push(static_val!(GEO)); - if sortable { - args.push(static_val!(SORTABLE)); - } - if unf { - args.push(static_val!(UNF)); - } - if noindex { - args.push(static_val!(NOINDEX)); - } - }, - SearchSchemaKind::Numeric { sortable, unf, noindex } => { - args.push(static_val!(NUMERIC)); - if sortable { - args.push(static_val!(SORTABLE)); - } - if unf { - args.push(static_val!(UNF)); - } - if noindex { - args.push(static_val!(NOINDEX)); - } - }, - SearchSchemaKind::Tag { - sortable, - unf, - separator, - casesensitive, - withsuffixtrie, - noindex, - } => { - args.push(static_val!(TAG)); - if sortable { - args.push(static_val!(SORTABLE)); - } - if unf { - args.push(static_val!(UNF)); - } - if let Some(separator) = separator { - args.extend([static_val!(SEPARATOR), separator.to_string().into()]); - } - if casesensitive { - args.push(static_val!(CASESENSITIVE)); - } - if withsuffixtrie { - args.push(static_val!(WITHSUFFIXTRIE)); - } - if noindex { - args.push(static_val!(NOINDEX)); - } - }, - SearchSchemaKind::Text { - sortable, - unf, - nostem, - phonetic, - weight, - withsuffixtrie, - noindex, - } => { - args.push(static_val!(TEXT)); - if sortable { - args.push(static_val!(SORTABLE)); - } - if unf { - args.push(static_val!(UNF)); - } - if nostem { - args.push(static_val!(NOSTEM)); - } - if let Some(matcher) = phonetic { - args.extend([static_val!(PHONETIC), matcher.into()]); - } - if let Some(weight) = weight { - args.extend([static_val!(WEIGHT), weight.into()]); - } - if withsuffixtrie { - args.push(static_val!(WITHSUFFIXTRIE)); - } - if noindex { - args.push(static_val!(NOINDEX)); - } - }, - }; - - Ok(()) -} - -fn gen_alter_options(args: &mut Vec, options: FtAlterOptions) -> Result<(), RedisError> { - if options.skipinitialscan { - args.push(static_val!(SKIPINITIALSCAN)); - } - args.extend([static_val!(SCHEMA), static_val!(ADD), options.attribute.into()]); - gen_schema_kind(args, options.options)?; - - Ok(()) -} - -fn gen_create_options(args: &mut Vec, options: FtCreateOptions) -> Result<(), RedisError> { - if let Some(kind) = options.on { - args.extend([static_val!(ON), kind.to_str().into()]); - } - if !options.prefixes.is_empty() { - args.extend([static_val!(PREFIX), options.prefixes.len().try_into()?]); - args.extend(options.prefixes.into_iter().map(|s| s.into())); - } - if let Some(filter) = options.filter { - args.extend([static_val!(FILTER), filter.into()]); - } - if let Some(language) = options.language { - args.extend([static_val!(LANGUAGE), language.into()]); - } - if let Some(language_field) = options.language_field { - args.extend([static_val!(LANGUAGE_FIELD), language_field.into()]); - } - if let Some(score) = options.score { - args.extend([static_val!(SCORE), score.try_into()?]); - } - if let Some(score_field) = options.score_field { - args.extend([static_val!(SCORE_FIELD), score_field.try_into()?]); - } - if let Some(payload_field) = options.payload_field { - args.extend([static_val!(PAYLOAD_FIELD), payload_field.into()]); - } - if options.maxtextfields { - args.push(static_val!(MAXTEXTFIELDS)); - } - if let Some(temporary) = options.temporary { - args.extend([static_val!(TEMPORARY), temporary.try_into()?]); - } - if options.nooffsets { - args.push(static_val!(NOOFFSETS)); - } - if options.nohl { - args.push(static_val!(NOHL)); - } - if options.nofields { - args.push(static_val!(NOFIELDS)); - } - if options.nofreqs { - args.push(static_val!(NOFREQS)); - } - if !options.stopwords.is_empty() { - args.extend([static_val!(STOPWORDS), options.stopwords.len().try_into()?]); - args.extend(options.stopwords.into_iter().map(|s| s.into())); - } - if options.skipinitialscan { - args.push(static_val!(SKIPINITIALSCAN)); - } - - Ok(()) -} - -// does not include the prefix SCHEMA -fn gen_schema_args(args: &mut Vec, options: SearchSchema) -> Result<(), RedisError> { - args.push(options.field_name.into()); - if let Some(alias) = options.alias { - args.extend([static_val!(AS), alias.into()]); - } - gen_schema_kind(args, options.kind)?; - - Ok(()) -} - -pub async fn ft_list(client: &C) -> Result { - args_values_cmd(client, RedisCommandKind::FtList, vec![]).await -} - -pub async fn ft_aggregate( - client: &C, - index: Str, - query: Str, - options: FtAggregateOptions, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2 + options.num_args()); - args.push(index.into()); - args.push(query.into()); - gen_aggregate_options(&mut args, options)?; - - Ok((RedisCommandKind::FtAggregate, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ft_search( - client: &C, - index: Str, - query: Str, - options: FtSearchOptions, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2 + options.num_args()); - args.extend([index.into(), query.into()]); - gen_search_options(&mut args, options)?; - - Ok((RedisCommandKind::FtSearch, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ft_create( - client: &C, - index: Str, - options: FtCreateOptions, - schema: Vec, -) -> Result { - let frame = utils::request_response(client, move || { - let schema_num_args = schema.iter().fold(0, |m, s| m + s.num_args()); - let mut args = Vec::with_capacity(2 + options.num_args() + schema_num_args); - args.push(index.into()); - gen_create_options(&mut args, options)?; - - args.push(static_val!(SCHEMA)); - for schema in schema.into_iter() { - gen_schema_args(&mut args, schema)?; - } - - Ok((RedisCommandKind::FtCreate, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ft_alter( - client: &C, - index: Str, - options: FtAlterOptions, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1 + options.num_args()); - args.push(index.into()); - gen_alter_options(&mut args, options)?; - - Ok((RedisCommandKind::FtAlter, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ft_aliasadd(client: &C, alias: Str, index: Str) -> Result { - args_values_cmd(client, RedisCommandKind::FtAliasAdd, vec![alias.into(), index.into()]).await -} - -pub async fn ft_aliasdel(client: &C, alias: Str) -> Result { - args_values_cmd(client, RedisCommandKind::FtAliasDel, vec![alias.into()]).await -} - -pub async fn ft_aliasupdate(client: &C, alias: Str, index: Str) -> Result { - args_values_cmd(client, RedisCommandKind::FtAliasUpdate, vec![ - alias.into(), - index.into(), - ]) - .await -} - -pub async fn ft_config_get(client: &C, option: Str) -> Result { - args_values_cmd(client, RedisCommandKind::FtConfigGet, vec![option.into()]).await -} - -pub async fn ft_config_set( - client: &C, - option: Str, - value: RedisValue, -) -> Result { - args_values_cmd(client, RedisCommandKind::FtConfigSet, vec![option.into(), value]).await -} - -pub async fn ft_cursor_del( - client: &C, - index: Str, - cursor: RedisValue, -) -> Result { - args_values_cmd(client, RedisCommandKind::FtCursorDel, vec![index.into(), cursor]).await -} - -pub async fn ft_cursor_read( - client: &C, - index: Str, - cursor: RedisValue, - count: Option, -) -> Result { - let args = if let Some(count) = count { - vec![index.into(), cursor, static_val!(COUNT), count.try_into()?] - } else { - vec![index.into(), cursor] - }; - - args_values_cmd(client, RedisCommandKind::FtCursorRead, args).await -} - -pub async fn ft_dictadd( - client: &C, - dict: Str, - terms: MultipleStrings, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(terms.len() + 1); - args.push(dict.into()); - for term in terms.inner().into_iter() { - args.push(term.into()); - } - - Ok((RedisCommandKind::FtDictAdd, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ft_dictdel( - client: &C, - dict: Str, - terms: MultipleStrings, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(terms.len() + 1); - args.push(dict.into()); - for term in terms.inner().into_iter() { - args.push(term.into()); - } - - Ok((RedisCommandKind::FtDictDel, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ft_dictdump(client: &C, dict: Str) -> Result { - one_arg_values_cmd(client, RedisCommandKind::FtDictDump, dict.into()).await -} - -pub async fn ft_dropindex(client: &C, index: Str, dd: bool) -> Result { - let args = if dd { - vec![index.into(), static_val!(DD)] - } else { - vec![index.into()] - }; - - args_values_cmd(client, RedisCommandKind::FtDropIndex, args).await -} - -pub async fn ft_explain( - client: &C, - index: Str, - query: Str, - dialect: Option, -) -> Result { - let args = if let Some(dialect) = dialect { - vec![index.into(), query.into(), static_val!(DIALECT), dialect.into()] - } else { - vec![index.into(), query.into()] - }; - - args_values_cmd(client, RedisCommandKind::FtExplain, args).await -} - -pub async fn ft_info(client: &C, index: Str) -> Result { - one_arg_values_cmd(client, RedisCommandKind::FtInfo, index.into()).await -} - -pub async fn ft_spellcheck( - client: &C, - index: Str, - query: Str, - distance: Option, - terms: Option, - dialect: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let terms_len = terms.as_ref().map(|t| t.num_args()).unwrap_or(0); - let mut args = Vec::with_capacity(9 + terms_len); - args.push(index.into()); - args.push(query.into()); - - if let Some(distance) = distance { - args.push(static_val!(DISTANCE)); - args.push(distance.into()); - } - if let Some(terms) = terms { - args.push(static_val!(TERMS)); - let (dictionary, terms) = match terms { - SpellcheckTerms::Include { dictionary, terms } => { - args.push(static_val!(INCLUDE)); - (dictionary, terms) - }, - SpellcheckTerms::Exclude { dictionary, terms } => { - args.push(static_val!(EXCLUDE)); - (dictionary, terms) - }, - }; - - args.push(dictionary.into()); - for term in terms.into_iter() { - args.push(term.into()); - } - } - if let Some(dialect) = dialect { - args.extend([static_val!(DIALECT), dialect.into()]); - } - - Ok((RedisCommandKind::FtSpellCheck, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ft_sugadd( - client: &C, - key: RedisKey, - string: Str, - score: f64, - incr: bool, - payload: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(6); - args.extend([key.into(), string.into(), score.try_into()?]); - - if incr { - args.push(static_val!(INCR)); - } - if let Some(payload) = payload { - args.extend([static_val!(PAYLOAD), RedisValue::Bytes(payload)]); - } - - Ok((RedisCommandKind::FtSugAdd, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ft_sugdel(client: &C, key: RedisKey, string: Str) -> Result { - args_values_cmd(client, RedisCommandKind::FtSugDel, vec![key.into(), string.into()]).await -} - -pub async fn ft_sugget( - client: &C, - key: RedisKey, - prefix: Str, - fuzzy: bool, - withscores: bool, - withpayloads: bool, - max: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(7); - args.push(key.into()); - args.push(prefix.into()); - if fuzzy { - args.push(static_val!(FUZZY)); - } - if withscores { - args.push(static_val!(WITHSCORES)); - } - if withpayloads { - args.push(static_val!(WITHPAYLOADS)); - } - if let Some(max) = max { - args.extend([static_val!(MAX), max.try_into()?]); - } - - Ok((RedisCommandKind::FtSugGet, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ft_suglen(client: &C, key: RedisKey) -> Result { - one_arg_values_cmd(client, RedisCommandKind::FtSugLen, key.into()).await -} - -pub async fn ft_syndump(client: &C, index: Str) -> Result { - one_arg_values_cmd(client, RedisCommandKind::FtSynDump, index.into()).await -} - -pub async fn ft_synupdate( - client: &C, - index: Str, - synonym_group_id: Str, - skipinitialscan: bool, - terms: MultipleStrings, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(3 + terms.len()); - args.push(index.into()); - args.push(synonym_group_id.into()); - if skipinitialscan { - args.push(static_val!(SKIPINITIALSCAN)); - } - for term in terms.inner().into_iter() { - args.push(term.into()); - } - - Ok((RedisCommandKind::FtSynUpdate, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ft_tagvals(client: &C, index: Str, field_name: Str) -> Result { - args_values_cmd(client, RedisCommandKind::FtTagVals, vec![ - index.into(), - field_name.into(), - ]) - .await -} diff --git a/src/commands/impls/scan.rs b/src/commands/impls/scan.rs deleted file mode 100644 index 9a31050e..00000000 --- a/src/commands/impls/scan.rs +++ /dev/null @@ -1,230 +0,0 @@ -use super::*; -use crate::{ - error::*, - interfaces, - modules::inner::RedisClientInner, - protocol::{ - command::{RedisCommand, RedisCommandKind}, - responders::ResponseKind, - types::*, - }, - runtime::{rx_stream, unbounded_channel, RefCount}, - types::*, - utils, -}; -use bytes_utils::Str; -use futures::stream::{Stream, TryStreamExt}; - -#[cfg(feature = "glommio")] -use crate::runtime::UnboundedSender; - -static STARTING_CURSOR: &str = "0"; - -fn values_args(key: RedisKey, pattern: Str, count: Option) -> Vec { - let mut args = Vec::with_capacity(6); - args.push(key.into()); - args.push(static_val!(STARTING_CURSOR)); - args.push(static_val!(MATCH)); - args.push(pattern.into()); - - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.into()); - } - - args -} - -pub fn scan_cluster( - inner: &RefCount, - pattern: Str, - count: Option, - r#type: Option, -) -> impl Stream> { - let (tx, rx) = unbounded_channel(); - #[cfg(feature = "glommio")] - let tx: UnboundedSender<_> = tx.into(); - - let hash_slots = inner.with_cluster_state(|state| Ok(state.unique_hash_slots())); - let hash_slots = match hash_slots { - Ok(slots) => slots, - Err(e) => { - let _ = tx.send(Err(e)); - return rx_stream(rx); - }, - }; - - let mut args = Vec::with_capacity(7); - args.push(static_val!(STARTING_CURSOR)); - args.push(static_val!(MATCH)); - args.push(pattern.into()); - - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.into()); - } - if let Some(r#type) = r#type { - args.push(static_val!(TYPE)); - args.push(r#type.to_str().into()); - } - - for slot in hash_slots.into_iter() { - _trace!(inner, "Scan cluster hash slot server: {}", slot); - let response = ResponseKind::KeyScan(KeyScanInner { - hash_slot: Some(slot), - args: args.clone(), - cursor_idx: 0, - tx: tx.clone(), - server: None, - }); - let command: RedisCommand = (RedisCommandKind::Scan, Vec::new(), response).into(); - - if let Err(e) = interfaces::default_send_command(inner, command) { - let _ = tx.send(Err(e)); - break; - } - } - - rx_stream(rx) -} - -pub fn scan( - inner: &RefCount, - pattern: Str, - count: Option, - r#type: Option, - server: Option, -) -> impl Stream> { - let (tx, rx) = unbounded_channel(); - #[cfg(feature = "glommio")] - let tx: UnboundedSender<_> = tx.into(); - - let hash_slot = if inner.config.server.is_clustered() { - if utils::clustered_scan_pattern_has_hash_tag(inner, &pattern) { - Some(redis_protocol::redis_keyslot(pattern.as_bytes())) - } else { - None - } - } else { - None - }; - - let mut args = Vec::with_capacity(7); - args.push(static_val!(STARTING_CURSOR)); - args.push(static_val!(MATCH)); - args.push(pattern.into()); - - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.into()); - } - if let Some(r#type) = r#type { - args.push(static_val!(TYPE)); - args.push(r#type.to_str().into()); - } - - let response = ResponseKind::KeyScan(KeyScanInner { - hash_slot, - args, - server, - cursor_idx: 0, - tx: tx.clone(), - }); - let command: RedisCommand = (RedisCommandKind::Scan, Vec::new(), response).into(); - - if let Err(e) = interfaces::default_send_command(inner, command) { - let _ = tx.send(Err(e)); - } - - rx_stream(rx) -} - -pub fn hscan( - inner: &RefCount, - key: RedisKey, - pattern: Str, - count: Option, -) -> impl Stream> { - let (tx, rx) = unbounded_channel(); - let args = values_args(key, pattern, count); - - #[cfg(feature = "glommio")] - let tx: UnboundedSender<_> = tx.into(); - let response = ResponseKind::ValueScan(ValueScanInner { - tx: tx.clone(), - cursor_idx: 1, - args, - }); - let command: RedisCommand = (RedisCommandKind::Hscan, Vec::new(), response).into(); - - if let Err(e) = interfaces::default_send_command(inner, command) { - let _ = tx.send(Err(e)); - } - - rx_stream(rx).try_filter_map(|result| async move { - match result { - ValueScanResult::HScan(res) => Ok(Some(res)), - _ => Err(RedisError::new(RedisErrorKind::Protocol, "Expected HSCAN result.")), - } - }) -} - -pub fn sscan( - inner: &RefCount, - key: RedisKey, - pattern: Str, - count: Option, -) -> impl Stream> { - let (tx, rx) = unbounded_channel(); - let args = values_args(key, pattern, count); - - #[cfg(feature = "glommio")] - let tx: UnboundedSender<_> = tx.into(); - let response = ResponseKind::ValueScan(ValueScanInner { - tx: tx.clone(), - cursor_idx: 1, - args, - }); - let command: RedisCommand = (RedisCommandKind::Sscan, Vec::new(), response).into(); - - if let Err(e) = interfaces::default_send_command(inner, command) { - let _ = tx.send(Err(e)); - } - - rx_stream(rx).try_filter_map(|result| async move { - match result { - ValueScanResult::SScan(res) => Ok(Some(res)), - _ => Err(RedisError::new(RedisErrorKind::Protocol, "Expected SSCAN result.")), - } - }) -} - -pub fn zscan( - inner: &RefCount, - key: RedisKey, - pattern: Str, - count: Option, -) -> impl Stream> { - let (tx, rx) = unbounded_channel(); - let args = values_args(key, pattern, count); - - #[cfg(feature = "glommio")] - let tx: UnboundedSender<_> = tx.into(); - let response = ResponseKind::ValueScan(ValueScanInner { - tx: tx.clone(), - cursor_idx: 1, - args, - }); - let command: RedisCommand = (RedisCommandKind::Zscan, Vec::new(), response).into(); - - if let Err(e) = interfaces::default_send_command(inner, command) { - let _ = tx.send(Err(e)); - } - - rx_stream(rx).try_filter_map(|result| async move { - match result { - ValueScanResult::ZScan(res) => Ok(Some(res)), - _ => Err(RedisError::new(RedisErrorKind::Protocol, "Expected ZSCAN result.")), - } - }) -} diff --git a/src/commands/impls/sentinel.rs b/src/commands/impls/sentinel.rs deleted file mode 100644 index 2b08e951..00000000 --- a/src/commands/impls/sentinel.rs +++ /dev/null @@ -1,200 +0,0 @@ -use super::*; -use crate::{ - error::RedisError, - protocol::{command::RedisCommandKind, utils as protocol_utils}, - router::sentinel::{ - CKQUORUM, - CONFIG, - FAILOVER, - FLUSHCONFIG, - GET_MASTER_ADDR_BY_NAME, - INFO_CACHE, - MASTER, - MASTERS, - MONITOR, - MYID, - PENDING_SCRIPTS, - REMOVE, - REPLICAS, - SENTINELS, - SET, - SIMULATE_FAILURE, - }, - types::*, - utils, -}; -use bytes_utils::Str; -use std::net::IpAddr; - -pub async fn config_get(client: &C, name: Str) -> Result { - let frame = utils::request_response(client, move || { - let args = vec![static_val!(CONFIG), static_val!(GET), name.into()]; - Ok((RedisCommandKind::Sentinel, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn config_set(client: &C, name: Str, value: RedisValue) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Sentinel, vec![ - static_val!(CONFIG), - static_val!(SET), - name.into(), - value, - ])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ckquorum(client: &C, name: Str) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Sentinel, vec![static_val!(CKQUORUM), name.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn flushconfig(client: &C) -> Result { - args_values_cmd(client, RedisCommandKind::Sentinel, vec![static_val!(FLUSHCONFIG)]).await -} - -pub async fn failover(client: &C, name: Str) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Sentinel, vec![static_val!(FAILOVER), name.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn get_master_addr_by_name(client: &C, name: Str) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Sentinel, vec![ - static_val!(GET_MASTER_ADDR_BY_NAME), - name.into(), - ])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn info_cache(client: &C) -> Result { - args_values_cmd(client, RedisCommandKind::Sentinel, vec![static_val!(INFO_CACHE)]).await -} - -pub async fn masters(client: &C) -> Result { - args_values_cmd(client, RedisCommandKind::Sentinel, vec![static_val!(MASTERS)]).await -} - -pub async fn master(client: &C, name: Str) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Sentinel, vec![static_val!(MASTER), name.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn monitor( - client: &C, - name: Str, - ip: IpAddr, - port: u16, - quorum: u32, -) -> Result { - let ip = ip.to_string(); - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Sentinel, vec![ - static_val!(MONITOR), - name.into(), - ip.into(), - port.into(), - quorum.into(), - ])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn myid(client: &C) -> Result { - args_values_cmd(client, RedisCommandKind::Sentinel, vec![static_val!(MYID)]).await -} - -pub async fn pending_scripts(client: &C) -> Result { - args_values_cmd(client, RedisCommandKind::Sentinel, vec![static_val!(PENDING_SCRIPTS)]).await -} - -pub async fn remove(client: &C, name: Str) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Sentinel, vec![static_val!(REMOVE), name.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn replicas(client: &C, name: Str) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Sentinel, vec![static_val!(REPLICAS), name.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn sentinels(client: &C, name: Str) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Sentinel, vec![static_val!(SENTINELS), name.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn set(client: &C, name: Str, options: RedisMap) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2 + options.len()); - args.push(static_val!(SET)); - args.push(name.into()); - - for (key, value) in options.inner().into_iter() { - args.push(key.into()); - args.push(value); - } - Ok((RedisCommandKind::Sentinel, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn simulate_failure( - client: &C, - kind: SentinelFailureKind, -) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Sentinel, vec![ - static_val!(SIMULATE_FAILURE), - kind.to_str().into(), - ])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn reset(client: &C, pattern: Str) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Sentinel, vec![static_val!(RESET), pattern.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/server.rs b/src/commands/impls/server.rs deleted file mode 100644 index d9ffb0d4..00000000 --- a/src/commands/impls/server.rs +++ /dev/null @@ -1,294 +0,0 @@ -use super::*; -use crate::{ - clients::RedisClient, - error::*, - interfaces, - modules::inner::RedisClientInner, - prelude::Resp3Frame, - protocol::{ - command::{RedisCommand, RedisCommandKind, RouterCommand}, - responders::ResponseKind, - utils as protocol_utils, - }, - runtime::{oneshot_channel, RefCount}, - types::*, - utils, -}; -use bytes_utils::Str; - -pub async fn active_connections(client: &C) -> Result, RedisError> { - let (tx, rx) = oneshot_channel(); - let command = RouterCommand::Connections { tx }; - interfaces::send_to_router(client.inner(), command)?; - - rx.await.map_err(|e| e.into()) -} - -pub async fn quit(client: &C) -> Result<(), RedisError> { - let inner = client.inner().clone(); - _debug!(inner, "Closing Redis connection with Quit command."); - - let (tx, rx) = oneshot_channel(); - let mut command: RedisCommand = if inner.config.server.is_clustered() { - let response = ResponseKind::new_buffer(tx); - (RedisCommandKind::Quit, vec![], response).into() - } else { - let response = ResponseKind::Respond(Some(tx)); - (RedisCommandKind::Quit, vec![], response).into() - }; - utils::set_client_state(&inner.state, ClientState::Disconnecting); - inner.notifications.broadcast_close(); - - let timeout_dur = utils::prepare_command(client, &mut command); - client.send_command(command)?; - let _ = utils::timeout(rx, timeout_dur).await??; - inner - .notifications - .close_public_receivers(inner.with_perf_config(|c| c.broadcast_channel_capacity)); - inner.backchannel.write().await.check_and_disconnect(&inner, None).await; - - Ok(()) -} - -pub async fn shutdown(client: &C, flags: Option) -> Result<(), RedisError> { - let inner = client.inner().clone(); - _debug!(inner, "Shutting down server."); - - let args = if let Some(flags) = flags { - vec![flags.to_str().into()] - } else { - Vec::new() - }; - let (tx, rx) = oneshot_channel(); - let mut command: RedisCommand = if inner.config.server.is_clustered() { - let response = ResponseKind::new_buffer(tx); - (RedisCommandKind::Shutdown, args, response).into() - } else { - let response = ResponseKind::Respond(Some(tx)); - (RedisCommandKind::Shutdown, args, response).into() - }; - utils::set_client_state(&inner.state, ClientState::Disconnecting); - inner.notifications.broadcast_close(); - - let timeout_dur = utils::prepare_command(client, &mut command); - client.send_command(command)?; - let _ = utils::timeout(rx, timeout_dur).await??; - inner - .notifications - .close_public_receivers(inner.with_perf_config(|c| c.broadcast_channel_capacity)); - inner.backchannel.write().await.check_and_disconnect(&inner, None).await; - - Ok(()) -} - -/// Create a new client struct for each unique primary cluster node based on the cached cluster state. -pub fn split(inner: &RefCount) -> Result, RedisError> { - if !inner.config.server.is_clustered() { - return Err(RedisError::new( - RedisErrorKind::Config, - "Expected clustered redis deployment.", - )); - } - let servers = inner.with_cluster_state(|state| Ok(state.unique_primary_nodes()))?; - _debug!(inner, "Unique primary nodes in split: {:?}", servers); - - Ok( - servers - .into_iter() - .map(|server| { - let mut config = inner.config.as_ref().clone(); - config.server = ServerConfig::Centralized { server }; - let perf = inner.performance_config(); - let policy = inner.reconnect_policy(); - let connection = inner.connection_config(); - - RedisClient::new(config, Some(perf), Some(connection), policy) - }) - .collect(), - ) -} - -pub async fn force_reconnection(inner: &RefCount) -> Result<(), RedisError> { - let (tx, rx) = oneshot_channel(); - let command = RouterCommand::Reconnect { - server: None, - force: true, - tx: Some(tx), - #[cfg(feature = "replicas")] - replica: false, - }; - interfaces::send_to_router(inner, command)?; - - rx.await?.map(|_| ()) -} - -pub async fn flushall(client: &C, r#async: bool) -> Result { - let args = if r#async { vec![static_val!(ASYNC)] } else { Vec::new() }; - let frame = utils::request_response(client, move || Ok((RedisCommandKind::FlushAll, args))).await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn flushall_cluster(client: &C) -> Result<(), RedisError> { - if !client.inner().config.server.is_clustered() { - return flushall(client, false).await.map(|_| ()); - } - - let (tx, rx) = oneshot_channel(); - let response = ResponseKind::new_buffer(tx); - let mut command: RedisCommand = (RedisCommandKind::_FlushAllCluster, vec![], response).into(); - let timeout_dur = utils::prepare_command(client, &mut command); - client.send_command(command)?; - - let _ = utils::timeout(rx, timeout_dur).await??; - Ok(()) -} - -pub async fn ping(client: &C) -> Result { - let frame = utils::request_response(client, || Ok((RedisCommandKind::Ping, vec![]))).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn select(client: &C, db: u8) -> Result { - let frame = utils::request_response(client, || Ok((RedisCommandKind::Select, vec![db.into()]))).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn info(client: &C, section: Option) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1); - if let Some(section) = section { - args.push(section.to_str().into()); - } - - Ok((RedisCommandKind::Info, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn hello( - client: &C, - version: RespVersion, - auth: Option<(Str, Str)>, - setname: Option, -) -> Result<(), RedisError> { - let mut args = if let Some((username, password)) = auth { - vec![username.into(), password.into()] - } else { - vec![] - }; - if let Some(name) = setname { - args.push(name.into()); - } - - if client.inner().config.server.is_clustered() { - let (tx, rx) = oneshot_channel(); - let mut command: RedisCommand = RedisCommandKind::_HelloAllCluster(version).into(); - command.response = ResponseKind::new_buffer(tx); - - let timeout_dur = utils::prepare_command(client, &mut command); - client.send_command(command)?; - let _ = utils::timeout(rx, timeout_dur).await??; - Ok(()) - } else { - let frame = utils::request_response(client, move || Ok((RedisCommandKind::_Hello(version), args))).await?; - let _ = protocol_utils::frame_to_results(frame)?; - Ok(()) - } -} - -pub async fn auth(client: &C, username: Option, password: Str) -> Result<(), RedisError> { - let mut args = Vec::with_capacity(2); - if let Some(username) = username { - args.push(username.into()); - } - args.push(password.into()); - - if client.inner().config.server.is_clustered() { - let (tx, rx) = oneshot_channel(); - let response = ResponseKind::new_buffer(tx); - let mut command: RedisCommand = (RedisCommandKind::_AuthAllCluster, args, response).into(); - - let timeout_dur = utils::prepare_command(client, &mut command); - client.send_command(command)?; - let _ = utils::timeout(rx, timeout_dur).await??; - Ok(()) - } else { - let frame = utils::request_response(client, move || Ok((RedisCommandKind::Auth, args))).await?; - - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) - } -} - -pub async fn custom( - client: &C, - cmd: CustomCommand, - args: Vec, -) -> Result { - args_values_cmd(client, RedisCommandKind::_Custom(cmd), args).await -} - -pub async fn custom_raw( - client: &C, - cmd: CustomCommand, - args: Vec, -) -> Result { - utils::request_response(client, move || Ok((RedisCommandKind::_Custom(cmd), args))).await -} - -#[cfg(feature = "i-server")] -value_cmd!(dbsize, DBSize); -#[cfg(feature = "i-server")] -value_cmd!(bgrewriteaof, BgreWriteAof); -#[cfg(feature = "i-server")] -value_cmd!(bgsave, BgSave); - -#[cfg(feature = "i-server")] -pub async fn failover( - client: &C, - to: Option<(String, u16)>, - force: bool, - abort: bool, - timeout: Option, -) -> Result<(), RedisError> { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(7); - if let Some((host, port)) = to { - args.push(static_val!(TO)); - args.push(host.into()); - args.push(port.into()); - } - if force { - args.push(static_val!(FORCE)); - } - if abort { - args.push(static_val!(ABORT)); - } - if let Some(timeout) = timeout { - args.push(static_val!(TIMEOUT)); - args.push(timeout.into()); - } - - Ok((RedisCommandKind::Failover, args)) - }) - .await?; - - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response) -} - -#[cfg(feature = "i-server")] -value_cmd!(lastsave, LastSave); - -#[cfg(feature = "i-server")] -pub async fn wait(client: &C, numreplicas: i64, timeout: i64) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::Wait, vec![numreplicas.into(), timeout.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/sets.rs b/src/commands/impls/sets.rs deleted file mode 100644 index 5ae47e82..00000000 --- a/src/commands/impls/sets.rs +++ /dev/null @@ -1,226 +0,0 @@ -use super::*; -use crate::{ - protocol::{command::RedisCommandKind, utils as protocol_utils}, - types::*, - utils, -}; -use std::convert::TryInto; - -pub async fn sadd( - client: &C, - key: RedisKey, - members: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let members = members.into_multiple_values(); - let mut args = Vec::with_capacity(1 + members.len()); - args.push(key.into()); - - for member in members.into_iter() { - args.push(member); - } - Ok((RedisCommandKind::Sadd, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn scard(client: &C, key: RedisKey) -> Result { - one_arg_value_cmd(client, RedisCommandKind::Scard, key.into()).await -} - -pub async fn sdiff(client: &C, keys: MultipleKeys) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(keys.len()); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - Ok((RedisCommandKind::Sdiff, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn sdiffstore( - client: &C, - dest: RedisKey, - keys: MultipleKeys, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1 + keys.len()); - args.push(dest.into()); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - Ok((RedisCommandKind::Sdiffstore, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn sinter(client: &C, keys: MultipleKeys) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(keys.len()); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - Ok((RedisCommandKind::Sinter, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn sinterstore( - client: &C, - dest: RedisKey, - keys: MultipleKeys, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1 + keys.len()); - args.push(dest.into()); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - Ok((RedisCommandKind::Sinterstore, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn sismember( - client: &C, - key: RedisKey, - member: RedisValue, -) -> Result { - args_value_cmd(client, RedisCommandKind::Sismember, vec![key.into(), member]).await -} - -pub async fn smismember( - client: &C, - key: RedisKey, - members: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let members = members.into_multiple_values(); - let mut args = Vec::with_capacity(1 + members.len()); - args.push(key.into()); - - for member in members.into_iter() { - args.push(member); - } - Ok((RedisCommandKind::Smismember, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn smembers(client: &C, key: RedisKey) -> Result { - one_arg_values_cmd(client, RedisCommandKind::Smembers, key.into()).await -} - -pub async fn smove( - client: &C, - source: RedisKey, - dest: RedisKey, - member: RedisValue, -) -> Result { - let args = vec![source.into(), dest.into(), member]; - args_value_cmd(client, RedisCommandKind::Smove, args).await -} - -pub async fn spop(client: &C, key: RedisKey, count: Option) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2); - args.push(key.into()); - - if let Some(count) = count { - args.push(count.try_into()?); - } - Ok((RedisCommandKind::Spop, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn srandmember( - client: &C, - key: RedisKey, - count: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2); - args.push(key.into()); - - if let Some(count) = count { - args.push(count.try_into()?); - } - Ok((RedisCommandKind::Srandmember, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn srem( - client: &C, - key: RedisKey, - members: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let members = members.into_multiple_values(); - let mut args = Vec::with_capacity(1 + members.len()); - args.push(key.into()); - - for member in members.into_iter() { - args.push(member); - } - Ok((RedisCommandKind::Srem, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn sunion(client: &C, keys: MultipleKeys) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(keys.len()); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - Ok((RedisCommandKind::Sunion, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn sunionstore( - client: &C, - dest: RedisKey, - keys: MultipleKeys, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1 + keys.len()); - args.push(dest.into()); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - Ok((RedisCommandKind::Sunionstore, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/slowlog.rs b/src/commands/impls/slowlog.rs deleted file mode 100644 index 06275636..00000000 --- a/src/commands/impls/slowlog.rs +++ /dev/null @@ -1,30 +0,0 @@ -use super::*; -use crate::{ - protocol::{command::RedisCommandKind, utils as protocol_utils}, - utils, -}; - -pub async fn slowlog_get(client: &C, count: Option) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2); - args.push(static_val!(GET)); - - if let Some(count) = count { - args.push(count.into()); - } - - Ok((RedisCommandKind::Slowlog, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn slowlog_length(client: &C) -> Result { - let frame = utils::request_response(client, || Ok((RedisCommandKind::Slowlog, vec![LEN.into()]))).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn slowlog_reset(client: &C) -> Result<(), RedisError> { - args_ok_cmd(client, RedisCommandKind::Slowlog, vec![static_val!(RESET)]).await -} diff --git a/src/commands/impls/sorted_sets.rs b/src/commands/impls/sorted_sets.rs deleted file mode 100644 index 8486586e..00000000 --- a/src/commands/impls/sorted_sets.rs +++ /dev/null @@ -1,772 +0,0 @@ -use super::*; -use crate::{ - error::*, - protocol::{command::RedisCommandKind, utils as protocol_utils}, - types::*, - utils, -}; -use std::convert::TryInto; - -static INCR: &str = "INCR"; -static WITH_SCORES: &str = "WITHSCORES"; -static AGGREGATE: &str = "AGGREGATE"; -static WEIGHTS: &str = "WEIGHTS"; - -fn new_range_error(kind: &Option) -> Result<(), RedisError> { - if let Some(ref sort) = *kind { - Err(RedisError::new( - RedisErrorKind::InvalidArgument, - format!("Invalid range bound with {} sort", sort.to_str()), - )) - } else { - Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Invalid index range bound.", - )) - } -} - -fn check_range_type(range: &ZRange, kind: &Option) -> Result<(), RedisError> { - match kind { - Some(_kind) => match _kind { - ZSort::ByLex => match range.range { - ZRangeBound::Lex(_) | ZRangeBound::InfiniteLex | ZRangeBound::NegInfinityLex => Ok(()), - _ => new_range_error(kind), - }, - ZSort::ByScore => match range.range { - ZRangeBound::Score(_) | ZRangeBound::InfiniteScore | ZRangeBound::NegInfiniteScore => Ok(()), - _ => new_range_error(kind), - }, - }, - None => match range.range { - ZRangeBound::Index(_) => Ok(()), - _ => new_range_error(kind), - }, - } -} - -fn check_range_types(min: &ZRange, max: &ZRange, kind: &Option) -> Result<(), RedisError> { - check_range_type(min, kind)?; - check_range_type(max, kind)?; - Ok(()) -} - -pub async fn bzmpop( - client: &C, - timeout: f64, - keys: MultipleKeys, - sort: ZCmp, - count: Option, -) -> Result { - let timeout: RedisValue = timeout.try_into()?; - - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(keys.len() + 4); - args.push(timeout); - args.push(keys.len().try_into()?); - for key in keys.inner().into_iter() { - args.push(key.into()); - } - args.push(sort.to_str().into()); - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.into()); - } - - Ok((RedisCommandKind::BzmPop, args)) - }) - .await?; - - protocol_utils::check_null_timeout(&frame)?; - protocol_utils::frame_to_results(frame) -} - -pub async fn bzpopmin(client: &C, keys: MultipleKeys, timeout: f64) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1 + keys.len()); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - args.push(timeout.try_into()?); - - Ok((RedisCommandKind::BzPopMin, args)) - }) - .await?; - - protocol_utils::check_null_timeout(&frame)?; - protocol_utils::frame_to_results(frame) -} - -pub async fn bzpopmax(client: &C, keys: MultipleKeys, timeout: f64) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1 + keys.len()); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - args.push(timeout.try_into()?); - - Ok((RedisCommandKind::BzPopMax, args)) - }) - .await?; - - protocol_utils::check_null_timeout(&frame)?; - protocol_utils::frame_to_results(frame) -} - -pub async fn zadd( - client: &C, - key: RedisKey, - options: Option, - ordering: Option, - changed: bool, - incr: bool, - values: MultipleZaddValues, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(5 + (values.len() * 2)); - args.push(key.into()); - - if let Some(options) = options { - args.push(options.to_str().into()); - } - if let Some(ordering) = ordering { - args.push(ordering.to_str().into()); - } - if changed { - args.push(static_val!(CHANGED)); - } - if incr { - args.push(static_val!(INCR)); - } - - for (score, value) in values.inner().into_iter() { - args.push(score.try_into()?); - args.push(value); - } - - Ok((RedisCommandKind::Zadd, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zcard(client: &C, key: RedisKey) -> Result { - one_arg_value_cmd(client, RedisCommandKind::Zcard, key.into()).await -} - -pub async fn zcount(client: &C, key: RedisKey, min: f64, max: f64) -> Result { - let (min, max) = (min.try_into()?, max.try_into()?); - args_value_cmd(client, RedisCommandKind::Zcount, vec![key.into(), min, max]).await -} - -pub async fn zdiff( - client: &C, - keys: MultipleKeys, - withscores: bool, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2 + keys.len()); - args.push(keys.len().try_into()?); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - if withscores { - args.push(static_val!(WITH_SCORES)); - } - - Ok((RedisCommandKind::Zdiff, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zdiffstore( - client: &C, - dest: RedisKey, - keys: MultipleKeys, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2 + keys.len()); - args.push(dest.into()); - args.push(keys.len().try_into()?); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - Ok((RedisCommandKind::Zdiffstore, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zincrby( - client: &C, - key: RedisKey, - increment: f64, - member: RedisValue, -) -> Result { - let increment = increment.try_into()?; - let args = vec![key.into(), increment, member]; - args_value_cmd(client, RedisCommandKind::Zincrby, args).await -} - -pub async fn zinter( - client: &C, - keys: MultipleKeys, - weights: MultipleWeights, - aggregate: Option, - withscores: bool, -) -> Result { - let frame = utils::request_response(client, move || { - let args_len = 6 + keys.len() + weights.len(); - let mut args = Vec::with_capacity(args_len); - args.push(keys.len().try_into()?); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - if weights.len() > 0 { - args.push(static_val!(WEIGHTS)); - for weight in weights.inner().into_iter() { - args.push(weight.try_into()?); - } - } - if let Some(options) = aggregate { - args.push(static_val!(AGGREGATE)); - args.push(options.to_str().into()); - } - if withscores { - args.push(static_val!(WITH_SCORES)); - } - - Ok((RedisCommandKind::Zinter, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zinterstore( - client: &C, - dest: RedisKey, - keys: MultipleKeys, - weights: MultipleWeights, - aggregate: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let args_len = 5 + keys.len() + weights.len(); - let mut args = Vec::with_capacity(args_len); - args.push(dest.into()); - args.push(keys.len().try_into()?); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - if weights.len() > 0 { - args.push(static_val!(WEIGHTS)); - for weight in weights.inner().into_iter() { - args.push(weight.try_into()?); - } - } - if let Some(options) = aggregate { - args.push(static_val!(AGGREGATE)); - args.push(options.to_str().into()); - } - - Ok((RedisCommandKind::Zinterstore, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zlexcount( - client: &C, - key: RedisKey, - min: ZRange, - max: ZRange, -) -> Result { - check_range_types(&min, &max, &Some(ZSort::ByLex))?; - - let args = vec![key.into(), min.into_value()?, max.into_value()?]; - args_value_cmd(client, RedisCommandKind::Zlexcount, args).await -} - -pub async fn zpopmax( - client: &C, - key: RedisKey, - count: Option, -) -> Result { - let args = if let Some(count) = count { - vec![key.into(), count.try_into()?] - } else { - vec![key.into()] - }; - - args_values_cmd(client, RedisCommandKind::Zpopmax, args).await -} - -pub async fn zpopmin( - client: &C, - key: RedisKey, - count: Option, -) -> Result { - let args = if let Some(count) = count { - vec![key.into(), count.try_into()?] - } else { - vec![key.into()] - }; - - args_values_cmd(client, RedisCommandKind::Zpopmin, args).await -} - -pub async fn zmpop( - client: &C, - keys: MultipleKeys, - sort: ZCmp, - count: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(keys.len() + 3); - args.push(keys.len().try_into()?); - for key in keys.inner().into_iter() { - args.push(key.into()); - } - args.push(sort.to_str().into()); - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.into()); - } - - Ok((RedisCommandKind::Zmpop, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zrandmember( - client: &C, - key: RedisKey, - count: Option<(i64, bool)>, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(3); - args.push(key.into()); - - if let Some((count, withscores)) = count { - args.push(count.into()); - if withscores { - args.push(static_val!(WITH_SCORES)); - } - } - - Ok((RedisCommandKind::Zrandmember, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zrangestore( - client: &C, - dest: RedisKey, - source: RedisKey, - min: ZRange, - max: ZRange, - sort: Option, - rev: bool, - limit: Option, -) -> Result { - check_range_types(&min, &max, &sort)?; - - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(9); - args.push(dest.into()); - args.push(source.into()); - args.push(min.into_value()?); - args.push(max.into_value()?); - - if let Some(sort) = sort { - args.push(sort.to_str().into()); - } - if rev { - args.push(static_val!(REV)); - } - if let Some((offset, count)) = limit { - args.push(static_val!(LIMIT)); - args.push(offset.into()); - args.push(count.into()); - } - - Ok((RedisCommandKind::Zrangestore, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zrange( - client: &C, - key: RedisKey, - min: ZRange, - max: ZRange, - sort: Option, - rev: bool, - limit: Option, - withscores: bool, -) -> Result { - check_range_types(&min, &max, &sort)?; - - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(9); - args.push(key.into()); - args.push(min.into_value()?); - args.push(max.into_value()?); - - if let Some(sort) = sort { - args.push(sort.to_str().into()); - } - if rev { - args.push(static_val!(REV)); - } - if let Some((offset, count)) = limit { - args.push(static_val!(LIMIT)); - args.push(offset.into()); - args.push(count.into()); - } - if withscores { - args.push(static_val!(WITH_SCORES)); - } - - Ok((RedisCommandKind::Zrange, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zrangebylex( - client: &C, - key: RedisKey, - min: ZRange, - max: ZRange, - limit: Option, -) -> Result { - check_range_types(&min, &max, &Some(ZSort::ByLex))?; - - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(6); - args.push(key.into()); - args.push(min.into_value()?); - args.push(max.into_value()?); - - if let Some((offset, count)) = limit { - args.push(static_val!(LIMIT)); - args.push(offset.into()); - args.push(count.into()); - } - - Ok((RedisCommandKind::Zrangebylex, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zrevrangebylex( - client: &C, - key: RedisKey, - max: ZRange, - min: ZRange, - limit: Option, -) -> Result { - check_range_types(&min, &max, &Some(ZSort::ByLex))?; - - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(6); - args.push(key.into()); - args.push(max.into_value()?); - args.push(min.into_value()?); - - if let Some((offset, count)) = limit { - args.push(static_val!(LIMIT)); - args.push(offset.into()); - args.push(count.into()); - } - - Ok((RedisCommandKind::Zrevrangebylex, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zrangebyscore( - client: &C, - key: RedisKey, - min: ZRange, - max: ZRange, - withscores: bool, - limit: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(7); - args.push(key.into()); - args.push(min.into_value()?); - args.push(max.into_value()?); - - if withscores { - args.push(static_val!(WITH_SCORES)); - } - if let Some((offset, count)) = limit { - args.push(static_val!(LIMIT)); - args.push(offset.into()); - args.push(count.into()); - } - - Ok((RedisCommandKind::Zrangebyscore, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zrevrangebyscore( - client: &C, - key: RedisKey, - max: ZRange, - min: ZRange, - withscores: bool, - limit: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(7); - args.push(key.into()); - args.push(max.into_value()?); - args.push(min.into_value()?); - - if withscores { - args.push(static_val!(WITH_SCORES)); - } - if let Some((offset, count)) = limit { - args.push(static_val!(LIMIT)); - args.push(offset.into()); - args.push(count.into()); - } - - Ok((RedisCommandKind::Zrevrangebyscore, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zrank(client: &C, key: RedisKey, member: RedisValue) -> Result { - args_value_cmd(client, RedisCommandKind::Zrank, vec![key.into(), member]).await -} - -pub async fn zrem( - client: &C, - key: RedisKey, - members: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let members = members.into_multiple_values(); - let mut args = Vec::with_capacity(1 + members.len()); - args.push(key.into()); - - for member in members.into_iter() { - args.push(member); - } - Ok((RedisCommandKind::Zrem, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zremrangebylex( - client: &C, - key: RedisKey, - min: ZRange, - max: ZRange, -) -> Result { - let frame = utils::request_response(client, move || { - check_range_types(&min, &max, &Some(ZSort::ByLex))?; - - Ok((RedisCommandKind::Zremrangebylex, vec![ - key.into(), - min.into_value()?, - max.into_value()?, - ])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zremrangebyrank( - client: &C, - key: RedisKey, - start: i64, - stop: i64, -) -> Result { - let (start, stop) = (start.into(), stop.into()); - args_value_cmd(client, RedisCommandKind::Zremrangebyrank, vec![key.into(), start, stop]).await -} - -pub async fn zremrangebyscore( - client: &C, - key: RedisKey, - min: ZRange, - max: ZRange, -) -> Result { - let frame = utils::request_response(client, move || { - check_range_types(&min, &max, &Some(ZSort::ByScore))?; - - Ok((RedisCommandKind::Zremrangebyscore, vec![ - key.into(), - min.into_value()?, - max.into_value()?, - ])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zrevrange( - client: &C, - key: RedisKey, - start: i64, - stop: i64, - withscores: bool, -) -> Result { - let (start, stop) = (start.into(), stop.into()); - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(4); - args.push(key.into()); - args.push(start); - args.push(stop); - - if withscores { - args.push(static_val!(WITH_SCORES)); - } - - Ok((RedisCommandKind::Zrevrange, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zrevrank( - client: &C, - key: RedisKey, - member: RedisValue, -) -> Result { - args_value_cmd(client, RedisCommandKind::Zrevrank, vec![key.into(), member]).await -} - -pub async fn zscore(client: &C, key: RedisKey, member: RedisValue) -> Result { - args_value_cmd(client, RedisCommandKind::Zscore, vec![key.into(), member]).await -} - -pub async fn zunion( - client: &C, - keys: MultipleKeys, - weights: MultipleWeights, - aggregate: Option, - withscores: bool, -) -> Result { - let frame = utils::request_response(client, move || { - let args_len = keys.len() + weights.len(); - let mut args = Vec::with_capacity(5 + args_len); - args.push(keys.len().try_into()?); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - if weights.len() > 0 { - args.push(static_val!(WEIGHTS)); - for weight in weights.inner().into_iter() { - args.push(weight.try_into()?); - } - } - - if let Some(aggregate) = aggregate { - args.push(static_val!(AGGREGATE)); - args.push(aggregate.to_str().into()); - } - if withscores { - args.push(static_val!(WITH_SCORES)); - } - - Ok((RedisCommandKind::Zunion, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zunionstore( - client: &C, - dest: RedisKey, - keys: MultipleKeys, - weights: MultipleWeights, - aggregate: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let args_len = keys.len() + weights.len(); - let mut args = Vec::with_capacity(5 + args_len); - args.push(dest.into()); - args.push(keys.len().try_into()?); - - for key in keys.inner().into_iter() { - args.push(key.into()); - } - if weights.len() > 0 { - args.push(static_val!(WEIGHTS)); - for weight in weights.inner().into_iter() { - args.push(weight.try_into()?); - } - } - - if let Some(aggregate) = aggregate { - args.push(static_val!(AGGREGATE)); - args.push(aggregate.to_str().into()); - } - - Ok((RedisCommandKind::Zunionstore, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn zmscore( - client: &C, - key: RedisKey, - members: MultipleValues, -) -> Result { - let frame = utils::request_response(client, move || { - let members = members.into_multiple_values(); - let mut args = Vec::with_capacity(1 + members.len()); - args.push(key.into()); - - for member in members.into_iter() { - args.push(member); - } - Ok((RedisCommandKind::Zmscore, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/streams.rs b/src/commands/impls/streams.rs deleted file mode 100644 index a1025759..00000000 --- a/src/commands/impls/streams.rs +++ /dev/null @@ -1,515 +0,0 @@ -use super::*; -use crate::{ - error::RedisError, - protocol::{ - command::{RedisCommand, RedisCommandKind}, - hashers::ClusterHash, - utils as protocol_utils, - }, - types::{ - MultipleIDs, - MultipleKeys, - MultipleOrderedPairs, - MultipleStrings, - RedisKey, - RedisValue, - XCap, - XPendingArgs, - XID, - }, - utils, -}; -use bytes_utils::Str; -use std::convert::TryInto; - -fn encode_cap(args: &mut Vec, cap: XCap) { - if let Some((kind, trim, threshold, limit)) = cap.into_parts() { - args.push(kind.to_str().into()); - args.push(trim.to_str().into()); - args.push(threshold.into_arg()); - if let Some(count) = limit { - args.push(static_val!(LIMIT)); - args.push(count.into()); - } - } -} - -pub async fn xinfo_consumers( - client: &C, - key: RedisKey, - groupname: Str, -) -> Result { - let frame = utils::request_response(client, move || { - let args = vec![key.into(), groupname.into()]; - Ok((RedisCommandKind::XinfoConsumers, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xinfo_groups(client: &C, key: RedisKey) -> Result { - let frame = utils::request_response(client, move || Ok((RedisCommandKind::XinfoGroups, vec![key.into()]))).await?; - protocol_utils::frame_to_results(frame) -} - -pub async fn xinfo_stream( - client: &C, - key: RedisKey, - full: bool, - count: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(4); - args.push(key.into()); - - if full { - args.push(static_val!(FULL)); - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.try_into()?); - } - } - - Ok((RedisCommandKind::XinfoStream, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xadd( - client: &C, - key: RedisKey, - nomkstream: bool, - cap: XCap, - id: XID, - fields: MultipleOrderedPairs, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(8 + (fields.len() * 2)); - args.push(key.into()); - - if nomkstream { - args.push(static_val!(NOMKSTREAM)); - } - encode_cap(&mut args, cap); - - args.push(id.into_str().into()); - for (key, value) in fields.inner().into_iter() { - args.push(key.into()); - args.push(value); - } - - Ok((RedisCommandKind::Xadd, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xtrim(client: &C, key: RedisKey, cap: XCap) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(6); - args.push(key.into()); - encode_cap(&mut args, cap); - - Ok((RedisCommandKind::Xtrim, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xdel(client: &C, key: RedisKey, ids: MultipleStrings) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(1 + ids.len()); - args.push(key.into()); - - for id in ids.inner().into_iter() { - args.push(id.into()); - } - Ok((RedisCommandKind::Xdel, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xrange( - client: &C, - key: RedisKey, - start: RedisValue, - end: RedisValue, - count: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(5); - args.push(key.into()); - args.push(start); - args.push(end); - - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.try_into()?); - } - - Ok((RedisCommandKind::Xrange, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xrevrange( - client: &C, - key: RedisKey, - end: RedisValue, - start: RedisValue, - count: Option, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(5); - args.push(key.into()); - args.push(end); - args.push(start); - - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.try_into()?); - } - - Ok((RedisCommandKind::Xrevrange, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xlen(client: &C, key: RedisKey) -> Result { - one_arg_value_cmd(client, RedisCommandKind::Xlen, key.into()).await -} - -pub async fn xread( - client: &C, - count: Option, - block: Option, - keys: MultipleKeys, - ids: MultipleIDs, -) -> Result { - let is_clustered = client.inner().config.server.is_clustered(); - let frame = utils::request_response(client, move || { - let is_blocking = block.is_some(); - let mut hash_slot = None; - let mut args = Vec::with_capacity(5 + keys.len() + ids.len()); - - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.try_into()?); - } - if let Some(block) = block { - args.push(static_val!(BLOCK)); - args.push(block.try_into()?); - } - args.push(static_val!(STREAMS)); - - for (idx, key) in keys.inner().into_iter().enumerate() { - // set the hash slot from the first key. if any other keys hash into another slot the server will return - // CROSSSLOT error - if is_clustered && idx == 0 { - hash_slot = Some(ClusterHash::Offset(args.len())); - } - - args.push(key.into()); - } - for id in ids.inner().into_iter() { - args.push(id.into_str().into()); - } - - let mut command: RedisCommand = (RedisCommandKind::Xread, args).into(); - command.can_pipeline = !is_blocking; - command.hasher = hash_slot.unwrap_or(ClusterHash::Random); - Ok(command) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xgroup_create( - client: &C, - key: RedisKey, - groupname: Str, - id: XID, - mkstream: bool, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(4); - args.push(key.into()); - args.push(groupname.into()); - args.push(id.into_str().into()); - if mkstream { - args.push(static_val!(MKSTREAM)); - } - - Ok((RedisCommandKind::Xgroupcreate, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xgroup_createconsumer( - client: &C, - key: RedisKey, - groupname: Str, - consumername: Str, -) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::XgroupCreateConsumer, vec![ - key.into(), - groupname.into(), - consumername.into(), - ])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xgroup_delconsumer( - client: &C, - key: RedisKey, - groupname: Str, - consumername: Str, -) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::XgroupDelConsumer, vec![ - key.into(), - groupname.into(), - consumername.into(), - ])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xgroup_destroy( - client: &C, - key: RedisKey, - groupname: Str, -) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::XgroupDestroy, vec![key.into(), groupname.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xgroup_setid( - client: &C, - key: RedisKey, - groupname: Str, - id: XID, -) -> Result { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::XgroupSetId, vec![ - key.into(), - groupname.into(), - id.into_str().into(), - ])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xreadgroup( - client: &C, - group: Str, - consumer: Str, - count: Option, - block: Option, - noack: bool, - keys: MultipleKeys, - ids: MultipleIDs, -) -> Result { - let is_clustered = client.inner().config.server.is_clustered(); - let frame = utils::request_response(client, move || { - let is_blocking = block.is_some(); - let mut hash_slot = None; - - let mut args = Vec::with_capacity(9 + keys.len() + ids.len()); - args.push(static_val!(GROUP)); - args.push(group.into()); - args.push(consumer.into()); - - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.try_into()?); - } - if let Some(block) = block { - args.push(static_val!(BLOCK)); - args.push(block.try_into()?); - } - if noack { - args.push(static_val!(NOACK)); - } - - args.push(static_val!(STREAMS)); - for (idx, key) in keys.inner().into_iter().enumerate() { - if is_clustered && idx == 0 { - hash_slot = Some(ClusterHash::Offset(args.len())); - } - - args.push(key.into()); - } - for id in ids.inner().into_iter() { - args.push(id.into_str().into()); - } - - let mut command: RedisCommand = (RedisCommandKind::Xreadgroup, args).into(); - command.can_pipeline = !is_blocking; - command.hasher = hash_slot.unwrap_or(ClusterHash::Random); - Ok(command) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xack( - client: &C, - key: RedisKey, - group: Str, - ids: MultipleIDs, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2 + ids.len()); - args.push(key.into()); - args.push(group.into()); - - for id in ids.inner().into_iter() { - args.push(id.into_str().into()); - } - Ok((RedisCommandKind::Xack, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xclaim( - client: &C, - key: RedisKey, - group: Str, - consumer: Str, - min_idle_time: u64, - ids: MultipleIDs, - idle: Option, - time: Option, - retry_count: Option, - force: bool, - justid: bool, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(12 + ids.len()); - args.push(key.into()); - args.push(group.into()); - args.push(consumer.into()); - args.push(min_idle_time.try_into()?); - - for id in ids.inner().into_iter() { - args.push(id.into_str().into()); - } - if let Some(idle) = idle { - args.push(static_val!(IDLE)); - args.push(idle.try_into()?); - } - if let Some(time) = time { - args.push(static_val!(TIME)); - args.push(time.try_into()?); - } - if let Some(retry_count) = retry_count { - args.push(static_val!(RETRYCOUNT)); - args.push(retry_count.try_into()?); - } - if force { - args.push(static_val!(FORCE)); - } - if justid { - args.push(static_val!(JUSTID)); - } - - Ok((RedisCommandKind::Xclaim, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xautoclaim( - client: &C, - key: RedisKey, - group: Str, - consumer: Str, - min_idle_time: u64, - start: XID, - count: Option, - justid: bool, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(8); - args.push(key.into()); - args.push(group.into()); - args.push(consumer.into()); - args.push(min_idle_time.try_into()?); - args.push(start.into_str().into()); - - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.try_into()?); - } - if justid { - args.push(static_val!(JUSTID)); - } - - Ok((RedisCommandKind::Xautoclaim, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn xpending( - client: &C, - key: RedisKey, - group: Str, - cmd_args: XPendingArgs, -) -> Result { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(8); - args.push(key.into()); - args.push(group.into()); - - if let Some((idle, start, end, count, consumer)) = cmd_args.into_parts()? { - if let Some(idle) = idle { - args.push(static_val!(IDLE)); - args.push(idle.try_into()?); - } - args.push(start.into_str().into()); - args.push(end.into_str().into()); - args.push(count.try_into()?); - if let Some(consumer) = consumer { - args.push(consumer.into()); - } - } - - Ok((RedisCommandKind::Xpending, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/strings.rs b/src/commands/impls/strings.rs deleted file mode 100644 index 8b137891..00000000 --- a/src/commands/impls/strings.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/commands/impls/timeseries.rs b/src/commands/impls/timeseries.rs deleted file mode 100644 index 091a0aa4..00000000 --- a/src/commands/impls/timeseries.rs +++ /dev/null @@ -1,574 +0,0 @@ -use crate::{ - error::RedisError, - interfaces::{ClientLike, RedisResult}, - prelude::RedisKey, - protocol::{command::RedisCommandKind, utils as protocol_utils}, - types::{ - Aggregator, - DuplicatePolicy, - Encoding, - GetLabels, - GetTimestamp, - GroupBy, - RangeAggregation, - RedisMap, - RedisValue, - Timestamp, - }, - utils, -}; -use bytes_utils::Str; - -static LATEST: &str = "LATEST"; -static FILTER_BY_TS: &str = "FILTER_BY_TS"; -static FILTER_BY_VALUE: &str = "FILTER_BY_VALUE"; -static COUNT: &str = "COUNT"; -static ALIGN: &str = "ALIGN"; -static AGGREGATION: &str = "AGGREGATION"; -static BUCKETTIMESTAMP: &str = "BUCKETTIMESTAMP"; -static EMPTY: &str = "EMPTY"; -static WITHLABELS: &str = "WITHLABELS"; -static SELECTED_LABELS: &str = "SELECTED_LABELS"; -static FILTER: &str = "FILTER"; -static GROUPBY: &str = "GROUPBY"; -static REDUCE: &str = "REDUCE"; -static RETENTION: &str = "RETENTION"; -static ENCODING: &str = "ENCODING"; -static CHUNK_SIZE: &str = "CHUNK_SIZE"; -static ON_DUPLICATE: &str = "ON_DUPLICATE"; -static DUPLICATE_POLICY: &str = "DUPLICATE_POLICY"; -static LABELS: &str = "LABELS"; -static UNCOMPRESSED: &str = "UNCOMPRESSED"; -static TIMESTAMP: &str = "TIMESTAMP"; -static DEBUG: &str = "DEBUG"; - -fn add_labels(args: &mut Vec, labels: RedisMap) { - if !labels.is_empty() { - args.push(static_val!(LABELS)); - - for (label, value) in labels.inner().into_iter() { - args.push(label.into()); - args.push(value); - } - } -} - -fn add_retention(args: &mut Vec, retention: Option) -> Result<(), RedisError> { - if let Some(retention) = retention { - args.push(static_val!(RETENTION)); - args.push(retention.try_into()?); - } - - Ok(()) -} - -fn add_encoding(args: &mut Vec, encoding: Option) { - if let Some(encoding) = encoding { - args.push(static_val!(ENCODING)); - args.push(encoding.to_str().into()); - } -} - -fn add_chunk_size(args: &mut Vec, chunk_size: Option) -> Result<(), RedisError> { - if let Some(chunk_size) = chunk_size { - args.push(static_val!(CHUNK_SIZE)); - args.push(chunk_size.try_into()?); - } - - Ok(()) -} - -fn add_timestamp(args: &mut Vec, timestamp: Option) { - if let Some(timestamp) = timestamp { - args.push(static_val!(TIMESTAMP)); - args.push(timestamp.to_value()); - } -} - -fn add_duplicate_policy(args: &mut Vec, duplicate_policy: Option) { - if let Some(duplicate) = duplicate_policy { - args.push(static_val!(DUPLICATE_POLICY)); - args.push(duplicate.to_str().into()); - } -} - -fn add_count(args: &mut Vec, count: Option) -> Result<(), RedisError> { - if let Some(count) = count { - args.push(static_val!(COUNT)); - args.push(count.try_into()?); - } - Ok(()) -} - -fn add_get_labels(args: &mut Vec, labels: Option) { - if let Some(labels) = labels { - match labels { - GetLabels::WithLabels => args.push(static_val!(WITHLABELS)), - GetLabels::SelectedLabels(labels) => { - args.push(static_val!(SELECTED_LABELS)); - args.extend(labels.into_iter().map(|v| v.into())); - }, - } - } -} - -fn add_range_aggregation( - args: &mut Vec, - aggregation: Option, -) -> Result<(), RedisError> { - if let Some(aggregation) = aggregation { - if let Some(align) = aggregation.align { - args.push(static_val!(ALIGN)); - args.push(align.to_value()); - } - - args.push(static_val!(AGGREGATION)); - args.push(aggregation.aggregation.to_str().into()); - args.push(aggregation.bucket_duration.try_into()?); - - if let Some(bucket_timestamp) = aggregation.bucket_timestamp { - args.push(static_val!(BUCKETTIMESTAMP)); - args.push(bucket_timestamp.to_str().into()); - } - if aggregation.empty { - args.push(static_val!(EMPTY)); - } - } - - Ok(()) -} - -fn add_groupby(args: &mut Vec, group_by: Option) { - if let Some(group_by) = group_by { - args.push(static_val!(GROUPBY)); - args.push(group_by.groupby.into()); - args.push(static_val!(REDUCE)); - args.push(group_by.reduce.to_str().into()); - } -} - -fn add_filters(args: &mut Vec, filters: Vec) { - args.push(static_val!(FILTER)); - args.extend(filters.into_iter().map(|s| s.into())); -} - -pub async fn ts_add( - client: &C, - key: RedisKey, - timestamp: Timestamp, - value: f64, - retention: Option, - encoding: Option, - chunk_size: Option, - on_duplicate: Option, - labels: RedisMap, -) -> RedisResult { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(12 + labels.len() * 2); - args.push(key.into()); - args.push(timestamp.to_value()); - args.push(value.into()); - - add_retention(&mut args, retention)?; - add_encoding(&mut args, encoding); - add_chunk_size(&mut args, chunk_size)?; - if let Some(duplicate) = on_duplicate { - args.push(static_val!(ON_DUPLICATE)); - args.push(duplicate.to_str().into()); - } - - add_labels(&mut args, labels); - Ok((RedisCommandKind::TsAdd, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_alter( - client: &C, - key: RedisKey, - retention: Option, - chunk_size: Option, - duplicate_policy: Option, - labels: RedisMap, -) -> RedisResult { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(8 + labels.len() * 2); - args.push(key.into()); - - add_retention(&mut args, retention)?; - add_chunk_size(&mut args, chunk_size)?; - add_duplicate_policy(&mut args, duplicate_policy); - add_labels(&mut args, labels); - Ok((RedisCommandKind::TsAlter, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_create( - client: &C, - key: RedisKey, - retention: Option, - encoding: Option, - chunk_size: Option, - duplicate_policy: Option, - labels: RedisMap, -) -> RedisResult { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(10 + labels.len() * 2); - args.push(key.into()); - - add_retention(&mut args, retention)?; - add_encoding(&mut args, encoding); - add_chunk_size(&mut args, chunk_size)?; - add_duplicate_policy(&mut args, duplicate_policy); - add_labels(&mut args, labels); - Ok((RedisCommandKind::TsCreate, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_createrule( - client: &C, - src: RedisKey, - dest: RedisKey, - aggregation: (Aggregator, u64), - align_timestamp: Option, -) -> RedisResult { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(6); - args.extend([ - src.into(), - dest.into(), - static_val!(AGGREGATION), - aggregation.0.to_str().into(), - aggregation.1.try_into()?, - ]); - - if let Some(align) = align_timestamp { - args.push(align.try_into()?) - } - Ok((RedisCommandKind::TsCreateRule, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_decrby( - client: &C, - key: RedisKey, - subtrahend: f64, - timestamp: Option, - retention: Option, - uncompressed: bool, - chunk_size: Option, - labels: RedisMap, -) -> RedisResult { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(10 + labels.len() * 2); - args.push(key.into()); - args.push(subtrahend.into()); - - add_timestamp(&mut args, timestamp); - add_retention(&mut args, retention)?; - if uncompressed { - args.push(static_val!(UNCOMPRESSED)); - } - add_chunk_size(&mut args, chunk_size)?; - add_labels(&mut args, labels); - - Ok((RedisCommandKind::TsDecrBy, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_del(client: &C, key: RedisKey, from: i64, to: i64) -> RedisResult { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::TsDel, vec![key.into(), from.into(), to.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_deleterule(client: &C, src: RedisKey, dest: RedisKey) -> RedisResult { - let frame = utils::request_response(client, move || { - Ok((RedisCommandKind::TsDeleteRule, vec![src.into(), dest.into()])) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_get(client: &C, key: RedisKey, latest: bool) -> RedisResult { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2); - args.push(key.into()); - if latest { - args.push(static_val!(LATEST)); - } - - Ok((RedisCommandKind::TsGet, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_incrby( - client: &C, - key: RedisKey, - addend: f64, - timestamp: Option, - retention: Option, - uncompressed: bool, - chunk_size: Option, - labels: RedisMap, -) -> RedisResult { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(10 + labels.len() * 2); - args.push(key.into()); - args.push(addend.into()); - - add_timestamp(&mut args, timestamp); - add_retention(&mut args, retention)?; - if uncompressed { - args.push(static_val!(UNCOMPRESSED)); - } - add_chunk_size(&mut args, chunk_size)?; - add_labels(&mut args, labels); - - Ok((RedisCommandKind::TsIncrBy, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_info(client: &C, key: RedisKey, debug: bool) -> RedisResult { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(2); - args.push(key.into()); - if debug { - args.push(static_val!(DEBUG)); - } - - Ok((RedisCommandKind::TsInfo, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_madd(client: &C, samples: Vec<(RedisKey, Timestamp, f64)>) -> RedisResult { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(samples.len() * 3); - for (key, timestamp, value) in samples.into_iter() { - args.extend([key.into(), timestamp.to_value(), value.into()]); - } - Ok((RedisCommandKind::TsMAdd, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_mget( - client: &C, - latest: bool, - labels: Option, - filters: Vec, -) -> RedisResult { - let frame = utils::request_response(client, move || { - let labels_len = labels.as_ref().map(|l| l.args_len()).unwrap_or(0); - let mut args = Vec::with_capacity(2 + labels_len + filters.len()); - if latest { - args.push(static_val!(LATEST)); - } - add_get_labels(&mut args, labels); - add_filters(&mut args, filters); - - Ok((RedisCommandKind::TsMGet, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_mrange( - client: &C, - from: GetTimestamp, - to: GetTimestamp, - latest: bool, - filter_by_ts: Vec, - filter_by_value: Option<(i64, i64)>, - labels: Option, - count: Option, - aggregation: Option, - filters: Vec, - group_by: Option, -) -> RedisResult { - let frame = utils::request_response(client, move || { - let labels_len = labels.as_ref().map(|l| l.args_len()).unwrap_or(0); - let mut args = Vec::with_capacity(18 + filter_by_ts.len() + labels_len + filters.len()); - - args.extend([from.to_value(), to.to_value()]); - if latest { - args.push(static_val!(LATEST)); - } - if !filter_by_ts.is_empty() { - args.push(static_val!(FILTER_BY_TS)); - args.extend(filter_by_ts.into_iter().map(|t| t.into())); - } - if let Some((min, max)) = filter_by_value { - args.push(static_val!(FILTER_BY_VALUE)); - args.extend([min.into(), max.into()]); - } - add_get_labels(&mut args, labels); - add_count(&mut args, count)?; - add_range_aggregation(&mut args, aggregation)?; - add_filters(&mut args, filters); - add_groupby(&mut args, group_by); - - Ok((RedisCommandKind::TsMRange, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_mrevrange( - client: &C, - from: GetTimestamp, - to: GetTimestamp, - latest: bool, - filter_by_ts: Vec, - filter_by_value: Option<(i64, i64)>, - labels: Option, - count: Option, - aggregation: Option, - filters: Vec, - group_by: Option, -) -> RedisResult { - let frame = utils::request_response(client, move || { - let labels_len = labels.as_ref().map(|l| l.args_len()).unwrap_or(0); - let mut args = Vec::with_capacity(18 + filter_by_ts.len() + labels_len + filters.len()); - - args.extend([from.to_value(), to.to_value()]); - if latest { - args.push(static_val!(LATEST)); - } - if !filter_by_ts.is_empty() { - args.push(static_val!(FILTER_BY_TS)); - args.extend(filter_by_ts.into_iter().map(|t| t.into())); - } - if let Some((min, max)) = filter_by_value { - args.push(static_val!(FILTER_BY_VALUE)); - args.extend([min.into(), max.into()]); - } - add_get_labels(&mut args, labels); - add_count(&mut args, count)?; - add_range_aggregation(&mut args, aggregation)?; - add_filters(&mut args, filters); - add_groupby(&mut args, group_by); - - Ok((RedisCommandKind::TsMRevRange, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_queryindex(client: &C, filters: Vec) -> RedisResult { - let frame = utils::request_response(client, move || { - Ok(( - RedisCommandKind::TsQueryIndex, - filters.into_iter().map(|v| v.into()).collect(), - )) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_range( - client: &C, - key: RedisKey, - from: GetTimestamp, - to: GetTimestamp, - latest: bool, - filter_by_ts: Vec, - filter_by_value: Option<(i64, i64)>, - count: Option, - aggregation: Option, -) -> RedisResult { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(14 + filter_by_ts.len()); - args.push(key.into()); - args.extend([from.to_value(), to.to_value()]); - - if latest { - args.push(static_val!(LATEST)); - } - if !filter_by_ts.is_empty() { - args.push(static_val!(FILTER_BY_TS)); - args.extend(filter_by_ts.into_iter().map(|v| v.into())); - } - if let Some((min, max)) = filter_by_value { - args.push(static_val!(FILTER_BY_VALUE)); - args.extend([min.into(), max.into()]); - } - add_count(&mut args, count)?; - add_range_aggregation(&mut args, aggregation)?; - - Ok((RedisCommandKind::TsRange, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} - -pub async fn ts_revrange( - client: &C, - key: RedisKey, - from: GetTimestamp, - to: GetTimestamp, - latest: bool, - filter_by_ts: Vec, - filter_by_value: Option<(i64, i64)>, - count: Option, - aggregation: Option, -) -> RedisResult { - let frame = utils::request_response(client, move || { - let mut args = Vec::with_capacity(14 + filter_by_ts.len()); - args.push(key.into()); - args.extend([from.to_value(), to.to_value()]); - - if latest { - args.push(static_val!(LATEST)); - } - if !filter_by_ts.is_empty() { - args.push(static_val!(FILTER_BY_TS)); - args.extend(filter_by_ts.into_iter().map(|v| v.into())); - } - if let Some((min, max)) = filter_by_value { - args.push(static_val!(FILTER_BY_VALUE)); - args.extend([min.into(), max.into()]); - } - add_count(&mut args, count)?; - add_range_aggregation(&mut args, aggregation)?; - - Ok((RedisCommandKind::TsRevRange, args)) - }) - .await?; - - protocol_utils::frame_to_results(frame) -} diff --git a/src/commands/impls/tracking.rs b/src/commands/impls/tracking.rs deleted file mode 100644 index 9b088ddf..00000000 --- a/src/commands/impls/tracking.rs +++ /dev/null @@ -1,171 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - interfaces::ClientLike, - protocol::{ - command::{RedisCommand, RedisCommandKind}, - responders::ResponseKind, - utils as protocol_utils, - }, - runtime::oneshot_channel, - types::{ClusterHash, MultipleStrings, RedisValue, Toggle}, - utils, -}; -use redis_protocol::redis_keyslot; - -pub static PREFIX: &str = "PREFIX"; -pub static REDIRECT: &str = "REDIRECT"; -pub static BCAST: &str = "BCAST"; -pub static OPTIN: &str = "OPTIN"; -pub static OPTOUT: &str = "OPTOUT"; -pub static NOLOOP: &str = "NOLOOP"; -pub static YES: &str = "YES"; -pub static NO: &str = "NO"; - -fn tracking_args( - toggle: Toggle, - redirect: Option, - prefixes: MultipleStrings, - bcast: bool, - optin: bool, - optout: bool, - noloop: bool, -) -> Vec { - let mut args = Vec::with_capacity(prefixes.len() * 2 + 7); - args.push(static_val!(toggle.to_str())); - if let Some(redirect) = redirect { - args.push(static_val!(REDIRECT)); - args.push(redirect.into()); - } - for prefix in prefixes.inner().into_iter() { - args.push(static_val!(PREFIX)); - args.push(prefix.into()); - } - if bcast { - args.push(static_val!(BCAST)); - } - if optin { - args.push(static_val!(OPTIN)); - } - if optout { - args.push(static_val!(OPTOUT)); - } - if noloop { - args.push(static_val!(NOLOOP)); - } - - args -} - -pub async fn start_tracking( - client: &C, - prefixes: MultipleStrings, - bcast: bool, - optin: bool, - optout: bool, - noloop: bool, -) -> Result<(), RedisError> { - if !client.inner().is_resp3() { - return Err(RedisError::new( - RedisErrorKind::Config, - "Client tracking requires RESP3.", - )); - } - - let args = tracking_args(Toggle::On, None, prefixes, bcast, optin, optout, noloop); - if client.inner().config.server.is_clustered() { - if bcast { - // only send the tracking command on one connection when in bcast mode - let frame = utils::request_response(client, move || { - let mut command = RedisCommand::new(RedisCommandKind::ClientTracking, args); - command.hasher = ClusterHash::Custom(redis_keyslot(client.id().as_bytes())); - Ok(command) - }) - .await?; - - protocol_utils::frame_to_results(frame)?.convert() - } else { - // send the tracking command to all nodes when not in bcast mode - let (tx, rx) = oneshot_channel(); - let response = ResponseKind::new_buffer(tx); - let command: RedisCommand = (RedisCommandKind::_ClientTrackingCluster, args, response).into(); - client.send_command(command)?; - - let frame = utils::timeout(rx, client.inner().internal_command_timeout()).await??; - let _ = protocol_utils::frame_to_results(frame)?; - Ok(()) - } - } else { - utils::request_response(client, move || Ok((RedisCommandKind::ClientTracking, args))) - .await - .and_then(protocol_utils::frame_to_results) - .and_then(|v| v.convert()) - } -} - -pub async fn stop_tracking(client: &C) -> Result<(), RedisError> { - if !client.inner().is_resp3() { - return Err(RedisError::new( - RedisErrorKind::Config, - "Client tracking requires RESP3.", - )); - } - - let args = vec![static_val!(Toggle::Off.to_str())]; - if client.is_clustered() { - // turn off tracking on all connections - let (tx, rx) = oneshot_channel(); - let response = ResponseKind::new_buffer(tx); - let command: RedisCommand = (RedisCommandKind::_ClientTrackingCluster, args, response).into(); - client.send_command(command)?; - - let frame = utils::timeout(rx, client.inner().internal_command_timeout()).await??; - let _ = protocol_utils::frame_to_results(frame)?; - Ok(()) - } else { - utils::request_response(client, move || Ok((RedisCommandKind::ClientTracking, args))) - .await - .and_then(protocol_utils::frame_to_results) - .and_then(|v| v.convert()) - } -} - -pub async fn client_tracking( - client: &C, - toggle: Toggle, - redirect: Option, - prefixes: MultipleStrings, - bcast: bool, - optin: bool, - optout: bool, - noloop: bool, -) -> Result { - let args = tracking_args(toggle, redirect, prefixes, bcast, optin, optout, noloop); - - utils::request_response(client, move || Ok((RedisCommandKind::ClientTracking, args))) - .await - .and_then(protocol_utils::frame_to_results) -} - -pub async fn client_trackinginfo(client: &C) -> Result { - utils::request_response(client, move || Ok((RedisCommandKind::ClientTrackingInfo, vec![]))) - .await - .and_then(protocol_utils::frame_to_results) -} - -pub async fn client_getredir(client: &C) -> Result { - utils::request_response(client, move || Ok((RedisCommandKind::ClientGetRedir, vec![]))) - .await - .and_then(protocol_utils::frame_to_results) -} - -pub async fn client_caching(client: &C, enabled: bool) -> Result { - let args = if enabled { - vec![static_val!(YES)] - } else { - vec![static_val!(NO)] - }; - - utils::request_response(client, move || Ok((RedisCommandKind::ClientCaching, args))) - .await - .and_then(protocol_utils::frame_to_results) -} diff --git a/src/commands/interfaces/acl.rs b/src/commands/interfaces/acl.rs deleted file mode 100644 index bd87f8f5..00000000 --- a/src/commands/interfaces/acl.rs +++ /dev/null @@ -1,142 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - types::{FromRedis, MultipleStrings, MultipleValues}, -}; -use bytes_utils::Str; -use futures::Future; -use rm_send_macros::rm_send_if; - -/// Functions that implement the [ACL](https://redis.io/commandserver) interface. -#[rm_send_if(feature = "glommio")] -pub trait AclInterface: ClientLike + Sized { - /// Create an ACL user with the specified rules or modify the rules of an existing user. - /// - /// - fn acl_setuser(&self, username: S, rules: V) -> impl Future> + Send - where - S: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(username); - try_into!(rules); - commands::acl::acl_setuser(self, username, rules).await - } - } - - /// When Redis is configured to use an ACL file (with the aclfile configuration option), this command will reload - /// the ACLs from the file, replacing all the current ACL rules with the ones defined in the file. - /// - /// - fn acl_load(&self) -> impl Future> + Send { - async move { commands::acl::acl_load(self).await } - } - - /// When Redis is configured to use an ACL file (with the aclfile configuration option), this command will save the - /// currently defined ACLs from the server memory to the ACL file. - /// - /// - fn acl_save(&self) -> impl Future> + Send { - async move { commands::acl::acl_save(self).await } - } - - /// The command shows the currently active ACL rules in the Redis server. - /// - /// \ - fn acl_list(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::acl::acl_list(self).await?.convert() } - } - - /// The command shows a list of all the usernames of the currently configured users in the Redis ACL system. - /// - /// - fn acl_users(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::acl::acl_users(self).await?.convert() } - } - - /// The command returns all the rules defined for an existing ACL user. - /// - /// - fn acl_getuser(&self, username: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(username); - commands::acl::acl_getuser(self, username).await?.convert() - } - } - - /// Delete all the specified ACL users and terminate all the connections that are authenticated with such users. - /// - /// - fn acl_deluser(&self, usernames: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(usernames); - commands::acl::acl_deluser(self, usernames).await?.convert() - } - } - - /// The command shows the available ACL categories if called without arguments. If a category name is given, - /// the command shows all the Redis commands in the specified category. - /// - /// - fn acl_cat(&self, category: Option) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::acl::acl_cat(self, category).await?.convert() } - } - - /// Generate a password with length `bits`, returning the password. - /// - /// - fn acl_genpass(&self, bits: Option) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::acl::acl_genpass(self, bits).await?.convert() } - } - - /// Return the username the current connection is authenticated with. New connections are authenticated - /// with the "default" user. - /// - /// - fn acl_whoami(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::acl::acl_whoami(self).await?.convert() } - } - - /// Read `count` recent ACL security events. - /// - /// - fn acl_log_count(&self, count: Option) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::acl::acl_log_count(self, count).await?.convert() } - } - - /// Clear the ACL security events logs. - /// - /// - fn acl_log_reset(&self) -> impl Future> + Send { - async move { commands::acl::acl_log_reset(self).await } - } -} diff --git a/src/commands/interfaces/client.rs b/src/commands/interfaces/client.rs deleted file mode 100644 index 73004c15..00000000 --- a/src/commands/interfaces/client.rs +++ /dev/null @@ -1,243 +0,0 @@ -use crate::{ - commands, - interfaces::{ClientLike, RedisResult}, - types::{ - ClientKillFilter, - ClientKillType, - ClientPauseKind, - ClientReplyFlag, - ClientUnblockFlag, - FromRedis, - RedisValue, - Server, - }, -}; -#[cfg(feature = "i-tracking")] -use crate::{ - error::RedisError, - types::{MultipleStrings, Toggle}, -}; -use bytes_utils::Str; -use futures::Future; -use rm_send_macros::rm_send_if; -use std::collections::HashMap; - -/// Functions that implement the [client](https://redis.io/commands#connection) interface. -#[rm_send_if(feature = "glommio")] -pub trait ClientInterface: ClientLike + Sized { - /// Return the ID of the current connection. - /// - /// Note: Against a clustered deployment this will return the ID of a random connection. See - /// [connection_ids](Self::connection_ids) for more information. - /// - /// - fn client_id(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::client::client_id(self).await?.convert() } - } - - /// Read the connection IDs for the active connections to each server. - /// - /// The returned map contains each server's `host:port` and the result of calling `CLIENT ID` on the connection. - /// - /// Note: despite being async this function will return cached information from the client if possible. - fn connection_ids(&self) -> impl Future> + Send { - async move { self.inner().backchannel.write().await.connection_ids.clone() } - } - - /// The command returns information and statistics about the current client connection in a mostly human readable - /// format. - /// - /// - fn client_info(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::client::client_info(self).await?.convert() } - } - - /// Close a given connection or set of connections. - /// - /// - fn client_kill(&self, filters: Vec) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::client::client_kill(self, filters).await?.convert() } - } - - /// The CLIENT LIST command returns information and statistics about the client connections server in a mostly human - /// readable format. - /// - /// - fn client_list( - &self, - r#type: Option, - ids: Option>, - ) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::client::client_list(self, r#type, ids).await?.convert() } - } - - /// The CLIENT GETNAME returns the name of the current connection as set by CLIENT SETNAME. - /// - /// - fn client_getname(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::client::client_getname(self).await?.convert() } - } - - /// Assign a name to the current connection. - /// - /// **Note: The client automatically generates a unique name for each client that is shared by all underlying - /// connections. Use `self.id() to read the automatically generated name.** - /// - /// - fn client_setname(&self, name: S) -> impl Future> + Send - where - S: Into + Send, - { - async move { - into!(name); - commands::client::client_setname(self, name).await - } - } - - /// CLIENT PAUSE is a connections control command able to suspend all the Redis clients for the specified amount of - /// time (in milliseconds). - /// - /// - fn client_pause( - &self, - timeout: i64, - mode: Option, - ) -> impl Future> + Send { - async move { commands::client::client_pause(self, timeout, mode).await } - } - - /// CLIENT UNPAUSE is used to resume command processing for all clients that were paused by CLIENT PAUSE. - /// - /// - fn client_unpause(&self) -> impl Future> + Send { - async move { commands::client::client_unpause(self).await } - } - - /// The CLIENT REPLY command controls whether the server will reply the client's commands. The following modes are - /// available: - /// - /// - fn client_reply(&self, flag: ClientReplyFlag) -> impl Future> + Send { - async move { commands::client::client_reply(self, flag).await } - } - - /// This command can unblock, from a different connection, a client blocked in a blocking operation, such as for - /// instance BRPOP or XREAD or WAIT. - /// - /// Note: this command is sent on a backchannel connection and will work even when the main connection is blocked. - /// - /// - fn client_unblock( - &self, - id: S, - flag: Option, - ) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(id); - commands::client::client_unblock(self, id, flag).await?.convert() - } - } - - /// A convenience function to unblock any blocked connection on this client. - fn unblock_self(&self, flag: Option) -> impl Future> + Send { - async move { commands::client::unblock_self(self, flag).await } - } - - /// This command enables the tracking feature of the Redis server that is used for server assisted client side - /// caching. - /// - /// - /// - /// This function is designed to work against a specific server, either via a centralized server config or - /// [with_options](crate::interfaces::ClientLike::with_options). See - /// [crate::interfaces::TrackingInterface::start_tracking] for a version that works with all server deployment - /// modes. - #[cfg(feature = "i-tracking")] - #[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] - fn client_tracking( - &self, - toggle: T, - redirect: Option, - prefixes: P, - bcast: bool, - optin: bool, - optout: bool, - noloop: bool, - ) -> impl Future> + Send - where - R: FromRedis, - T: TryInto + Send, - T::Error: Into + Send, - P: Into + Send, - { - async move { - try_into!(toggle); - into!(prefixes); - commands::tracking::client_tracking(self, toggle, redirect, prefixes, bcast, optin, optout, noloop) - .await? - .convert() - } - } - - /// The command returns information about the current client connection's use of the server assisted client side - /// caching feature. - /// - /// - - #[cfg(feature = "i-tracking")] - #[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] - fn client_trackinginfo(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::tracking::client_trackinginfo(self).await?.convert() } - } - - /// This command returns the client ID we are redirecting our tracking notifications to. - /// - /// - #[cfg(feature = "i-tracking")] - #[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] - fn client_getredir(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::tracking::client_getredir(self).await?.convert() } - } - - /// This command controls the tracking of the keys in the next command executed by the connection, when tracking is - /// enabled in OPTIN or OPTOUT mode. - /// - /// - /// - /// This function is designed to work against a specific server. See - /// [with_options](crate::interfaces::ClientLike::with_options) for a variation that works with all deployment - /// types. - #[cfg(feature = "i-tracking")] - #[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] - fn client_caching(&self, enabled: bool) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::tracking::client_caching(self, enabled).await?.convert() } - } -} diff --git a/src/commands/interfaces/cluster.rs b/src/commands/interfaces/cluster.rs deleted file mode 100644 index 22dd4e7b..00000000 --- a/src/commands/interfaces/cluster.rs +++ /dev/null @@ -1,273 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - protocol::types::ClusterRouting, - types::{ClusterFailoverFlag, ClusterResetFlag, ClusterSetSlotState, FromRedis, MultipleHashSlots, RedisKey}, -}; -use bytes_utils::Str; -use futures::Future; -use rm_send_macros::rm_send_if; - -/// Functions that implement the [cluster](https://redis.io/commands#cluster) interface. -#[rm_send_if(feature = "glommio")] -pub trait ClusterInterface: ClientLike + Sized { - /// Read the cached cluster state used for routing commands to the correct cluster nodes. - fn cached_cluster_state(&self) -> Option { - self.inner().with_cluster_state(|state| Ok(state.clone())).ok() - } - - /// Read the number of known primary cluster nodes, or `0` if the cluster state is not known. - fn num_primary_cluster_nodes(&self) -> usize { - self - .inner() - .with_cluster_state(|state| Ok(state.unique_primary_nodes().len())) - .unwrap_or(0) - } - - /// Update the cached cluster state and add or remove any changed cluster node connections. - fn sync_cluster(&self) -> impl Future> + Send { - async move { commands::cluster::sync_cluster(self).await } - } - - /// Advances the cluster config epoch. - /// - /// - fn cluster_bumpepoch(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::cluster::cluster_bumpepoch(self).await?.convert() } - } - - /// Deletes all slots from a node. - /// - /// - fn cluster_flushslots(&self) -> impl Future> + Send { - async move { commands::cluster::cluster_flushslots(self).await } - } - - /// Returns the node's id. - /// - /// - fn cluster_myid(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::cluster::cluster_myid(self).await?.convert() } - } - - /// Read the current cluster node configuration. - /// - /// Note: The client keeps a cached, parsed version of the cluster state in memory available at - /// [cached_cluster_state](Self::cached_cluster_state). - /// - /// - fn cluster_nodes(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::cluster::cluster_nodes(self).await?.convert() } - } - - /// Forces a node to save the nodes.conf configuration on disk. - /// - /// - fn cluster_saveconfig(&self) -> impl Future> + Send { - async move { commands::cluster::cluster_saveconfig(self).await } - } - - /// CLUSTER SLOTS returns details about which cluster slots map to which Redis instances. - /// - /// - fn cluster_slots(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::cluster::cluster_slots(self).await?.convert() } - } - - /// CLUSTER INFO provides INFO style information about Redis Cluster vital parameters. - /// - /// - fn cluster_info(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::cluster::cluster_info(self).await?.convert() } - } - - /// This command is useful in order to modify a node's view of the cluster configuration. Specifically it assigns a - /// set of hash slots to the node receiving the command. - /// - /// - fn cluster_add_slots(&self, slots: S) -> impl Future> + Send - where - S: Into + Send, - { - async move { - into!(slots); - commands::cluster::cluster_add_slots(self, slots).await - } - } - - /// The command returns the number of failure reports for the specified node. - /// - /// - fn cluster_count_failure_reports(&self, node_id: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(node_id); - commands::cluster::cluster_count_failure_reports(self, node_id) - .await? - .convert() - } - } - - /// Returns the number of keys in the specified Redis Cluster hash slot. - /// - /// - fn cluster_count_keys_in_slot(&self, slot: u16) -> impl Future> + Send - where - R: FromRedis, - { - async move { - commands::cluster::cluster_count_keys_in_slot(self, slot) - .await? - .convert() - } - } - - /// The CLUSTER DELSLOTS command asks a particular Redis Cluster node to forget which master is serving the hash - /// slots specified as arguments. - /// - /// - fn cluster_del_slots(&self, slots: S) -> impl Future> + Send - where - S: Into + Send, - { - async move { - into!(slots); - commands::cluster::cluster_del_slots(self, slots).await - } - } - - /// This command, that can only be sent to a Redis Cluster replica node, forces the replica to start a manual - /// failover of its master instance. - /// - /// - fn cluster_failover(&self, flag: Option) -> impl Future> + Send { - async move { commands::cluster::cluster_failover(self, flag).await } - } - - /// The command is used in order to remove a node, specified via its node ID, from the set of known nodes of the - /// Redis Cluster node receiving the command. In other words the specified node is removed from the nodes table of - /// the node receiving the command. - /// - /// - fn cluster_forget(&self, node_id: S) -> impl Future> + Send - where - S: Into + Send, - { - async move { - into!(node_id); - commands::cluster::cluster_forget(self, node_id).await - } - } - - /// The command returns an array of keys names stored in the contacted node and hashing to the specified hash slot. - /// - /// - fn cluster_get_keys_in_slot(&self, slot: u16, count: u64) -> impl Future> + Send - where - R: FromRedis, - { - async move { - commands::cluster::cluster_get_keys_in_slot(self, slot, count) - .await? - .convert() - } - } - - /// Returns an integer identifying the hash slot the specified key hashes to. - /// - /// - fn cluster_keyslot(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::cluster::cluster_keyslot(self, key).await?.convert() - } - } - - /// CLUSTER MEET is used in order to connect different Redis nodes with cluster support enabled, into a working - /// cluster. - /// - /// - fn cluster_meet(&self, ip: S, port: u16) -> impl Future> + Send - where - S: Into + Send, - { - async move { - into!(ip); - commands::cluster::cluster_meet(self, ip, port).await - } - } - - /// The command reconfigures a node as a replica of the specified master. If the node receiving the command is an - /// empty master, as a side effect of the command, the node role is changed from master to replica. - /// - /// - fn cluster_replicate(&self, node_id: S) -> impl Future> + Send - where - S: Into + Send, - { - async move { - into!(node_id); - commands::cluster::cluster_replicate(self, node_id).await - } - } - - /// The command provides a list of replica nodes replicating from the specified master node. - /// - /// - fn cluster_replicas(&self, node_id: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(node_id); - commands::cluster::cluster_replicas(self, node_id).await?.convert() - } - } - - /// Reset a Redis Cluster node, in a more or less drastic way depending on the reset type, that can be hard or soft. - /// Note that this command does not work for masters if they hold one or more keys, in that case to completely - /// reset a master node keys must be removed first, e.g. by using FLUSHALL first, and then CLUSTER RESET. - /// - /// - fn cluster_reset(&self, mode: Option) -> impl Future> + Send { - async move { commands::cluster::cluster_reset(self, mode).await } - } - - /// This command sets a specific config epoch in a fresh node. - /// - /// - fn cluster_set_config_epoch(&self, epoch: u64) -> impl Future> + Send { - async move { commands::cluster::cluster_set_config_epoch(self, epoch).await } - } - - /// CLUSTER SETSLOT is responsible for changing the state of a hash slot in the receiving node in different ways. - /// - /// - fn cluster_setslot(&self, slot: u16, state: ClusterSetSlotState) -> impl Future> + Send { - async move { commands::cluster::cluster_setslot(self, slot, state).await } - } -} diff --git a/src/commands/interfaces/config.rs b/src/commands/interfaces/config.rs deleted file mode 100644 index f67846b3..00000000 --- a/src/commands/interfaces/config.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - types::{FromRedis, RedisValue}, -}; -use bytes_utils::Str; -use futures::Future; -use rm_send_macros::rm_send_if; -use std::convert::TryInto; - -/// Functions that implement the [config](https://redis.io/commands#server) interface. -#[rm_send_if(feature = "glommio")] -pub trait ConfigInterface: ClientLike + Sized { - /// Resets the statistics reported by Redis using the INFO command. - /// - /// - fn config_resetstat(&self) -> impl Future> + Send { - async move { commands::config::config_resetstat(self).await } - } - - /// The CONFIG REWRITE command rewrites the redis.conf file the server was started with, applying the minimal - /// changes needed to make it reflect the configuration currently used by the server, which may be different - /// compared to the original one because of the use of the CONFIG SET command. - /// - /// - fn config_rewrite(&self) -> impl Future> + Send { - async move { commands::config::config_rewrite(self).await } - } - - /// The CONFIG GET command is used to read the configuration parameters of a running Redis server. - /// - /// - fn config_get(&self, parameter: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(parameter); - commands::config::config_get(self, parameter).await?.convert() - } - } - - /// The CONFIG SET command is used in order to reconfigure the server at run time without the need to restart Redis. - /// - /// - fn config_set(&self, parameter: P, value: V) -> impl Future> + Send - where - P: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(parameter); - try_into!(value); - commands::config::config_set(self, parameter, value).await - } - } -} diff --git a/src/commands/interfaces/geo.rs b/src/commands/interfaces/geo.rs deleted file mode 100644 index d0178a1e..00000000 --- a/src/commands/interfaces/geo.rs +++ /dev/null @@ -1,271 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - types::{ - Any, - FromRedis, - GeoPosition, - GeoUnit, - MultipleGeoValues, - MultipleValues, - RedisKey, - RedisValue, - SetOptions, - SortOrder, - }, -}; -use futures::Future; -use rm_send_macros::rm_send_if; -use std::convert::TryInto; - -/// Functions that implement the [geo](https://redis.io/commands#geo) interface. -#[rm_send_if(feature = "glommio")] -pub trait GeoInterface: ClientLike + Sized { - /// Adds the specified geospatial items (longitude, latitude, name) to the specified key. - /// - /// - fn geoadd( - &self, - key: K, - options: Option, - changed: bool, - values: V, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: Into + Send, - { - async move { - into!(key, values); - commands::geo::geoadd(self, key, options, changed, values) - .await? - .convert() - } - } - - /// Return valid Geohash strings representing the position of one or more elements in a sorted set value - /// representing a geospatial index (where elements were added using GEOADD). - /// - /// - fn geohash(&self, key: K, members: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(members); - commands::geo::geohash(self, key, members).await?.convert() - } - } - - /// Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by - /// the sorted set at key. - /// - /// Callers can use [as_geo_position](crate::types::RedisValue::as_geo_position) to lazily parse results as needed. - /// - /// - fn geopos(&self, key: K, members: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(members); - commands::geo::geopos(self, key, members).await?.convert() - } - } - - /// Return the distance between two members in the geospatial index represented by the sorted set. - /// - /// - fn geodist( - &self, - key: K, - src: S, - dest: D, - unit: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - S: TryInto + Send, - S::Error: Into + Send, - D: TryInto + Send, - D::Error: Into + Send, - { - async move { - into!(key); - try_into!(src, dest); - commands::geo::geodist(self, key, src, dest, unit).await?.convert() - } - } - - /// Return the members of a sorted set populated with geospatial information using GEOADD, which are within the - /// borders of the area specified with the center location and the maximum distance from the center (the radius). - /// - /// - fn georadius( - &self, - key: K, - position: P, - radius: f64, - unit: GeoUnit, - withcoord: bool, - withdist: bool, - withhash: bool, - count: Option<(u64, Any)>, - ord: Option, - store: Option, - storedist: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key, position); - commands::geo::georadius( - self, key, position, radius, unit, withcoord, withdist, withhash, count, ord, store, storedist, - ) - .await? - .convert() - } - } - - /// This command is exactly like GEORADIUS with the sole difference that instead of taking, as the center of the - /// area to query, a longitude and latitude value, it takes the name of a member already existing inside the - /// geospatial index represented by the sorted set. - /// - /// - fn georadiusbymember( - &self, - key: K, - member: V, - radius: f64, - unit: GeoUnit, - withcoord: bool, - withdist: bool, - withhash: bool, - count: Option<(u64, Any)>, - ord: Option, - store: Option, - storedist: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(member); - commands::geo::georadiusbymember( - self, - key, - to!(member)?, - radius, - unit, - withcoord, - withdist, - withhash, - count, - ord, - store, - storedist, - ) - .await? - .convert() - } - } - - /// Return the members of a sorted set populated with geospatial information using GEOADD, which are within the - /// borders of the area specified by a given shape. - /// - /// - fn geosearch( - &self, - key: K, - from_member: Option, - from_lonlat: Option, - by_radius: Option<(f64, GeoUnit)>, - by_box: Option<(f64, f64, GeoUnit)>, - ord: Option, - count: Option<(u64, Any)>, - withcoord: bool, - withdist: bool, - withhash: bool, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::geo::geosearch( - self, - key, - from_member, - from_lonlat, - by_radius, - by_box, - ord, - count, - withcoord, - withdist, - withhash, - ) - .await? - .convert() - } - } - - /// This command is like GEOSEARCH, but stores the result in destination key. Returns the number of members added to - /// the destination key. - /// - /// - fn geosearchstore( - &self, - dest: D, - source: S, - from_member: Option, - from_lonlat: Option, - by_radius: Option<(f64, GeoUnit)>, - by_box: Option<(f64, f64, GeoUnit)>, - ord: Option, - count: Option<(u64, Any)>, - storedist: bool, - ) -> impl Future> + Send - where - R: FromRedis, - D: Into + Send, - S: Into + Send, - { - async move { - into!(dest, source); - commands::geo::geosearchstore( - self, - dest, - source, - from_member, - from_lonlat, - by_radius, - by_box, - ord, - count, - storedist, - ) - .await? - .convert() - } - } -} diff --git a/src/commands/interfaces/hashes.rs b/src/commands/interfaces/hashes.rs deleted file mode 100644 index ed068dca..00000000 --- a/src/commands/interfaces/hashes.rs +++ /dev/null @@ -1,245 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - types::{FromRedis, MultipleKeys, RedisKey, RedisMap, RedisValue}, -}; -use futures::Future; -use rm_send_macros::rm_send_if; -use std::convert::TryInto; - -/// Functions that implement the [hashes](https://redis.io/commands#hashes) interface. -#[rm_send_if(feature = "glommio")] -pub trait HashesInterface: ClientLike + Sized { - /// Returns all fields and values of the hash stored at `key`. - /// - /// - fn hgetall(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::hashes::hgetall(self, key).await?.convert() - } - } - - /// Removes the specified fields from the hash stored at `key`. - /// - /// - fn hdel(&self, key: K, fields: F) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - F: Into + Send, - { - async move { - into!(key, fields); - commands::hashes::hdel(self, key, fields).await?.convert() - } - } - - /// Returns if `field` is an existing field in the hash stored at `key`. - /// - /// - fn hexists(&self, key: K, field: F) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - F: Into + Send, - { - async move { - into!(key, field); - commands::hashes::hexists(self, key, field).await?.convert() - } - } - - /// Returns the value associated with `field` in the hash stored at `key`. - /// - /// - fn hget(&self, key: K, field: F) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - F: Into + Send, - { - async move { - into!(key, field); - commands::hashes::hget(self, key, field).await?.convert() - } - } - - /// Increments the number stored at `field` in the hash stored at `key` by `increment`. - /// - /// - fn hincrby(&self, key: K, field: F, increment: i64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - F: Into + Send, - { - async move { - into!(key, field); - commands::hashes::hincrby(self, key, field, increment).await?.convert() - } - } - - /// Increment the specified `field` of a hash stored at `key`, and representing a floating point number, by the - /// specified `increment`. - /// - /// - fn hincrbyfloat(&self, key: K, field: F, increment: f64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - F: Into + Send, - { - async move { - into!(key, field); - commands::hashes::hincrbyfloat(self, key, field, increment) - .await? - .convert() - } - } - - /// Returns all field names in the hash stored at `key`. - /// - /// - fn hkeys(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::hashes::hkeys(self, key).await?.convert() - } - } - - /// Returns the number of fields contained in the hash stored at `key`. - /// - /// - fn hlen(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::hashes::hlen(self, key).await?.convert() - } - } - - /// Returns the values associated with the specified `fields` in the hash stored at `key`. - /// - /// - fn hmget(&self, key: K, fields: F) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - F: Into + Send, - { - async move { - into!(key, fields); - commands::hashes::hmget(self, key, fields).await?.convert() - } - } - - /// Sets the specified fields to their respective values in the hash stored at `key`. - /// - /// - fn hmset(&self, key: K, values: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(values); - commands::hashes::hmset(self, key, values).await?.convert() - } - } - - /// Sets fields in the hash stored at `key` to their provided values. - /// - /// - fn hset(&self, key: K, values: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(values); - commands::hashes::hset(self, key, values).await?.convert() - } - } - - /// Sets `field` in the hash stored at `key` to `value`, only if `field` does not yet exist. - /// - /// - fn hsetnx(&self, key: K, field: F, value: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - F: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key, field); - try_into!(value); - commands::hashes::hsetnx(self, key, field, value).await?.convert() - } - } - - /// When called with just the `key` argument, return a random field from the hash value stored at `key`. - /// - /// If the provided `count` argument is positive, return an array of distinct fields. - /// - /// - fn hrandfield(&self, key: K, count: Option<(i64, bool)>) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::hashes::hrandfield(self, key, count).await?.convert() - } - } - - /// Returns the string length of the value associated with `field` in the hash stored at `key`. - /// - /// - fn hstrlen(&self, key: K, field: F) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - F: Into + Send, - { - async move { - into!(key, field); - commands::hashes::hstrlen(self, key, field).await?.convert() - } - } - - /// Returns all values in the hash stored at `key`. - /// - /// - fn hvals(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::hashes::hvals(self, key).await?.convert() - } - } -} diff --git a/src/commands/interfaces/hyperloglog.rs b/src/commands/interfaces/hyperloglog.rs deleted file mode 100644 index 3f7cd695..00000000 --- a/src/commands/interfaces/hyperloglog.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - types::{FromRedis, MultipleKeys, MultipleValues, RedisKey}, -}; -use futures::Future; -use rm_send_macros::rm_send_if; -use std::convert::TryInto; - -/// Functions that implement the [HyperLogLog](https://redis.io/commands#hyperloglog) interface. -#[rm_send_if(feature = "glommio")] -pub trait HyperloglogInterface: ClientLike + Sized { - /// Adds all the element arguments to the HyperLogLog data structure stored at the variable name specified as first - /// argument. - /// - /// - fn pfadd(&self, key: K, elements: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(elements); - commands::hyperloglog::pfadd(self, key, elements).await?.convert() - } - } - - /// When called with a single key, returns the approximated cardinality computed by the HyperLogLog data structure - /// stored at the specified variable, which is 0 if the variable does not exist. - /// - /// When called with multiple keys, returns the approximated cardinality of the union of the HyperLogLogs passed, by - /// internally merging the HyperLogLogs stored at the provided keys into a temporary HyperLogLog. - /// - /// - fn pfcount(&self, keys: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::hyperloglog::pfcount(self, keys).await?.convert() - } - } - - /// Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of the - /// observed sets of the source HyperLogLog structures. - /// - /// - fn pfmerge(&self, dest: D, sources: S) -> impl Future> + Send - where - R: FromRedis, - D: Into + Send, - S: Into + Send, - { - async move { - into!(dest, sources); - commands::hyperloglog::pfmerge(self, dest, sources).await?.convert() - } - } -} diff --git a/src/commands/interfaces/keys.rs b/src/commands/interfaces/keys.rs deleted file mode 100644 index b148ac4e..00000000 --- a/src/commands/interfaces/keys.rs +++ /dev/null @@ -1,601 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - types::{Expiration, ExpireOptions, FromRedis, MultipleKeys, RedisKey, RedisMap, RedisValue, SetOptions}, -}; -use futures::Future; -use rm_send_macros::rm_send_if; -use std::convert::TryInto; - -/// Functions that implement the generic [keys](https://redis.io/commands#generic) interface. -#[rm_send_if(feature = "glommio")] -pub trait KeysInterface: ClientLike + Sized { - /// Marks the given keys to be watched for conditional execution of a transaction. - /// - /// - fn watch(&self, keys: K) -> impl Future> + Send - where - K: Into + Send, - { - async move { - into!(keys); - commands::keys::watch(self, keys).await - } - } - - /// Flushes all the previously watched keys for a transaction. - /// - /// - fn unwatch(&self) -> impl Future> + Send { - async move { commands::keys::unwatch(self).await } - } - - /// Return a random key from the currently selected database. - /// - /// - fn randomkey(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::keys::randomkey(self).await?.convert() } - } - - /// This command copies the value stored at the source key to the destination key. - /// - /// - fn copy( - &self, - source: S, - destination: D, - db: Option, - replace: bool, - ) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - D: Into + Send, - { - async move { - into!(source, destination); - commands::keys::copy(self, source, destination, db, replace) - .await? - .convert() - } - } - - /// Serialize the value stored at `key` in a Redis-specific format and return it as bulk string. - /// - /// - fn dump(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::dump(self, key).await?.convert() - } - } - - /// Create a key associated with a value that is obtained by deserializing the provided serialized value - /// - /// - fn restore( - &self, - key: K, - ttl: i64, - serialized: RedisValue, - replace: bool, - absttl: bool, - idletime: Option, - frequency: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::restore(self, key, ttl, serialized, replace, absttl, idletime, frequency) - .await? - .convert() - } - } - - /// Set a value with optional NX|XX, EX|PX|EXAT|PXAT|KEEPTTL, and GET arguments. - /// - /// Note: the `get` flag was added in 6.2.0. Setting it as `false` works with Redis versions <=6.2.0. - /// - /// - fn set( - &self, - key: K, - value: V, - expire: Option, - options: Option, - get: bool, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(value); - commands::keys::set(self, key, value, expire, options, get) - .await? - .convert() - } - } - - /// Read a value from the server. - /// - /// - fn get(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::get(self, key).await?.convert() - } - } - - /// Returns the substring of the string value stored at `key` with offsets `start` and `end` (both inclusive). - /// - /// Note: Command formerly called SUBSTR in Redis verison <=2.0. - /// - /// - fn getrange(&self, key: K, start: usize, end: usize) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::getrange(self, key, start, end).await?.convert() - } - } - - /// Overwrites part of the string stored at `key`, starting at the specified `offset`, for the entire length of - /// `value`. - /// - /// - fn setrange(&self, key: K, offset: u32, value: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(value); - commands::keys::setrange(self, key, offset, value).await?.convert() - } - } - - /// Atomically sets `key` to `value` and returns the old value stored at `key`. - /// - /// Returns an error if `key` does not hold string value. Returns nil if `key` does not exist. - /// - /// - fn getset(&self, key: K, value: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(value); - commands::keys::getset(self, key, value).await?.convert() - } - } - - /// Get the value of key and delete the key. This command is similar to GET, except for the fact that it also - /// deletes the key on success (if and only if the key's value type is a string). - /// - /// - fn getdel(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::getdel(self, key).await?.convert() - } - } - - /// Returns the length of the string value stored at key. An error is returned when key holds a non-string value. - /// - /// - fn strlen(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::strlen(self, key).await?.convert() - } - } - - /// Removes the specified keys. A key is ignored if it does not exist. - /// - /// Returns the number of keys removed. - /// - /// - fn del(&self, keys: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::keys::del(self, keys).await?.convert() - } - } - - /// Unlinks the specified keys. A key is ignored if it does not exist - /// - /// Returns the number of keys removed. - /// - /// - fn unlink(&self, keys: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::keys::unlink(self, keys).await?.convert() - } - } - - /// Renames `source` key to `destination`. - /// - /// Returns an error when `source` does not exist. If `destination` exists, it gets overwritten. - /// - /// - fn rename(&self, source: S, destination: D) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - D: Into + Send, - { - async move { - into!(source); - into!(destination); - commands::keys::rename(self, source, destination).await?.convert() - } - } - - /// Renames `source` key to `destination` if `destination` does not yet exist. - /// - /// Returns an error when `source` does not exist. - /// - /// - fn renamenx(&self, source: S, destination: D) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - D: Into + Send, - { - async move { - into!(source); - into!(destination); - commands::keys::renamenx(self, source, destination).await?.convert() - } - } - - /// Append `value` to `key` if it's a string. - /// - /// - fn append(&self, key: K, value: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(value); - commands::keys::append(self, key, value).await?.convert() - } - } - - /// Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the - /// special value nil is returned. - /// - /// - fn mget(&self, keys: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::keys::mget(self, keys).await?.convert() - } - } - - /// Sets the given keys to their respective values. - /// - /// - fn mset(&self, values: V) -> impl Future> + Send - where - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - try_into!(values); - commands::keys::mset(self, values).await?.convert() - } - } - - /// Sets the given keys to their respective values. MSETNX will not perform any operation at all even if just a - /// single key already exists. - /// - /// - fn msetnx(&self, values: V) -> impl Future> + Send - where - R: FromRedis, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - try_into!(values); - commands::keys::msetnx(self, values).await?.convert() - } - } - - /// Increments the number stored at `key` by one. If the key does not exist, it is set to 0 before performing the - /// operation. - /// - /// Returns an error if the value at key is of the wrong type. - /// - /// - fn incr(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::incr(self, key).await?.convert() - } - } - - /// Increments the number stored at `key` by `val`. If the key does not exist, it is set to 0 before performing the - /// operation. - /// - /// Returns an error if the value at key is of the wrong type. - /// - /// - fn incr_by(&self, key: K, val: i64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::incr_by(self, key, val).await?.convert() - } - } - - /// Increment the string representing a floating point number stored at key by `val`. If the key does not exist, it - /// is set to 0 before performing the operation. - /// - /// Returns an error if key value is the wrong type or if the current value cannot be parsed as a floating point - /// value. - /// - /// - fn incr_by_float(&self, key: K, val: f64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::incr_by_float(self, key, val).await?.convert() - } - } - - /// Decrements the number stored at `key` by one. If the key does not exist, it is set to 0 before performing the - /// operation. - /// - /// Returns an error if the key contains a value of the wrong type. - /// - /// - fn decr(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::decr(self, key).await?.convert() - } - } - - /// Decrements the number stored at `key` by `val`. If the key does not exist, it is set to 0 before performing the - /// operation. - /// - /// Returns an error if the key contains a value of the wrong type. - /// - /// - fn decr_by(&self, key: K, val: i64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::decr_by(self, key, val).await?.convert() - } - } - - /// Returns the remaining time to live of a key that has a timeout, in seconds. - /// - /// - fn ttl(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::ttl(self, key).await?.convert() - } - } - - /// Returns the remaining time to live of a key that has a timeout, in milliseconds. - /// - /// - fn pttl(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::pttl(self, key).await?.convert() - } - } - - /// Remove the existing timeout on a key, turning the key from volatile (a key with an expiration) - /// to persistent (a key that will never expire as no timeout is associated). - /// - /// Returns a boolean value describing whether the timeout was removed. - /// - /// - fn persist(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::persist(self, key).await?.convert() - } - } - - /// Set a timeout on key. After the timeout has expired, the key will be automatically deleted. - /// - /// - fn expire(&self, key: K, seconds: i64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::expire(self, key, seconds).await?.convert() - } - } - - /// Set a timeout on a key based on a UNIX timestamp. - /// - /// - fn expire_at(&self, key: K, timestamp: i64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::expire_at(self, key, timestamp).await?.convert() - } - } - - /// This command works exactly like EXPIRE but the time to live of the key is specified in milliseconds instead of - /// seconds. - /// - /// - fn pexpire( - &self, - key: K, - milliseconds: i64, - options: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::pexpire(self, key, milliseconds, options) - .await? - .convert() - } - } - - /// PEXPIREAT has the same effect and semantic as EXPIREAT, but the Unix time at which the key will expire is - /// specified in milliseconds instead of seconds. - /// - /// - fn pexpire_at( - &self, - key: K, - timestamp: i64, - options: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::keys::pexpire_at(self, key, timestamp, options) - .await? - .convert() - } - } - - /// Returns number of keys that exist from the `keys` arguments. - /// - /// - fn exists(&self, keys: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::keys::exists(self, keys).await?.convert() - } - } - - /// Runs the longest common subsequence algorithm on two keys. - /// - /// - fn lcs( - &self, - key1: K1, - key2: K2, - len: bool, - idx: bool, - minmatchlen: Option, - withmatchlen: bool, - ) -> impl Future> + Send - where - R: FromRedis, - K1: Into + Send, - K2: Into + Send, - { - async move { - into!(key1, key2); - commands::keys::lcs(self, key1, key2, len, idx, minmatchlen, withmatchlen) - .await? - .convert() - } - } -} diff --git a/src/commands/interfaces/lists.rs b/src/commands/interfaces/lists.rs deleted file mode 100644 index 4d50b716..00000000 --- a/src/commands/interfaces/lists.rs +++ /dev/null @@ -1,483 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - types::{ - FromRedis, - LMoveDirection, - Limit, - ListLocation, - MultipleKeys, - MultipleStrings, - MultipleValues, - RedisKey, - RedisValue, - SortOrder, - }, -}; -use bytes_utils::Str; -use futures::Future; -use rm_send_macros::rm_send_if; -use std::convert::TryInto; - -/// Functions that implement the [lists](https://redis.io/commands#lists) interface. -#[rm_send_if(feature = "glommio")] -pub trait ListInterface: ClientLike + Sized { - /// The blocking variant of [Self::lmpop]. - /// - /// - fn blmpop( - &self, - timeout: f64, - keys: K, - direction: LMoveDirection, - count: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::lists::blmpop(self, timeout, keys, direction, count) - .await? - .convert() - } - } - - /// BLPOP is a blocking list pop primitive. It is the blocking version of LPOP because it blocks the connection when - /// there are no elements to pop from any of the given lists. An element is popped from the head of the first list - /// that is non-empty, with the given keys being checked in the order that they are given. - /// - /// - fn blpop(&self, keys: K, timeout: f64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::lists::blpop(self, keys, timeout).await?.convert() - } - } - - /// BRPOP is a blocking list pop primitive. It is the blocking version of RPOP because it blocks the connection when - /// there are no elements to pop from any of the given lists. An element is popped from the tail of the first list - /// that is non-empty, with the given keys being checked in the order that they are given. - /// - /// - fn brpop(&self, keys: K, timeout: f64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::lists::brpop(self, keys, timeout).await?.convert() - } - } - - /// The blocking equivalent of [Self::rpoplpush]. - /// - /// - fn brpoplpush( - &self, - source: S, - destination: D, - timeout: f64, - ) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - D: Into + Send, - { - async move { - into!(source, destination); - commands::lists::brpoplpush(self, source, destination, timeout) - .await? - .convert() - } - } - - /// The blocking equivalent of [Self::lmove]. - /// - /// - fn blmove( - &self, - source: S, - destination: D, - source_direction: LMoveDirection, - destination_direction: LMoveDirection, - timeout: f64, - ) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - D: Into + Send, - { - async move { - into!(source, destination); - commands::lists::blmove( - self, - source, - destination, - source_direction, - destination_direction, - timeout, - ) - .await? - .convert() - } - } - - /// Pops one or more elements from the first non-empty list key from the list of provided key names. - /// - /// - fn lmpop( - &self, - keys: K, - direction: LMoveDirection, - count: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::lists::lmpop(self, keys, direction, count).await?.convert() - } - } - - /// Returns the element at index in the list stored at key. - /// - /// - fn lindex(&self, key: K, index: i64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::lists::lindex(self, key, index).await?.convert() - } - } - - /// Inserts element in the list stored at key either before or after the reference value `pivot`. - /// - /// - fn linsert( - &self, - key: K, - location: ListLocation, - pivot: P, - element: V, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: TryInto + Send, - P::Error: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(pivot, element); - commands::lists::linsert(self, key, location, pivot, element) - .await? - .convert() - } - } - - /// Returns the length of the list stored at key. - /// - /// - fn llen(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::lists::llen(self, key).await?.convert() - } - } - - /// Removes and returns the first elements of the list stored at key. - /// - /// - fn lpop(&self, key: K, count: Option) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::lists::lpop(self, key, count).await?.convert() - } - } - - /// The command returns the index of matching elements inside a Redis list. - /// - /// - fn lpos( - &self, - key: K, - element: V, - rank: Option, - count: Option, - maxlen: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(element); - commands::lists::lpos(self, key, element, rank, count, maxlen) - .await? - .convert() - } - } - - /// Insert all the specified values at the head of the list stored at `key`. - /// - /// - fn lpush(&self, key: K, elements: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(elements); - commands::lists::lpush(self, key, elements).await?.convert() - } - } - - /// Inserts specified values at the head of the list stored at `key`, only if `key` already exists and holds a list. - /// - /// - fn lpushx(&self, key: K, elements: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(elements); - commands::lists::lpushx(self, key, elements).await?.convert() - } - } - - /// Returns the specified elements of the list stored at `key`. - /// - /// - fn lrange(&self, key: K, start: i64, stop: i64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::lists::lrange(self, key, start, stop).await?.convert() - } - } - - /// Removes the first `count` occurrences of elements equal to `element` from the list stored at `key`. - /// - /// - fn lrem(&self, key: K, count: i64, element: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(element); - commands::lists::lrem(self, key, count, element).await?.convert() - } - } - - /// Sets the list element at `index` to `element`. - /// - /// - fn lset(&self, key: K, index: i64, element: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(element); - commands::lists::lset(self, key, index, element).await?.convert() - } - } - - /// Trim an existing list so that it will contain only the specified range of elements specified. - /// - /// - fn ltrim(&self, key: K, start: i64, stop: i64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::lists::ltrim(self, key, start, stop).await?.convert() - } - } - - /// Removes and returns the last elements of the list stored at `key`. - /// - /// - fn rpop(&self, key: K, count: Option) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::lists::rpop(self, key, count).await?.convert() - } - } - - /// Atomically returns and removes the last element (tail) of the list stored at `source`, and pushes the element at - /// the first element (head) of the list stored at `destination`. - /// - /// - fn rpoplpush(&self, source: S, dest: D) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - D: Into + Send, - { - async move { - into!(source, dest); - commands::lists::rpoplpush(self, source, dest).await?.convert() - } - } - - /// Atomically returns and removes the first/last element (head/tail depending on the source direction argument) of - /// the list stored at `source`, and pushes the element at the first/last element (head/tail depending on the - /// destination direction argument) of the list stored at `destination`. - /// - /// - fn lmove( - &self, - source: S, - dest: D, - source_direction: LMoveDirection, - dest_direction: LMoveDirection, - ) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - D: Into + Send, - { - async move { - into!(source, dest); - commands::lists::lmove(self, source, dest, source_direction, dest_direction) - .await? - .convert() - } - } - - /// Insert all the specified values at the tail of the list stored at `key`. - /// - /// - fn rpush(&self, key: K, elements: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(elements); - commands::lists::rpush(self, key, elements).await?.convert() - } - } - - /// Inserts specified values at the tail of the list stored at `key`, only if key already exists and holds a list. - /// - /// - fn rpushx(&self, key: K, elements: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(elements); - commands::lists::rpushx(self, key, elements).await?.convert() - } - } - - /// Returns or stores the elements contained in the list, set or sorted set at `key`. - /// - /// - fn sort( - &self, - key: K, - by: Option, - limit: Option, - get: S, - order: Option, - alpha: bool, - store: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - S: Into + Send, - { - async move { - into!(key, get); - commands::lists::sort(self, key, by, limit, get, order, alpha, store) - .await? - .convert() - } - } - - /// Read-only variant of the SORT command. It is exactly like the original SORT but refuses the STORE option and can - /// safely be used in read-only replicas. - /// - /// - fn sort_ro( - &self, - key: K, - by: Option, - limit: Option, - get: S, - order: Option, - alpha: bool, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - S: Into + Send, - { - async move { - into!(key, get); - commands::lists::sort_ro(self, key, by, limit, get, order, alpha) - .await? - .convert() - } - } -} diff --git a/src/commands/interfaces/lua.rs b/src/commands/interfaces/lua.rs deleted file mode 100644 index ba63cd42..00000000 --- a/src/commands/interfaces/lua.rs +++ /dev/null @@ -1,344 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - types::{FnPolicy, FromRedis, MultipleKeys, MultipleStrings, MultipleValues, ScriptDebugFlag}, -}; -use bytes::Bytes; -use bytes_utils::Str; -use futures::Future; -use rm_send_macros::rm_send_if; -use std::convert::TryInto; - -/// Functions that implement the [lua](https://redis.io/commands#lua) interface. -#[rm_send_if(feature = "glommio")] -pub trait LuaInterface: ClientLike + Sized { - /// Load a script into the scripts cache, without executing it. After the specified command is loaded into the - /// script cache it will be callable using EVALSHA with the correct SHA1 digest of the script. - /// - /// Returns the SHA-1 hash of the script. - /// - /// - fn script_load(&self, script: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(script); - commands::lua::script_load(self, script).await?.convert() - } - } - - /// A clustered variant of [script_load](Self::script_load) that loads the script on all primary nodes in a cluster. - /// - /// Returns the SHA-1 hash of the script. - #[cfg(feature = "sha-1")] - #[cfg_attr(docsrs, doc(cfg(feature = "sha-1")))] - fn script_load_cluster(&self, script: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(script); - commands::lua::script_load_cluster(self, script).await?.convert() - } - } - - /// Kills the currently executing Lua script, assuming no write operation was yet performed by the script. - /// - /// - fn script_kill(&self) -> impl Future> + Send { - async move { commands::lua::script_kill(self).await } - } - - /// A clustered variant of the [script_kill](Self::script_kill) command that issues the command to all primary nodes - /// in the cluster. - fn script_kill_cluster(&self) -> impl Future> + Send { - async move { commands::lua::script_kill_cluster(self).await } - } - - /// Flush the Lua scripts cache. - /// - /// - fn script_flush(&self, r#async: bool) -> impl Future> + Send { - async move { commands::lua::script_flush(self, r#async).await } - } - - /// A clustered variant of [script_flush](Self::script_flush) that flushes the script cache on all primary nodes in - /// the cluster. - fn script_flush_cluster(&self, r#async: bool) -> impl Future> + Send { - async move { commands::lua::script_flush_cluster(self, r#async).await } - } - - /// Returns information about the existence of the scripts in the script cache. - /// - /// - fn script_exists(&self, hashes: H) -> impl Future> + Send - where - R: FromRedis, - H: Into + Send, - { - async move { - into!(hashes); - commands::lua::script_exists(self, hashes).await?.convert() - } - } - - /// Set the debug mode for subsequent scripts executed with EVAL. - /// - /// - fn script_debug(&self, flag: ScriptDebugFlag) -> impl Future> + Send { - async move { commands::lua::script_debug(self, flag).await } - } - - /// Evaluates a script cached on the server side by its SHA1 digest. - /// - /// - /// - /// **Note: Use `None` to represent an empty set of keys or args.** - fn evalsha(&self, hash: S, keys: K, args: V) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(hash, keys); - try_into!(args); - commands::lua::evalsha(self, hash, keys, args).await?.convert() - } - } - - /// Evaluate a Lua script on the server. - /// - /// - /// - /// **Note: Use `None` to represent an empty set of keys or args.** - fn eval(&self, script: S, keys: K, args: V) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(script, keys); - try_into!(args); - commands::lua::eval(self, script, keys, args).await?.convert() - } - } -} - -/// Functions that implement the [function](https://redis.io/docs/manual/programmability/functions-intro/) interface. -#[rm_send_if(feature = "glommio")] -pub trait FunctionInterface: ClientLike + Sized { - /// Invoke a function. - /// - /// - fn fcall(&self, func: F, keys: K, args: V) -> impl Future> + Send - where - R: FromRedis, - F: Into + Send, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(func); - try_into!(keys, args); - commands::lua::fcall(self, func, keys, args).await?.convert() - } - } - - /// This is a read-only variant of the FCALL command that cannot execute commands that modify data. - /// - /// - fn fcall_ro(&self, func: F, keys: K, args: V) -> impl Future> + Send - where - R: FromRedis, - F: Into + Send, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(func); - try_into!(keys, args); - commands::lua::fcall_ro(self, func, keys, args).await?.convert() - } - } - - /// Delete a library and all its functions. - /// - /// - fn function_delete(&self, library_name: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(library_name); - commands::lua::function_delete(self, library_name).await?.convert() - } - } - - /// Delete a library and all its functions from each cluster node concurrently. - /// - /// - fn function_delete_cluster(&self, library_name: S) -> impl Future> + Send - where - S: Into + Send, - { - async move { - into!(library_name); - commands::lua::function_delete_cluster(self, library_name).await - } - } - - /// Return the serialized payload of loaded libraries. - /// - /// - fn function_dump(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::lua::function_dump(self).await?.convert() } - } - - /// Deletes all the libraries. - /// - /// - fn function_flush(&self, r#async: bool) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::lua::function_flush(self, r#async).await?.convert() } - } - - /// Deletes all the libraries on all cluster nodes concurrently. - /// - /// - fn function_flush_cluster(&self, r#async: bool) -> impl Future> + Send { - async move { commands::lua::function_flush_cluster(self, r#async).await } - } - - /// Kill a function that is currently executing. - /// - /// Note: This command runs on a backchannel connection to the server in order to take effect as quickly as - /// possible. - /// - /// - fn function_kill(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::lua::function_kill(self).await?.convert() } - } - - /// Return information about the functions and libraries. - /// - /// - fn function_list( - &self, - library_name: Option, - withcode: bool, - ) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - let library_name = library_name.map(|l| l.into()); - commands::lua::function_list(self, library_name, withcode) - .await? - .convert() - } - } - - /// Load a library to Redis. - /// - /// - fn function_load(&self, replace: bool, code: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(code); - commands::lua::function_load(self, replace, code).await?.convert() - } - } - - /// Load a library to Redis on all cluster nodes concurrently. - /// - /// - fn function_load_cluster(&self, replace: bool, code: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(code); - commands::lua::function_load_cluster(self, replace, code) - .await? - .convert() - } - } - - /// Restore libraries from the serialized payload. - /// - /// - /// - /// Note: Use `FnPolicy::default()` to use the default function restore policy (`"APPEND"`). - fn function_restore(&self, serialized: B, policy: P) -> impl Future> + Send - where - R: FromRedis, - B: Into + Send, - P: TryInto + Send, - P::Error: Into + Send, - { - async move { - into!(serialized); - try_into!(policy); - commands::lua::function_restore(self, serialized, policy) - .await? - .convert() - } - } - - /// Restore libraries from the serialized payload on all cluster nodes concurrently. - /// - /// - /// - /// Note: Use `FnPolicy::default()` to use the default function restore policy (`"APPEND"`). - fn function_restore_cluster(&self, serialized: B, policy: P) -> impl Future> + Send - where - B: Into + Send, - P: TryInto + Send, - P::Error: Into + Send, - { - async move { - into!(serialized); - try_into!(policy); - commands::lua::function_restore_cluster(self, serialized, policy).await - } - } - - /// Return information about the function that's currently running and information about the available execution - /// engines. - /// - /// Note: This command runs on a backchannel connection to the server. - /// - /// - fn function_stats(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::lua::function_stats(self).await?.convert() } - } -} diff --git a/src/commands/interfaces/memory.rs b/src/commands/interfaces/memory.rs deleted file mode 100644 index 078d6676..00000000 --- a/src/commands/interfaces/memory.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::{ - commands, - interfaces::{ClientLike, RedisResult}, - prelude::FromRedis, - types::RedisKey, -}; -use futures::Future; -use rm_send_macros::rm_send_if; - -/// Functions that implement the [memory](https://redis.io/commands#server) interface. -#[rm_send_if(feature = "glommio")] -pub trait MemoryInterface: ClientLike + Sized { - /// The MEMORY DOCTOR command reports about different memory-related issues that the Redis server experiences, and - /// advises about possible remedies. - /// - /// - fn memory_doctor(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::memory::memory_doctor(self).await?.convert() } - } - - /// The MEMORY MALLOC-STATS command provides an internal statistics report from the memory allocator. - /// - /// - fn memory_malloc_stats(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::memory::memory_malloc_stats(self).await?.convert() } - } - - /// The MEMORY PURGE command attempts to purge dirty pages so these can be reclaimed by the allocator. - /// - /// - fn memory_purge(&self) -> impl Future> + Send { - async move { commands::memory::memory_purge(self).await } - } - - /// The MEMORY STATS command returns an Array reply about the memory usage of the server. - /// - /// - fn memory_stats(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::memory::memory_stats(self).await?.convert() } - } - - /// The MEMORY USAGE command reports the number of bytes that a key and its value require to be stored in RAM. - /// - /// - fn memory_usage(&self, key: K, samples: Option) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::memory::memory_usage(self, key, samples).await?.convert() - } - } -} diff --git a/src/commands/interfaces/metrics.rs b/src/commands/interfaces/metrics.rs deleted file mode 100644 index aa687437..00000000 --- a/src/commands/interfaces/metrics.rs +++ /dev/null @@ -1,89 +0,0 @@ -use crate::interfaces::ClientLike; - -#[cfg(feature = "metrics")] -use crate::modules::metrics::Stats; - -/// Functions that implement the internal metrics interface. -pub trait MetricsInterface: ClientLike + Sized { - /// Read the number of request redeliveries. - /// - /// This is the number of times a request had to be sent again due to a connection closing while waiting on a - /// response. - fn read_redelivery_count(&self) -> usize { - self.inner().counters.read_redelivery_count() - } - - /// Read and reset the number of request redeliveries. - fn take_redelivery_count(&self) -> usize { - self.inner().counters.take_redelivery_count() - } - - /// Read the number of buffered commands that have not yet been sent to the server. - fn command_queue_len(&self) -> usize { - self.inner().counters.read_cmd_buffer_len() - } - - /// Read latency metrics across all commands. - /// - /// This metric reflects the total latency experienced by callers, including time spent waiting in memory to be - /// written and network latency. Features such as automatic reconnect and frame serialization time can all affect - /// these values. - #[cfg(feature = "metrics")] - #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] - fn read_latency_metrics(&self) -> Stats { - self.inner().latency_stats.read().read_metrics() - } - - /// Read and consume latency metrics, resetting their values afterwards. - #[cfg(feature = "metrics")] - #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] - fn take_latency_metrics(&self) -> Stats { - self.inner().latency_stats.write().take_metrics() - } - - /// Read network latency metrics across all commands. - /// - /// This metric only reflects time spent waiting on a response. It will factor in reconnect time if a response - /// doesn't arrive due to a connection closing, but it does not include the time a command spends waiting to be - /// written, serialization time, backpressure, etc. - #[cfg(feature = "metrics")] - #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] - fn read_network_latency_metrics(&self) -> Stats { - self.inner().network_latency_stats.read().read_metrics() - } - - /// Read and consume network latency metrics, resetting their values afterwards. - #[cfg(feature = "metrics")] - #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] - fn take_network_latency_metrics(&self) -> Stats { - self.inner().network_latency_stats.write().take_metrics() - } - - /// Read request payload size metrics across all commands. - #[cfg(feature = "metrics")] - #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] - fn read_req_size_metrics(&self) -> Stats { - self.inner().req_size_stats.read().read_metrics() - } - - /// Read and consume request payload size metrics, resetting their values afterwards. - #[cfg(feature = "metrics")] - #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] - fn take_req_size_metrics(&self) -> Stats { - self.inner().req_size_stats.write().take_metrics() - } - - /// Read response payload size metrics across all commands. - #[cfg(feature = "metrics")] - #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] - fn read_res_size_metrics(&self) -> Stats { - self.inner().res_size_stats.read().read_metrics() - } - - /// Read and consume response payload size metrics, resetting their values afterwards. - #[cfg(feature = "metrics")] - #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] - fn take_res_size_metrics(&self) -> Stats { - self.inner().res_size_stats.write().take_metrics() - } -} diff --git a/src/commands/interfaces/mod.rs b/src/commands/interfaces/mod.rs deleted file mode 100644 index 6117e744..00000000 --- a/src/commands/interfaces/mod.rs +++ /dev/null @@ -1,49 +0,0 @@ -#[cfg(feature = "i-acl")] -pub mod acl; -#[cfg(feature = "i-client")] -pub mod client; -#[cfg(feature = "i-cluster")] -pub mod cluster; -#[cfg(feature = "i-config")] -pub mod config; -#[cfg(feature = "i-geo")] -pub mod geo; -#[cfg(feature = "i-hashes")] -pub mod hashes; -#[cfg(feature = "i-hyperloglog")] -pub mod hyperloglog; -#[cfg(feature = "i-keys")] -pub mod keys; -#[cfg(feature = "i-lists")] -pub mod lists; -#[cfg(feature = "i-scripts")] -pub mod lua; -#[cfg(feature = "i-memory")] -pub mod memory; -pub mod metrics; -#[cfg(feature = "i-pubsub")] -pub mod pubsub; -#[cfg(feature = "i-redis-json")] -pub mod redis_json; -#[cfg(feature = "i-redisearch")] -pub mod redisearch; -pub mod scan; -#[cfg(feature = "sentinel-client")] -pub mod sentinel; -#[cfg(feature = "i-server")] -pub mod server; -#[cfg(feature = "i-sets")] -pub mod sets; -#[cfg(feature = "i-slowlog")] -pub mod slowlog; -#[cfg(feature = "i-sorted-sets")] -pub mod sorted_sets; -#[cfg(feature = "i-streams")] -pub mod streams; -pub mod strings; -#[cfg(feature = "i-time-series")] -pub mod timeseries; -#[cfg(feature = "i-tracking")] -pub mod tracking; -#[cfg(feature = "transactions")] -pub mod transactions; diff --git a/src/commands/interfaces/pubsub.rs b/src/commands/interfaces/pubsub.rs deleted file mode 100644 index 6a6c7bc6..00000000 --- a/src/commands/interfaces/pubsub.rs +++ /dev/null @@ -1,196 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - types::{FromRedis, MultipleStrings, RedisValue}, -}; -use bytes_utils::Str; -use futures::Future; -use rm_send_macros::rm_send_if; -use std::convert::TryInto; - -/// Functions that implement the [pubsub](https://redis.io/commands#pubsub) interface. -#[rm_send_if(feature = "glommio")] -pub trait PubsubInterface: ClientLike + Sized + Send { - /// Subscribe to a channel on the publish-subscribe interface. - /// - /// - fn subscribe(&self, channels: S) -> impl Future> + Send - where - S: Into + Send, - { - async move { - into!(channels); - commands::pubsub::subscribe(self, channels).await - } - } - - /// Unsubscribe from a channel on the PubSub interface. - /// - /// - fn unsubscribe(&self, channels: S) -> impl Future> + Send - where - S: Into + Send, - { - async move { - into!(channels); - commands::pubsub::unsubscribe(self, channels).await - } - } - - /// Subscribes the client to the given patterns. - /// - /// - fn psubscribe(&self, patterns: S) -> impl Future> + Send - where - S: Into + Send, - { - async move { - into!(patterns); - commands::pubsub::psubscribe(self, patterns).await - } - } - - /// Unsubscribes the client from the given patterns, or from all of them if none is given. - /// - /// If no channels are provided this command returns an empty array. - /// - /// - fn punsubscribe(&self, patterns: S) -> impl Future> + Send - where - S: Into + Send, - { - async move { - into!(patterns); - commands::pubsub::punsubscribe(self, patterns).await - } - } - - /// Publish a message on the PubSub interface, returning the number of clients that received the message. - /// - /// - fn publish(&self, channel: S, message: V) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(channel); - try_into!(message); - commands::pubsub::publish(self, channel, message).await?.convert() - } - } - - /// Subscribes the client to the specified shard channels. - /// - /// - fn ssubscribe(&self, channels: C) -> impl Future> + Send - where - C: Into + Send, - { - async move { - into!(channels); - commands::pubsub::ssubscribe(self, channels).await - } - } - - /// Unsubscribes the client from the given shard channels, or from all of them if none is given. - /// - /// If no channels are provided this command returns an empty array. - /// - /// - fn sunsubscribe(&self, channels: C) -> impl Future> + Send - where - C: Into + Send, - { - async move { - into!(channels); - commands::pubsub::sunsubscribe(self, channels).await - } - } - - /// Posts a message to the given shard channel. - /// - /// - fn spublish(&self, channel: S, message: V) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(channel); - try_into!(message); - commands::pubsub::spublish(self, channel, message).await?.convert() - } - } - - /// Lists the currently active channels. - /// - /// - fn pubsub_channels(&self, pattern: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(pattern); - commands::pubsub::pubsub_channels(self, pattern).await?.convert() - } - } - - /// Returns the number of unique patterns that are subscribed to by clients. - /// - /// - fn pubsub_numpat(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::pubsub::pubsub_numpat(self).await?.convert() } - } - - /// Returns the number of subscribers (exclusive of clients subscribed to patterns) for the specified channels. - /// - /// - fn pubsub_numsub(&self, channels: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(channels); - commands::pubsub::pubsub_numsub(self, channels).await?.convert() - } - } - - /// Lists the currently active shard channels. - /// - /// - fn pubsub_shardchannels(&self, pattern: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(pattern); - commands::pubsub::pubsub_shardchannels(self, pattern).await?.convert() - } - } - - /// Returns the number of subscribers for the specified shard channels. - /// - /// - fn pubsub_shardnumsub(&self, channels: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(channels); - commands::pubsub::pubsub_shardnumsub(self, channels).await?.convert() - } - } -} diff --git a/src/commands/interfaces/redis_json.rs b/src/commands/interfaces/redis_json.rs deleted file mode 100644 index 127688b8..00000000 --- a/src/commands/interfaces/redis_json.rs +++ /dev/null @@ -1,473 +0,0 @@ -use crate::{ - commands, - interfaces::{ClientLike, RedisResult}, - types::{FromRedis, MultipleKeys, MultipleStrings, RedisKey, SetOptions}, -}; -use bytes_utils::Str; -use futures::Future; -use rm_send_macros::rm_send_if; -use serde_json::Value; - -/// The client commands in the [RedisJSON](https://redis.io/docs/data-types/json/) interface. -/// -/// ## String Values -/// -/// This interface uses [serde_json::Value](serde_json::Value) as the baseline type and will convert non-string values -/// to RESP bulk strings via [to_string](serde_json::to_string). -/// -/// Some of the RedisJSON commands include the following notice in the documentation: -/// -/// > To specify a string as an array value to append, wrap the quoted string with an additional set of single quotes. -/// > Example: '"silver"'. -/// -/// The [serde_json::to_string](serde_json::to_string) functions are often the easiest way to do -/// this. The [json_quote](crate::json_quote) macro can also help. -/// -/// For example: -/// -/// ```rust -/// use fred::{json_quote, prelude::*}; -/// use serde_json::json; -/// async fn example(client: &RedisClient) -> Result<(), RedisError> { -/// let _: () = client.json_set("foo", "$", json!(["a", "b"]), None).await?; -/// -/// // need to double quote string values in this context -/// let size: i64 = client -/// .json_arrappend("foo", Some("$"), vec![ -/// json!("c").to_string(), -/// // or -/// serde_json::to_string(&json!("d"))?, -/// // or -/// json_quote!("e"), -/// ]) -/// .await?; -/// assert_eq!(size, 5); -/// Ok(()) -/// } -/// ``` -#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))] -#[rm_send_if(feature = "glommio")] -pub trait RedisJsonInterface: ClientLike + Sized { - /// Append the json values into the array at path after the last element in it. - /// - /// - fn json_arrappend(&self, key: K, path: P, values: Vec) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - V: Into + Send, - { - async move { - into!(key, path); - let values = values.into_iter().map(|v| v.into()).collect(); - commands::redis_json::json_arrappend(self, key, path, values) - .await? - .convert() - } - } - - /// Search for the first occurrence of a JSON value in an array. - /// - /// - fn json_arrindex( - &self, - key: K, - path: P, - value: V, - start: Option, - stop: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - V: Into + Send, - { - async move { - into!(key, path, value); - commands::redis_json::json_arrindex(self, key, path, value, start, stop) - .await? - .convert() - } - } - - /// Insert the json values into the array at path before the index (shifts to the right). - /// - /// - fn json_arrinsert( - &self, - key: K, - path: P, - index: i64, - values: Vec, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - V: Into + Send, - { - async move { - into!(key, path); - let values = values.into_iter().map(|v| v.into()).collect(); - commands::redis_json::json_arrinsert(self, key, path, index, values) - .await? - .convert() - } - } - - /// Report the length of the JSON array at path in key. - /// - /// - fn json_arrlen(&self, key: K, path: Option

) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key); - let path = path.map(|p| p.into()); - commands::redis_json::json_arrlen(self, key, path).await?.convert() - } - } - - /// Remove and return an element from the index in the array - /// - /// - fn json_arrpop( - &self, - key: K, - path: Option

, - index: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key); - let path = path.map(|p| p.into()); - commands::redis_json::json_arrpop(self, key, path, index) - .await? - .convert() - } - } - - /// Trim an array so that it contains only the specified inclusive range of elements - /// - /// - fn json_arrtrim( - &self, - key: K, - path: P, - start: i64, - stop: i64, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key, path); - commands::redis_json::json_arrtrim(self, key, path, start, stop) - .await? - .convert() - } - } - - /// Clear container values (arrays/objects) and set numeric values to 0 - /// - /// - fn json_clear(&self, key: K, path: Option

) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key); - let path = path.map(|p| p.into()); - commands::redis_json::json_clear(self, key, path).await?.convert() - } - } - - /// Report a value's memory usage in bytes - /// - /// - fn json_debug_memory(&self, key: K, path: Option

) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key); - let path = path.map(|p| p.into()); - commands::redis_json::json_debug_memory(self, key, path) - .await? - .convert() - } - } - - /// Delete a value. - /// - /// - fn json_del(&self, key: K, path: P) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key, path); - commands::redis_json::json_del(self, key, path).await?.convert() - } - } - - /// Return the value at path in JSON serialized form. - /// - /// - fn json_get( - &self, - key: K, - indent: Option, - newline: Option, - space: Option, - paths: P, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - I: Into + Send, - N: Into + Send, - S: Into + Send, - P: Into + Send, - { - async move { - into!(key, paths); - let indent = indent.map(|v| v.into()); - let newline = newline.map(|v| v.into()); - let space = space.map(|v| v.into()); - commands::redis_json::json_get(self, key, indent, newline, space, paths) - .await? - .convert() - } - } - - /// Merge a given JSON value into matching paths. - /// - /// - fn json_merge(&self, key: K, path: P, value: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - V: Into + Send, - { - async move { - into!(key, path, value); - commands::redis_json::json_merge(self, key, path, value) - .await? - .convert() - } - } - - /// Return the values at path from multiple key arguments. - /// - /// - fn json_mget(&self, keys: K, path: P) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(keys, path); - commands::redis_json::json_mget(self, keys, path).await?.convert() - } - } - - /// Set or update one or more JSON values according to the specified key-path-value triplets. - /// - /// - fn json_mset(&self, values: Vec<(K, P, V)>) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - V: Into + Send, - { - async move { - let values = values - .into_iter() - .map(|(k, p, v)| (k.into(), p.into(), v.into())) - .collect(); - commands::redis_json::json_mset(self, values).await?.convert() - } - } - - /// Increment the number value stored at path by number - /// - /// - fn json_numincrby(&self, key: K, path: P, value: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - V: Into + Send, - { - async move { - into!(key, path, value); - commands::redis_json::json_numincrby(self, key, path, value) - .await? - .convert() - } - } - - /// Return the keys in the object that's referenced by path. - /// - /// - fn json_objkeys(&self, key: K, path: Option

) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key); - let path = path.map(|p| p.into()); - commands::redis_json::json_objkeys(self, key, path).await?.convert() - } - } - - /// Report the number of keys in the JSON object at path in key. - /// - /// - fn json_objlen(&self, key: K, path: Option

) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key); - let path = path.map(|p| p.into()); - commands::redis_json::json_objlen(self, key, path).await?.convert() - } - } - - /// Return the JSON in key in Redis serialization protocol specification form. - /// - /// - fn json_resp(&self, key: K, path: Option

) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key); - let path = path.map(|p| p.into()); - commands::redis_json::json_resp(self, key, path).await?.convert() - } - } - - /// Set the JSON value at path in key. - /// - /// - fn json_set( - &self, - key: K, - path: P, - value: V, - options: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - V: Into + Send, - { - async move { - into!(key, path, value); - commands::redis_json::json_set(self, key, path, value, options) - .await? - .convert() - } - } - - /// Append the json-string values to the string at path. - /// - /// - fn json_strappend( - &self, - key: K, - path: Option

, - value: V, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - V: Into + Send, - { - async move { - into!(key, value); - let path = path.map(|p| p.into()); - commands::redis_json::json_strappend(self, key, path, value) - .await? - .convert() - } - } - - /// Report the length of the JSON String at path in key. - /// - /// - fn json_strlen(&self, key: K, path: Option

) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key); - let path = path.map(|p| p.into()); - commands::redis_json::json_strlen(self, key, path).await?.convert() - } - } - - /// Toggle a Boolean value stored at path. - /// - /// - fn json_toggle(&self, key: K, path: P) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key, path); - commands::redis_json::json_toggle(self, key, path).await?.convert() - } - } - - /// Report the type of JSON value at path. - /// - /// - fn json_type(&self, key: K, path: Option

) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key); - let path = path.map(|p| p.into()); - commands::redis_json::json_type(self, key, path).await?.convert() - } - } -} diff --git a/src/commands/interfaces/redisearch.rs b/src/commands/interfaces/redisearch.rs deleted file mode 100644 index 0b7d004e..00000000 --- a/src/commands/interfaces/redisearch.rs +++ /dev/null @@ -1,491 +0,0 @@ -use crate::{ - commands, - interfaces::{ClientLike, RedisResult}, - prelude::RedisError, - types::{ - FromRedis, - FtAggregateOptions, - FtAlterOptions, - FtCreateOptions, - FtSearchOptions, - MultipleStrings, - RedisKey, - RedisValue, - SearchSchema, - SpellcheckTerms, - }, -}; -use bytes::Bytes; -use bytes_utils::Str; -use rm_send_macros::rm_send_if; -use std::future::Future; - -/// A [RediSearch](https://github.com/RediSearch/RediSearch) interface. -#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))] -#[rm_send_if(feature = "glommio")] -pub trait RediSearchInterface: ClientLike + Sized { - /// Returns a list of all existing indexes. - /// - /// - fn ft_list(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::redisearch::ft_list(self).await?.convert() } - } - - /// Run a search query on an index, and perform aggregate transformations on the results. - /// - /// - fn ft_aggregate( - &self, - index: I, - query: Q, - options: FtAggregateOptions, - ) -> impl Future> + Send - where - R: FromRedis, - I: Into + Send, - Q: Into + Send, - { - async move { - into!(index, query); - commands::redisearch::ft_aggregate(self, index, query, options) - .await? - .convert() - } - } - - /// Search the index with a textual query, returning either documents or just ids. - /// - /// - /// - /// Note: `FT.SEARCH` uses a different format in RESP3 mode. - fn ft_search( - &self, - index: I, - query: Q, - options: FtSearchOptions, - ) -> impl Future> + Send - where - R: FromRedis, - I: Into + Send, - Q: Into + Send, - { - async move { - into!(index, query); - commands::redisearch::ft_search(self, index, query, options) - .await? - .convert() - } - } - - /// Create an index with the given specification. - /// - /// - fn ft_create( - &self, - index: I, - options: FtCreateOptions, - schema: Vec, - ) -> impl Future> + Send - where - R: FromRedis, - I: Into + Send, - { - async move { - into!(index); - commands::redisearch::ft_create(self, index, options, schema) - .await? - .convert() - } - } - - /// Add a new attribute to the index. - /// - /// - fn ft_alter(&self, index: I, options: FtAlterOptions) -> impl Future> + Send - where - R: FromRedis, - I: Into + Send, - { - async move { - into!(index); - commands::redisearch::ft_alter(self, index, options).await?.convert() - } - } - - /// Add an alias to an index. - /// - /// - fn ft_aliasadd(&self, alias: A, index: I) -> impl Future> + Send - where - R: FromRedis, - A: Into + Send, - I: Into + Send, - { - async move { - into!(alias, index); - commands::redisearch::ft_aliasadd(self, alias, index).await?.convert() - } - } - - /// Remove an alias from an index. - /// - /// - fn ft_aliasdel(&self, alias: A) -> impl Future> + Send - where - R: FromRedis, - A: Into + Send, - { - async move { - into!(alias); - commands::redisearch::ft_aliasdel(self, alias).await?.convert() - } - } - - /// Add an alias to an index. If the alias is already associated with another index, FT.ALIASUPDATE removes the - /// alias association with the previous index. - /// - /// - fn ft_aliasupdate(&self, alias: A, index: I) -> impl Future> + Send - where - R: FromRedis, - A: Into + Send, - I: Into + Send, - { - async move { - into!(alias, index); - commands::redisearch::ft_aliasupdate(self, alias, index) - .await? - .convert() - } - } - - /// Retrieve configuration options. - /// - /// - fn ft_config_get(&self, option: S) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - { - async move { - into!(option); - commands::redisearch::ft_config_get(self, option).await?.convert() - } - } - - /// Set the value of a RediSearch configuration parameter. - /// - /// - fn ft_config_set(&self, option: S, value: V) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(option); - try_into!(value); - commands::redisearch::ft_config_set(self, option, value) - .await? - .convert() - } - } - - /// Delete a cursor. - /// - /// - fn ft_cursor_del(&self, index: I, cursor: C) -> impl Future> + Send - where - R: FromRedis, - I: Into + Send, - C: TryInto + Send, - C::Error: Into + Send, - { - async move { - into!(index); - try_into!(cursor); - commands::redisearch::ft_cursor_del(self, index, cursor) - .await? - .convert() - } - } - - /// Read next results from an existing cursor. - /// - /// - fn ft_cursor_read( - &self, - index: I, - cursor: C, - count: Option, - ) -> impl Future> + Send - where - R: FromRedis, - I: Into + Send, - C: TryInto + Send, - C::Error: Into + Send, - { - async move { - into!(index); - try_into!(cursor); - commands::redisearch::ft_cursor_read(self, index, cursor, count) - .await? - .convert() - } - } - - /// Add terms to a dictionary. - /// - /// - fn ft_dictadd(&self, dict: D, terms: S) -> impl Future> - where - R: FromRedis, - D: Into + Send, - S: Into + Send, - { - async move { - into!(dict, terms); - commands::redisearch::ft_dictadd(self, dict, terms).await?.convert() - } - } - - /// Remove terms from a dictionary. - /// - /// - fn ft_dictdel(&self, dict: D, terms: S) -> impl Future> - where - R: FromRedis, - D: Into + Send, - S: Into + Send, - { - async move { - into!(dict, terms); - commands::redisearch::ft_dictdel(self, dict, terms).await?.convert() - } - } - - /// Dump all terms in the given dictionary. - /// - /// - fn ft_dictdump(&self, dict: D) -> impl Future> - where - R: FromRedis, - D: Into + Send, - { - async move { - into!(dict); - commands::redisearch::ft_dictdump(self, dict).await?.convert() - } - } - - /// Delete an index. - /// - /// - fn ft_dropindex(&self, index: I, dd: bool) -> impl Future> + Send - where - R: FromRedis, - I: Into + Send, - { - async move { - into!(index); - commands::redisearch::ft_dropindex(self, index, dd).await?.convert() - } - } - - /// Return the execution plan for a complex query. - /// - /// - fn ft_explain( - &self, - index: I, - query: Q, - dialect: Option, - ) -> impl Future> + Send - where - R: FromRedis, - I: Into + Send, - Q: Into + Send, - { - async move { - into!(index, query); - commands::redisearch::ft_explain(self, index, query, dialect) - .await? - .convert() - } - } - - /// Return information and statistics on the index. - /// - /// - fn ft_info(&self, index: I) -> impl Future> + Send - where - R: FromRedis, - I: Into + Send, - { - async move { - into!(index); - commands::redisearch::ft_info(self, index).await?.convert() - } - } - - /// Perform spelling correction on a query, returning suggestions for misspelled terms. - /// - /// - fn ft_spellcheck( - &self, - index: I, - query: Q, - distance: Option, - terms: Option, - dialect: Option, - ) -> impl Future> + Send - where - R: FromRedis, - I: Into + Send, - Q: Into + Send, - { - async move { - into!(index, query); - commands::redisearch::ft_spellcheck(self, index, query, distance, terms, dialect) - .await? - .convert() - } - } - - /// Add a suggestion string to an auto-complete suggestion dictionary. - /// - /// - fn ft_sugadd( - &self, - key: K, - string: S, - score: f64, - incr: bool, - payload: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - S: Into + Send, - { - async move { - into!(key, string); - commands::redisearch::ft_sugadd(self, key, string, score, incr, payload) - .await? - .convert() - } - } - - /// Delete a string from a suggestion index. - /// - /// - fn ft_sugdel(&self, key: K, string: S) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - S: Into + Send, - { - async move { - into!(key, string); - commands::redisearch::ft_sugdel(self, key, string).await?.convert() - } - } - - /// Get completion suggestions for a prefix. - /// - /// - fn ft_sugget( - &self, - key: K, - prefix: P, - fuzzy: bool, - withscores: bool, - withpayloads: bool, - max: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - P: Into + Send, - { - async move { - into!(key, prefix); - commands::redisearch::ft_sugget(self, key, prefix, fuzzy, withscores, withpayloads, max) - .await? - .convert() - } - } - - /// Get the size of an auto-complete suggestion dictionary. - /// - /// - fn ft_suglen(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::redisearch::ft_suglen(self, key).await?.convert() - } - } - - /// Dump the contents of a synonym group. - /// - /// - fn ft_syndump(&self, index: I) -> impl Future> + Send - where - R: FromRedis, - I: Into + Send, - { - async move { - into!(index); - commands::redisearch::ft_syndump(self, index).await?.convert() - } - } - - /// Update a synonym group. - /// - /// - fn ft_synupdate( - &self, - index: I, - synonym_group_id: S, - skipinitialscan: bool, - terms: T, - ) -> impl Future> + Send - where - R: FromRedis, - I: Into + Send, - S: Into + Send, - T: Into + Send, - { - async move { - into!(index, synonym_group_id, terms); - commands::redisearch::ft_synupdate(self, index, synonym_group_id, skipinitialscan, terms) - .await? - .convert() - } - } - - /// Return a distinct set of values indexed in a Tag field. - /// - /// - fn ft_tagvals(&self, index: I, field_name: F) -> impl Future> + Send - where - R: FromRedis, - I: Into + Send, - F: Into + Send, - { - async move { - into!(index, field_name); - commands::redisearch::ft_tagvals(self, index, field_name) - .await? - .convert() - } - } -} diff --git a/src/commands/interfaces/scan.rs b/src/commands/interfaces/scan.rs deleted file mode 100644 index f4011a66..00000000 --- a/src/commands/interfaces/scan.rs +++ /dev/null @@ -1 +0,0 @@ -// `impl Trait` doesn't work inside traits, so we just put these functions directly on the RedisClient diff --git a/src/commands/interfaces/sentinel.rs b/src/commands/interfaces/sentinel.rs deleted file mode 100644 index a6d2adcc..00000000 --- a/src/commands/interfaces/sentinel.rs +++ /dev/null @@ -1,222 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - types::{FromRedis, RedisMap, RedisValue, SentinelFailureKind}, -}; -use bytes_utils::Str; -use futures::Future; -use rm_send_macros::rm_send_if; -use std::{convert::TryInto, net::IpAddr}; - -/// Functions that implement the [sentinel](https://redis.io/topics/sentinel#sentinel-commands) interface. -#[rm_send_if(feature = "glommio")] -pub trait SentinelInterface: ClientLike + Sized { - /// Check if the current Sentinel configuration is able to reach the quorum needed to failover a master, and the - /// majority needed to authorize the failover. - fn ckquorum(&self, name: N) -> impl Future> + Send - where - R: FromRedis, - N: Into + Send, - { - async move { - into!(name); - commands::sentinel::ckquorum(self, name).await?.convert() - } - } - - /// Force Sentinel to rewrite its configuration on disk, including the current Sentinel state. - fn flushconfig(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::sentinel::flushconfig(self).await?.convert() } - } - - /// Force a failover as if the master was not reachable, and without asking for agreement to other Sentinels. - fn failover(&self, name: N) -> impl Future> + Send - where - R: FromRedis, - N: Into + Send, - { - async move { - into!(name); - commands::sentinel::failover(self, name).await?.convert() - } - } - - /// Return the ip and port number of the master with that name. - fn get_master_addr_by_name(&self, name: N) -> impl Future> + Send - where - R: FromRedis, - N: Into + Send, - { - async move { - into!(name); - commands::sentinel::get_master_addr_by_name(self, name).await?.convert() - } - } - - /// Return cached INFO output from masters and replicas. - fn info_cache(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::sentinel::info_cache(self).await?.convert() } - } - - /// Show the state and info of the specified master. - fn master(&self, name: N) -> impl Future> + Send - where - R: FromRedis, - N: Into + Send, - { - async move { - into!(name); - commands::sentinel::master(self, name).await?.convert() - } - } - - /// Show a list of monitored masters and their state. - fn masters(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::sentinel::masters(self).await?.convert() } - } - - /// Start Sentinel's monitoring. - /// - /// - fn monitor(&self, name: N, ip: IpAddr, port: u16, quorum: u32) -> impl Future> + Send - where - R: FromRedis, - N: Into + Send, - { - async move { - into!(name); - commands::sentinel::monitor(self, name, ip, port, quorum) - .await? - .convert() - } - } - - /// Return the ID of the Sentinel instance. - fn myid(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::sentinel::myid(self).await?.convert() } - } - - /// This command returns information about pending scripts. - fn pending_scripts(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::sentinel::pending_scripts(self).await?.convert() } - } - - /// Stop Sentinel's monitoring. - /// - /// - fn remove(&self, name: N) -> impl Future> + Send - where - R: FromRedis, - N: Into + Send, - { - async move { - into!(name); - commands::sentinel::remove(self, name).await?.convert() - } - } - - /// Show a list of replicas for this master, and their state. - fn replicas(&self, name: N) -> impl Future> + Send - where - R: FromRedis, - N: Into + Send, - { - async move { - into!(name); - commands::sentinel::replicas(self, name).await?.convert() - } - } - - /// Show a list of sentinel instances for this master, and their state. - fn sentinels(&self, name: N) -> impl Future> + Send - where - R: FromRedis, - N: Into + Send, - { - async move { - into!(name); - commands::sentinel::sentinels(self, name).await?.convert() - } - } - - /// Set Sentinel's monitoring configuration. - /// - /// - fn set(&self, name: N, args: V) -> impl Future> + Send - where - R: FromRedis, - N: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(name); - try_into!(args); - commands::sentinel::set(self, name, args).await?.convert() - } - } - - /// This command simulates different Sentinel crash scenarios. - fn simulate_failure(&self, kind: SentinelFailureKind) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::sentinel::simulate_failure(self, kind).await?.convert() } - } - - /// This command will reset all the masters with matching name. - fn reset(&self, pattern: P) -> impl Future> + Send - where - R: FromRedis, - P: Into + Send, - { - async move { - into!(pattern); - commands::sentinel::reset(self, pattern).await?.convert() - } - } - - /// Get the current value of a global Sentinel configuration parameter. The specified name may be a wildcard, - /// similar to the Redis CONFIG GET command. - fn config_get(&self, name: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(name); - commands::sentinel::config_get(self, name).await?.convert() - } - } - - /// Set the value of a global Sentinel configuration parameter. - fn config_set(&self, name: K, value: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(name); - try_into!(value); - commands::sentinel::config_set(self, name, value).await?.convert() - } - } -} diff --git a/src/commands/interfaces/server.rs b/src/commands/interfaces/server.rs deleted file mode 100644 index ce94e3f9..00000000 --- a/src/commands/interfaces/server.rs +++ /dev/null @@ -1,96 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - types::{FromRedis, Server}, -}; -use futures::Future; -use rm_send_macros::rm_send_if; - -/// Functions that implement the [server](https://redis.io/commands#server) interface. -#[rm_send_if(feature = "glommio")] -pub trait ServerInterface: ClientLike { - /// Instruct Redis to start an Append Only File rewrite process. - /// - /// - fn bgrewriteaof(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::server::bgrewriteaof(self).await?.convert() } - } - - /// Save the DB in background. - /// - /// - fn bgsave(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::server::bgsave(self).await?.convert() } - } - - /// Return the number of keys in the selected database. - /// - /// - fn dbsize(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::server::dbsize(self).await?.convert() } - } - - /// Select the database this client should use. - /// - /// - fn select(&self, db: u8) -> impl Future> + Send { - async move { commands::server::select(self, db).await?.convert() } - } - - /// This command will start a coordinated failover between the currently-connected-to master and one of its - /// replicas. - /// - /// - fn failover( - &self, - to: Option<(String, u16)>, - force: bool, - abort: bool, - timeout: Option, - ) -> impl Future> + Send { - async move { commands::server::failover(self, to, force, abort, timeout).await } - } - - /// Return the UNIX TIME of the last DB save executed with success. - /// - /// - fn lastsave(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::server::lastsave(self).await?.convert() } - } - - /// This command blocks the current client until all the previous write commands are successfully transferred and - /// acknowledged by at least the specified number of replicas. If the timeout, specified in milliseconds, is - /// reached, the command returns even if the specified number of replicas were not yet reached. - /// - /// - fn wait(&self, numreplicas: i64, timeout: i64) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::server::wait(self, numreplicas, timeout).await?.convert() } - } - - /// Read the primary Redis server identifier returned from the sentinel nodes. - fn sentinel_primary(&self) -> Option { - self.inner().server_state.read().kind.sentinel_primary() - } - - /// Read the set of known sentinel nodes. - fn sentinel_nodes(&self) -> Option> { - let inner = self.inner(); - inner.server_state.read().kind.read_sentinel_nodes(&inner.config.server) - } -} diff --git a/src/commands/interfaces/sets.rs b/src/commands/interfaces/sets.rs deleted file mode 100644 index e1c16230..00000000 --- a/src/commands/interfaces/sets.rs +++ /dev/null @@ -1,245 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - types::{FromRedis, MultipleKeys, MultipleValues, RedisKey, RedisValue}, -}; -use futures::Future; -use rm_send_macros::rm_send_if; -use std::convert::TryInto; - -/// Functions that implement the [sets](https://redis.io/commands#set) interface. -#[rm_send_if(feature = "glommio")] -pub trait SetsInterface: ClientLike + Sized { - /// Add the specified members to the set stored at `key`. - /// - /// - fn sadd(&self, key: K, members: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(members); - commands::sets::sadd(self, key, members).await?.convert() - } - } - - /// Returns the set cardinality (number of elements) of the set stored at `key`. - /// - /// - fn scard(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::sets::scard(self, key).await?.convert() - } - } - - /// Returns the members of the set resulting from the difference between the first set and all the successive sets. - /// - /// - fn sdiff(&self, keys: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::sets::sdiff(self, keys).await?.convert() - } - } - - /// This command is equal to SDIFF, but instead of returning the resulting set, it is stored in `destination`. - /// - /// - fn sdiffstore(&self, dest: D, keys: K) -> impl Future> + Send - where - R: FromRedis, - D: Into + Send, - K: Into + Send, - { - async move { - into!(dest, keys); - commands::sets::sdiffstore(self, dest, keys).await?.convert() - } - } - - /// Returns the members of the set resulting from the intersection of all the given sets. - /// - /// - fn sinter(&self, keys: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::sets::sinter(self, keys).await?.convert() - } - } - - /// This command is equal to SINTER, but instead of returning the resulting set, it is stored in `destination`. - /// - /// - fn sinterstore(&self, dest: D, keys: K) -> impl Future> + Send - where - R: FromRedis, - D: Into + Send, - K: Into + Send, - { - async move { - into!(dest, keys); - commands::sets::sinterstore(self, dest, keys).await?.convert() - } - } - - /// Returns if `member` is a member of the set stored at `key`. - /// - /// - fn sismember(&self, key: K, member: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(member); - commands::sets::sismember(self, key, member).await?.convert() - } - } - - /// Returns whether each member is a member of the set stored at `key`. - /// - /// - fn smismember(&self, key: K, members: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(members); - commands::sets::smismember(self, key, members).await?.convert() - } - } - - /// Returns all the members of the set value stored at `key`. - /// - /// - fn smembers(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::sets::smembers(self, key).await?.convert() - } - } - - /// Move `member` from the set at `source` to the set at `destination`. - /// - /// - fn smove(&self, source: S, dest: D, member: V) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - D: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(source, dest); - try_into!(member); - commands::sets::smove(self, source, dest, member).await?.convert() - } - } - - /// Removes and returns one or more random members from the set value store at `key`. - /// - /// - fn spop(&self, key: K, count: Option) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::sets::spop(self, key, count).await?.convert() - } - } - - /// When called with just the key argument, return a random element from the set value stored at `key`. - /// - /// If the provided `count` argument is positive, return an array of distinct elements. The array's length is either - /// count or the set's cardinality (SCARD), whichever is lower. - /// - /// - fn srandmember(&self, key: K, count: Option) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::sets::srandmember(self, key, count).await?.convert() - } - } - - /// Remove the specified members from the set stored at `key`. - /// - /// - fn srem(&self, key: K, members: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(members); - commands::sets::srem(self, key, members).await?.convert() - } - } - - /// Returns the members of the set resulting from the union of all the given sets. - /// - /// - fn sunion(&self, keys: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::sets::sunion(self, keys).await?.convert() - } - } - - /// This command is equal to SUNION, but instead of returning the resulting set, it is stored in `destination`. - /// - /// - fn sunionstore(&self, dest: D, keys: K) -> impl Future> + Send - where - R: FromRedis, - D: Into + Send, - K: Into + Send, - { - async move { - into!(dest, keys); - commands::sets::sunionstore(self, dest, keys).await?.convert() - } - } -} diff --git a/src/commands/interfaces/slowlog.rs b/src/commands/interfaces/slowlog.rs deleted file mode 100644 index 6d2dac4c..00000000 --- a/src/commands/interfaces/slowlog.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::{ - commands, - interfaces::{ClientLike, RedisResult}, - types::FromRedis, -}; -use futures::Future; -use rm_send_macros::rm_send_if; - -/// Functions that implement the [slowlog](https://redis.io/commands#server) interface. -#[rm_send_if(feature = "glommio")] -pub trait SlowlogInterface: ClientLike + Sized { - /// This command is used to read the slow queries log. - /// - /// - fn slowlog_get(&self, count: Option) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::slowlog::slowlog_get(self, count).await?.convert() } - } - - /// This command is used to read length of the slow queries log. - /// - /// - fn slowlog_length(&self) -> impl Future> + Send - where - R: FromRedis, - { - async move { commands::slowlog::slowlog_length(self).await?.convert() } - } - - /// This command is used to reset the slow queries log. - /// - /// - fn slowlog_reset(&self) -> impl Future> + Send { - async move { commands::slowlog::slowlog_reset(self).await } - } -} diff --git a/src/commands/interfaces/sorted_sets.rs b/src/commands/interfaces/sorted_sets.rs deleted file mode 100644 index abfed23b..00000000 --- a/src/commands/interfaces/sorted_sets.rs +++ /dev/null @@ -1,700 +0,0 @@ -use crate::{ - commands, - error::RedisError, - interfaces::{ClientLike, RedisResult}, - types::{ - AggregateOptions, - FromRedis, - Limit, - MultipleKeys, - MultipleValues, - MultipleWeights, - MultipleZaddValues, - Ordering, - RedisKey, - RedisValue, - SetOptions, - ZCmp, - ZRange, - ZSort, - }, -}; -use futures::Future; -use rm_send_macros::rm_send_if; -use std::convert::TryInto; - -/// Functions that implement the [sorted sets](https://redis.io/commands#sorted_set) interface. -#[rm_send_if(feature = "glommio")] -pub trait SortedSetsInterface: ClientLike + Sized { - /// The blocking variant of [Self::zmpop]. - /// - /// - fn bzmpop( - &self, - timeout: f64, - keys: K, - sort: ZCmp, - count: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::sorted_sets::bzmpop(self, timeout, keys, sort, count) - .await? - .convert() - } - } - - /// The blocking variant of [Self::zpopmin]. - /// - /// - fn bzpopmin(&self, keys: K, timeout: f64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::sorted_sets::bzpopmin(self, keys, timeout).await?.convert() - } - } - - /// The blocking variant of [Self::zpopmax]. - /// - /// - fn bzpopmax(&self, keys: K, timeout: f64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::sorted_sets::bzpopmax(self, keys, timeout).await?.convert() - } - } - - /// Adds all the specified members with the specified scores to the sorted set stored at `key`. - /// - /// - fn zadd( - &self, - key: K, - options: Option, - ordering: Option, - changed: bool, - incr: bool, - values: V, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(values); - commands::sorted_sets::zadd(self, key, options, ordering, changed, incr, values) - .await? - .convert() - } - } - - /// Returns the sorted set cardinality (number of elements) of the sorted set stored at `key`. - /// - /// - fn zcard(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::sorted_sets::zcard(self, key).await?.convert() - } - } - - /// Returns the number of elements in the sorted set at `key` with a score between `min` and `max`. - /// - /// - fn zcount(&self, key: K, min: f64, max: f64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::sorted_sets::zcount(self, key, min, max).await?.convert() - } - } - - /// This command is similar to ZDIFFSTORE, but instead of storing the resulting sorted set, it is returned to the - /// client. - /// - /// - fn zdiff(&self, keys: K, withscores: bool) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::sorted_sets::zdiff(self, keys, withscores).await?.convert() - } - } - - /// Computes the difference between the first and all successive input sorted sets and stores the result in - /// `destination`. - /// - /// - fn zdiffstore(&self, dest: D, keys: K) -> impl Future> + Send - where - R: FromRedis, - D: Into + Send, - K: Into + Send, - { - async move { - into!(dest, keys); - commands::sorted_sets::zdiffstore(self, dest, keys).await?.convert() - } - } - - /// Increments the score of `member` in the sorted set stored at `key` by `increment`. - /// - /// - fn zincrby(&self, key: K, increment: f64, member: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(member); - commands::sorted_sets::zincrby(self, key, increment, member) - .await? - .convert() - } - } - - /// This command is similar to ZINTERSTORE, but instead of storing the resulting sorted set, it is returned to the - /// client. - /// - /// - fn zinter( - &self, - keys: K, - weights: W, - aggregate: Option, - withscores: bool, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - W: Into + Send, - { - async move { - into!(keys, weights); - commands::sorted_sets::zinter(self, keys, weights, aggregate, withscores) - .await? - .convert() - } - } - - /// Computes the intersection of the sorted sets given by the specified keys, and stores the result in - /// `destination`. - /// - /// - fn zinterstore( - &self, - dest: D, - keys: K, - weights: W, - aggregate: Option, - ) -> impl Future> + Send - where - R: FromRedis, - D: Into + Send, - K: Into + Send, - W: Into + Send, - { - async move { - into!(dest, keys, weights); - commands::sorted_sets::zinterstore(self, dest, keys, weights, aggregate) - .await? - .convert() - } - } - - /// When all the elements in a sorted set are inserted with the same score, in order to force lexicographical - /// ordering, this command returns the number of elements in the sorted set at key with a value between min and - /// max. - /// - /// - fn zlexcount(&self, key: K, min: M, max: N) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - M: TryInto + Send, - M::Error: Into + Send, - N: TryInto + Send, - N::Error: Into + Send, - { - async move { - into!(key); - try_into!(min, max); - commands::sorted_sets::zlexcount(self, key, min, max).await?.convert() - } - } - - /// Removes and returns up to count members with the highest scores in the sorted set stored at `key`. - /// - /// - fn zpopmax(&self, key: K, count: Option) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::sorted_sets::zpopmax(self, key, count).await?.convert() - } - } - - /// Removes and returns up to count members with the lowest scores in the sorted set stored at `key`. - /// - /// - fn zpopmin(&self, key: K, count: Option) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::sorted_sets::zpopmin(self, key, count).await?.convert() - } - } - - /// Pops one or more elements, that are member-score pairs, from the first non-empty sorted set in the provided list - /// of key names. - /// - /// - fn zmpop(&self, keys: K, sort: ZCmp, count: Option) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(keys); - commands::sorted_sets::zmpop(self, keys, sort, count).await?.convert() - } - } - - /// When called with just the key argument, return a random element from the sorted set value stored at `key`. - /// - /// - fn zrandmember(&self, key: K, count: Option<(i64, bool)>) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::sorted_sets::zrandmember(self, key, count).await?.convert() - } - } - - /// This command is like ZRANGE, but stores the result in the `destination` key. - /// - /// - fn zrangestore( - &self, - dest: D, - source: S, - min: M, - max: N, - sort: Option, - rev: bool, - limit: Option, - ) -> impl Future> + Send - where - R: FromRedis, - D: Into + Send, - S: Into + Send, - M: TryInto + Send, - M::Error: Into + Send, - N: TryInto + Send, - N::Error: Into + Send, - { - async move { - into!(dest, source); - try_into!(min, max); - commands::sorted_sets::zrangestore(self, dest, source, min, max, sort, rev, limit) - .await? - .convert() - } - } - - /// Returns the specified range of elements in the sorted set stored at `key`. - /// - /// - fn zrange( - &self, - key: K, - min: M, - max: N, - sort: Option, - rev: bool, - limit: Option, - withscores: bool, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - M: TryInto + Send, - M::Error: Into + Send, - N: TryInto + Send, - N::Error: Into + Send, - { - async move { - into!(key); - try_into!(min, max); - commands::sorted_sets::zrange(self, key, min, max, sort, rev, limit, withscores) - .await? - .convert() - } - } - - /// When all the elements in a sorted set are inserted with the same score, in order to force lexicographical - /// ordering, this command returns all the elements in the sorted set at `key` with a value between `min` and `max`. - /// - /// - fn zrangebylex( - &self, - key: K, - min: M, - max: N, - limit: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - M: TryInto + Send, - M::Error: Into + Send, - N: TryInto + Send, - N::Error: Into + Send, - { - async move { - into!(key); - try_into!(min, max); - commands::sorted_sets::zrangebylex(self, key, min, max, limit) - .await? - .convert() - } - } - - /// When all the elements in a sorted set are inserted with the same score, in order to force lexicographical - /// ordering, this command returns all the elements in the sorted set at `key` with a value between `max` and `min`. - /// - /// - fn zrevrangebylex( - &self, - key: K, - max: M, - min: N, - limit: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - M: TryInto + Send, - M::Error: Into + Send, - N: TryInto + Send, - N::Error: Into + Send, - { - async move { - into!(key); - try_into!(max, min); - commands::sorted_sets::zrevrangebylex(self, key, max, min, limit) - .await? - .convert() - } - } - - /// Returns all the elements in the sorted set at key with a score between `min` and `max` (including elements - /// with score equal to `min` or `max`). - /// - /// - fn zrangebyscore( - &self, - key: K, - min: M, - max: N, - withscores: bool, - limit: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - M: TryInto + Send, - M::Error: Into + Send, - N: TryInto + Send, - N::Error: Into + Send, - { - async move { - into!(key); - try_into!(min, max); - commands::sorted_sets::zrangebyscore(self, key, min, max, withscores, limit) - .await? - .convert() - } - } - - /// Returns all the elements in the sorted set at `key` with a score between `max` and `min` (including - /// elements with score equal to `max` or `min`). - /// - /// - fn zrevrangebyscore( - &self, - key: K, - max: M, - min: N, - withscores: bool, - limit: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - M: TryInto + Send, - M::Error: Into + Send, - N: TryInto + Send, - N::Error: Into + Send, - { - async move { - into!(key); - try_into!(max, min); - commands::sorted_sets::zrevrangebyscore(self, key, max, min, withscores, limit) - .await? - .convert() - } - } - - /// Returns the rank of member in the sorted set stored at `key`, with the scores ordered from low to high. - /// - /// - fn zrank(&self, key: K, member: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(member); - commands::sorted_sets::zrank(self, key, member).await?.convert() - } - } - - /// Removes the specified members from the sorted set stored at `key`. Non existing members are ignored. - /// - /// - fn zrem(&self, key: K, members: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(members); - commands::sorted_sets::zrem(self, key, members).await?.convert() - } - } - - /// When all the elements in a sorted set are inserted with the same score, in order to force lexicographical - /// ordering, this command removes all elements in the sorted set stored at `key` between the lexicographical range - /// specified by `min` and `max`. - /// - /// - fn zremrangebylex(&self, key: K, min: M, max: N) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - M: TryInto + Send, - M::Error: Into + Send, - N: TryInto + Send, - N::Error: Into + Send, - { - async move { - into!(key); - try_into!(min, max); - commands::sorted_sets::zremrangebylex(self, key, min, max) - .await? - .convert() - } - } - - /// Removes all elements in the sorted set stored at `key` with rank between `start` and `stop`. - /// - /// - fn zremrangebyrank(&self, key: K, start: i64, stop: i64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::sorted_sets::zremrangebyrank(self, key, start, stop) - .await? - .convert() - } - } - - /// Removes all elements in the sorted set stored at `key` with a score between `min` and `max`. - /// - /// - fn zremrangebyscore(&self, key: K, min: M, max: N) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - M: TryInto + Send, - M::Error: Into + Send, - N: TryInto + Send, - N::Error: Into + Send, - { - async move { - into!(key); - try_into!(min, max); - commands::sorted_sets::zremrangebyscore(self, key, min, max) - .await? - .convert() - } - } - - /// Returns the specified range of elements in the sorted set stored at `key`. - /// - /// - fn zrevrange( - &self, - key: K, - start: i64, - stop: i64, - withscores: bool, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::sorted_sets::zrevrange(self, key, start, stop, withscores) - .await? - .convert() - } - } - - /// Returns the rank of `member` in the sorted set stored at `key`, with the scores ordered from high to low. - /// - /// - fn zrevrank(&self, key: K, member: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(member); - commands::sorted_sets::zrevrank(self, key, member).await?.convert() - } - } - - /// Returns the score of `member` in the sorted set at `key`. - /// - /// - fn zscore(&self, key: K, member: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(member); - commands::sorted_sets::zscore(self, key, member).await?.convert() - } - } - - /// This command is similar to ZUNIONSTORE, but instead of storing the resulting sorted set, it is returned to the - /// client. - /// - /// - fn zunion( - &self, - keys: K, - weights: W, - aggregate: Option, - withscores: bool, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - W: Into + Send, - { - async move { - into!(keys, weights); - commands::sorted_sets::zunion(self, keys, weights, aggregate, withscores) - .await? - .convert() - } - } - - /// Computes the union of the sorted sets given by the specified keys, and stores the result in `destination`. - /// - /// - fn zunionstore( - &self, - dest: D, - keys: K, - weights: W, - aggregate: Option, - ) -> impl Future> + Send - where - R: FromRedis, - D: Into + Send, - K: Into + Send, - W: Into + Send, - { - async move { - into!(dest, keys, weights); - commands::sorted_sets::zunionstore(self, dest, keys, weights, aggregate) - .await? - .convert() - } - } - - /// Returns the scores associated with the specified members in the sorted set stored at `key`. - /// - /// - fn zmscore(&self, key: K, members: V) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - async move { - into!(key); - try_into!(members); - commands::sorted_sets::zmscore(self, key, members).await?.convert() - } - } -} diff --git a/src/commands/interfaces/streams.rs b/src/commands/interfaces/streams.rs deleted file mode 100644 index bcd7959d..00000000 --- a/src/commands/interfaces/streams.rs +++ /dev/null @@ -1,785 +0,0 @@ -use crate::{ - commands, - interfaces::{ClientLike, RedisResult}, - prelude::RedisError, - types::{ - FromRedis, - FromRedisKey, - MultipleIDs, - MultipleKeys, - MultipleOrderedPairs, - MultipleStrings, - RedisKey, - RedisValue, - XCap, - XPendingArgs, - XReadResponse, - XReadValue, - XID, - }, -}; -use bytes_utils::Str; -use futures::Future; -use rm_send_macros::rm_send_if; -use std::{convert::TryInto, hash::Hash}; - -/// Functions that implement the [streams](https://redis.io/commands#stream) interface. -/// -/// **Note:** Several of the stream commands can return types with verbose type declarations. Additionally, certain -/// commands can be parsed differently in RESP2 and RESP3 modes. Functions such as [xread_map](Self::xread_map), -/// [xreadgroup_map](Self::xreadgroup_map), [xrange_values](Self::xrange_values), etc exist to make this easier for -/// callers. These functions apply an additional layer of parsing logic that can make declaring response types easier, -/// as well as automatically handling any differences between RESP2 and RESP3 return value types. -#[rm_send_if(feature = "glommio")] -pub trait StreamsInterface: ClientLike + Sized { - /// This command returns the list of consumers that belong to the `groupname` consumer group of the stream stored at - /// `key`. - /// - /// - fn xinfo_consumers(&self, key: K, groupname: S) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - S: Into + Send, - { - async move { - into!(key, groupname); - commands::streams::xinfo_consumers(self, key, groupname) - .await? - .convert() - } - } - - /// This command returns the list of all consumers groups of the stream stored at `key`. - /// - /// - fn xinfo_groups(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::streams::xinfo_groups(self, key).await?.convert() - } - } - - /// This command returns information about the stream stored at `key`. - /// - /// - fn xinfo_stream(&self, key: K, full: bool, count: Option) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::streams::xinfo_stream(self, key, full, count).await?.convert() - } - } - - /// Appends the specified stream entry to the stream at the specified key. If the key does not exist, as a side - /// effect of running this command the key is created with a stream value. The creation of stream's key can be - /// disabled with the NOMKSTREAM option. - /// - /// - fn xadd( - &self, - key: K, - nomkstream: bool, - cap: C, - id: I, - fields: F, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - I: Into + Send, - F: TryInto + Send, - F::Error: Into + Send, - C: TryInto + Send, - C::Error: Into + Send, - { - async move { - into!(key, id); - try_into!(fields, cap); - commands::streams::xadd(self, key, nomkstream, cap, id, fields) - .await? - .convert() - } - } - - /// Trims the stream by evicting older entries (entries with lower IDs) if needed. - /// - /// - fn xtrim(&self, key: K, cap: C) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - C: TryInto + Send, - C::Error: Into + Send, - { - async move { - into!(key); - try_into!(cap); - commands::streams::xtrim(self, key, cap).await?.convert() - } - } - - /// Removes the specified entries from a stream, and returns the number of entries deleted. - /// - /// - fn xdel(&self, key: K, ids: S) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - S: Into + Send, - { - async move { - into!(key, ids); - commands::streams::xdel(self, key, ids).await?.convert() - } - } - - /// Return the stream entries matching the provided range of IDs, automatically converting to a less verbose type - /// definition. - /// - /// - fn xrange_values( - &self, - key: K, - start: S, - end: E, - count: Option, - ) -> impl Future>>> + Send - where - Ri: FromRedis, - Rk: FromRedisKey + Hash + Eq, - Rv: FromRedis, - K: Into + Send, - S: TryInto + Send, - S::Error: Into + Send, - E: TryInto + Send, - E::Error: Into + Send, - { - async move { - into!(key); - try_into!(start, end); - commands::streams::xrange(self, key, start, end, count) - .await? - .into_xread_value() - } - } - - /// The command returns the stream entries matching a given range of IDs. The range is specified by a minimum - /// and maximum ID. All the entries having an ID between the two specified or exactly one of the two IDs specified - /// (closed interval) are returned. - /// - /// - /// - /// **See [xrange_values](Self::xrange_values) for a variation of this function that may be more useful.** - fn xrange( - &self, - key: K, - start: S, - end: E, - count: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - S: TryInto + Send, - S::Error: Into + Send, - E: TryInto + Send, - E::Error: Into + Send, - { - async move { - into!(key); - try_into!(start, end); - commands::streams::xrange(self, key, start, end, count).await?.convert() - } - } - - /// Similar to `XRANGE`, but with the results returned in reverse order. The results will be automatically converted - /// to a less verbose type definition. - /// - /// - fn xrevrange_values( - &self, - key: K, - end: E, - start: S, - count: Option, - ) -> impl Future>>> + Send - where - Ri: FromRedis, - Rk: FromRedisKey + Hash + Eq, - Rv: FromRedis, - K: Into + Send, - S: TryInto + Send, - S::Error: Into + Send, - E: TryInto + Send, - E::Error: Into + Send, - { - async move { - into!(key); - try_into!(start, end); - commands::streams::xrevrange(self, key, end, start, count) - .await? - .into_xread_value() - } - } - - /// Similar to `XRANGE`, but with the results returned in reverse order. - /// - /// - /// - /// **See the [xrevrange_values](Self::xrevrange_values) for a variation of this function that may be more useful.** - fn xrevrange( - &self, - key: K, - end: E, - start: S, - count: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - S: TryInto + Send, - S::Error: Into + Send, - E: TryInto + Send, - E::Error: Into + Send, - { - async move { - into!(key); - try_into!(start, end); - commands::streams::xrevrange(self, key, end, start, count) - .await? - .convert() - } - } - - /// Returns the number of entries inside a stream. - /// - /// - fn xlen(&self, key: K) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::streams::xlen(self, key).await?.convert() - } - } - - /// Read data from one or multiple streams, only returning entries with an ID greater than the last received ID - /// reported by the caller. - /// - /// - /// - /// The `XREAD` and `XREADGROUP` commands return values that can be interpreted differently in RESP2 and RESP3 mode. - /// In many cases it is also easier to operate on the return values of these functions as a `HashMap`, but - /// manually declaring this type can be very verbose. This function will automatically convert the response to the - /// [most common](crate::types::XReadResponse) map representation while also handling the encoding differences - /// between RESP2 and RESP3. - /// - /// ```rust no_run - /// # use fred::{prelude::*, types::XReadResponse}; - /// async fn example(client: RedisClient) -> Result<(), RedisError> { - /// // borrowed from the tests. XREAD and XREADGROUP are very similar. - /// let result: XReadResponse = client - /// .xreadgroup_map("group1", "consumer1", None, None, false, "foo", ">") - /// .await?; - /// println!("Result: {:?}", result); - /// // Result: {"foo": [("1646240801081-0", {"count": 0}), ("1646240801082-0", {"count": 1}), ("1646240801082-1", {"count": 2})]} - /// - /// assert_eq!(result.len(), 1); - /// for (idx, (id, record)) in result.get("foo").unwrap().into_iter().enumerate() { - /// let value = record.get("count").expect("Failed to read count"); - /// assert_eq!(idx, *value); - /// } - /// - /// Ok(()) - /// } - /// ``` - // The underlying issue here isn't so much a semantic difference between RESP2 and RESP3, but rather an assumption - // that went into the logic behind the `FromRedis` trait. - // - // In all other Redis commands that return "maps" in RESP2 (or responses that should be interpreted as maps) a map - // is encoded as an array with an even number of elements representing `(key, value)` pairs. - // - // As a result the `FromRedis` implementation for `HashMap`, `BTreeMap`, etc, took a dependency on this behavior. For example: https://redis.io/commands/hgetall#return-value - // - // ``` - // 127.0.0.1:6379> hset foo bar 0 - // (integer) 1 - // 127.0.0.1:6379> hset foo baz 1 - // (integer) 1 - // 127.0.0.1:6379> hgetall foo - // 1) "bar" - // 2) "0" - // 3) "baz" - // 4) "1" - // // now switch to RESP3 which has a specific type for maps on the wire - // 127.0.0.1:6379> hello 3 - // ... - // 127.0.0.1:6379> hgetall foo - // 1# "bar" => "0" - // 2# "baz" => "1" - // ``` - // - // However, with XREAD/XREADGROUP there's an extra array wrapper in RESP2 around both the "outer" map and "inner" - // map(s): - // - // ``` - // // RESP3 - // 127.0.0.1:6379> xread count 2 streams foo bar 1643479648480-0 1643479834990-0 - // 1# "foo" => 1) 1) "1643479650336-0" - // 2) 1) "count" - // 2) "3" - // 2# "bar" => 1) 1) "1643479837746-0" - // 2) 1) "count" - // 2) "5" - // 2) 1) "1643479925582-0" - // 2) 1) "count" - // 2) "6" - // - // // RESP2 - // 127.0.0.1:6379> xread count 2 streams foo bar 1643479648480-0 1643479834990-0 - // 1) 1) "foo" - // 2) 1) 1) "1643479650336-0" - // 2) 1) "count" - // 2) "3" - // 2) 1) "bar" - // 2) 1) 1) "1643479837746-0" - // 2) 1) "count" - // 2) "5" - // 2) 1) "1643479925582-0" - // 2) 1) "count" - // 2) "6" - // ``` - // - // The underlying functions that do the RESP2 vs RESP3 conversion are public for callers as well, so one could use a - // `BTreeMap` instead of a `HashMap` like so: - // - // ``` - // let value: BTreeMap)>> = client - // .xread::(None, None, "foo", "0") - // .await? - // .flatten_array_values(2) - // .convert()?; - // ``` - fn xread_map( - &self, - count: Option, - block: Option, - keys: K, - ids: I, - ) -> impl Future>> + Send - where - Rk1: FromRedisKey + Hash + Eq, - Rk2: FromRedis, - Rk3: FromRedisKey + Hash + Eq, - Rv: FromRedis, - K: Into + Send, - I: Into + Send, - { - async move { - into!(keys, ids); - commands::streams::xread(self, count, block, keys, ids) - .await? - .into_xread_response() - } - } - - /// Read data from one or multiple streams, only returning entries with an ID greater than the last received ID - /// reported by the caller. - /// - /// - /// - /// **See [xread_map](Self::xread_map) for more information on a variation of this function that might be more - /// useful.** - fn xread( - &self, - count: Option, - block: Option, - keys: K, - ids: I, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - I: Into + Send, - { - async move { - into!(keys, ids); - commands::streams::xread(self, count, block, keys, ids).await?.convert() - } - } - - /// This command creates a new consumer group uniquely identified by `groupname` for the stream stored at `key`. - /// - /// - fn xgroup_create( - &self, - key: K, - groupname: S, - id: I, - mkstream: bool, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - S: Into + Send, - I: Into + Send, - { - async move { - into!(key, groupname, id); - commands::streams::xgroup_create(self, key, groupname, id, mkstream) - .await? - .convert() - } - } - - /// Create a consumer named `consumername` in the consumer group `groupname` of the stream that's stored at `key`. - /// - /// - fn xgroup_createconsumer( - &self, - key: K, - groupname: G, - consumername: C, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - G: Into + Send, - C: Into + Send, - { - async move { - into!(key, groupname, consumername); - commands::streams::xgroup_createconsumer(self, key, groupname, consumername) - .await? - .convert() - } - } - - /// Delete a consumer named `consumername` in the consumer group `groupname` of the stream that's stored at `key`. - /// - /// - fn xgroup_delconsumer( - &self, - key: K, - groupname: G, - consumername: C, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - G: Into + Send, - C: Into + Send, - { - async move { - into!(key, groupname, consumername); - commands::streams::xgroup_delconsumer(self, key, groupname, consumername) - .await? - .convert() - } - } - - /// Completely destroy a consumer group. - /// - /// - fn xgroup_destroy(&self, key: K, groupname: S) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - S: Into + Send, - { - async move { - into!(key, groupname); - commands::streams::xgroup_destroy(self, key, groupname).await?.convert() - } - } - - /// Set the last delivered ID for a consumer group. - /// - /// - fn xgroup_setid(&self, key: K, groupname: S, id: I) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - S: Into + Send, - I: Into + Send, - { - async move { - into!(key, groupname, id); - commands::streams::xgroup_setid(self, key, groupname, id) - .await? - .convert() - } - } - - /// A special version of the `XREAD` command with support for consumer groups. - /// - /// Declaring proper type declarations for this command can be complicated due to the complex nature of the response - /// values and the differences between RESP2 and RESP3. See the [xread](Self::xread) documentation for more - /// information. - /// - /// - /// - /// The `XREAD` and `XREADGROUP` commands return values that can be interpreted differently in RESP2 and RESP3 mode. - /// In many cases it is also easier to operate on the return values of these functions as a `HashMap`, but - /// manually declaring this type can be very verbose. This function will automatically convert the response to the - /// [most common](crate::types::XReadResponse) map representation while also handling the encoding differences - /// between RESP2 and RESP3. - /// - /// See the [xread_map](Self::xread_map) documentation for more information. - // See the `xread_map` source docs for more information. - fn xreadgroup_map( - &self, - group: G, - consumer: C, - count: Option, - block: Option, - noack: bool, - keys: K, - ids: I, - ) -> impl Future>> + Send - where - Rk1: FromRedisKey + Hash + Eq, - Rk2: FromRedis, - Rk3: FromRedisKey + Hash + Eq, - Rv: FromRedis, - G: Into + Send, - C: Into + Send, - K: Into + Send, - I: Into + Send, - { - async move { - into!(group, consumer, keys, ids); - commands::streams::xreadgroup(self, group, consumer, count, block, noack, keys, ids) - .await? - .into_xread_response() - } - } - - /// A special version of the `XREAD` command with support for consumer groups. - /// - /// Declaring proper type declarations for this command can be complicated due to the complex nature of the response - /// values and the differences between RESP2 and RESP3. See the [xread](Self::xread) documentation for more - /// information. - /// - /// - /// - /// **See [xreadgroup_map](Self::xreadgroup_map) for a variation of this function that might be more useful.** - fn xreadgroup( - &self, - group: G, - consumer: C, - count: Option, - block: Option, - noack: bool, - keys: K, - ids: I, - ) -> impl Future> + Send - where - R: FromRedis, - G: Into + Send, - C: Into + Send, - K: Into + Send, - I: Into + Send, - { - async move { - into!(group, consumer, keys, ids); - commands::streams::xreadgroup(self, group, consumer, count, block, noack, keys, ids) - .await? - .convert() - } - } - - /// Remove one or more messages from the Pending Entries List (PEL) of a stream consumer group. - /// - /// - fn xack(&self, key: K, group: G, ids: I) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - G: Into + Send, - I: Into + Send, - { - async move { - into!(key, group, ids); - commands::streams::xack(self, key, group, ids).await?.convert() - } - } - - /// A variation of [xclaim](Self::xclaim) with a less verbose return type. - fn xclaim_values( - &self, - key: K, - group: G, - consumer: C, - min_idle_time: u64, - ids: I, - idle: Option, - time: Option, - retry_count: Option, - force: bool, - justid: bool, - ) -> impl Future>>> + Send - where - Ri: FromRedis, - Rk: FromRedisKey + Hash + Eq, - Rv: FromRedis, - K: Into + Send, - G: Into + Send, - C: Into + Send, - I: Into + Send, - { - async move { - into!(key, group, consumer, ids); - commands::streams::xclaim( - self, - key, - group, - consumer, - min_idle_time, - ids, - idle, - time, - retry_count, - force, - justid, - ) - .await? - .into_xread_value() - } - } - - /// In the context of a stream consumer group, this command changes the ownership of a pending message, - /// so that the new owner is the consumer specified as the command argument. - /// - /// - /// - /// **See [xclaim_values](Self::xclaim_values) for a variation of this function that might be more useful.** - fn xclaim( - &self, - key: K, - group: G, - consumer: C, - min_idle_time: u64, - ids: I, - idle: Option, - time: Option, - retry_count: Option, - force: bool, - justid: bool, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - G: Into + Send, - C: Into + Send, - I: Into + Send, - { - async move { - into!(key, group, consumer, ids); - commands::streams::xclaim( - self, - key, - group, - consumer, - min_idle_time, - ids, - idle, - time, - retry_count, - force, - justid, - ) - .await? - .convert() - } - } - - /// This command transfers ownership of pending stream entries that match the specified criteria. It also converts - /// the response type to a less verbose type declaration and handles potential differences between RESP2 and RESP3. - /// - /// - // FIXME: this type declaration wont work for Redis v7. Probably need a new FF for this... - fn xautoclaim_values( - &self, - key: K, - group: G, - consumer: C, - min_idle_time: u64, - start: I, - count: Option, - justid: bool, - ) -> impl Future>)>> + Send - where - Ri: FromRedis, - Rk: FromRedisKey + Hash + Eq, - Rv: FromRedis, - K: Into + Send, - G: Into + Send, - C: Into + Send, - I: Into + Send, - { - async move { - into!(key, group, consumer, start); - commands::streams::xautoclaim(self, key, group, consumer, min_idle_time, start, count, justid) - .await? - .into_xautoclaim_values() - } - } - - /// This command transfers ownership of pending stream entries that match the specified criteria. - /// - /// - /// - /// **Note: See [xautoclaim_values](Self::xautoclaim_values) for a variation of this function that may be more - /// useful.** - fn xautoclaim( - &self, - key: K, - group: G, - consumer: C, - min_idle_time: u64, - start: I, - count: Option, - justid: bool, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - G: Into + Send, - C: Into + Send, - I: Into + Send, - { - async move { - into!(key, group, consumer, start); - commands::streams::xautoclaim(self, key, group, consumer, min_idle_time, start, count, justid) - .await? - .convert() - } - } - - /// Inspect the list of pending messages in a consumer group. - /// - /// - fn xpending(&self, key: K, group: G, args: A) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - G: Into + Send, - A: Into + Send, - { - async move { - into!(key, group, args); - commands::streams::xpending(self, key, group, args).await?.convert() - } - } -} diff --git a/src/commands/interfaces/strings.rs b/src/commands/interfaces/strings.rs deleted file mode 100644 index 8b137891..00000000 --- a/src/commands/interfaces/strings.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/commands/interfaces/timeseries.rs b/src/commands/interfaces/timeseries.rs deleted file mode 100644 index b463b826..00000000 --- a/src/commands/interfaces/timeseries.rs +++ /dev/null @@ -1,529 +0,0 @@ -use crate::{ - commands, - interfaces::ClientLike, - prelude::{RedisError, RedisKey, RedisResult}, - types::{ - Aggregator, - DuplicatePolicy, - Encoding, - FromRedis, - GetLabels, - GetTimestamp, - GroupBy, - RangeAggregation, - RedisMap, - Timestamp, - }, -}; -use bytes_utils::Str; -use futures::Future; -use rm_send_macros::rm_send_if; - -/// A [Redis Timeseries](https://github.com/RedisTimeSeries/RedisTimeSeries/) interface. -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -#[rm_send_if(feature = "glommio")] -pub trait TimeSeriesInterface: ClientLike { - /// Append a sample to a time series. - /// - /// - fn ts_add( - &self, - key: K, - timestamp: T, - value: f64, - retention: Option, - encoding: Option, - chunk_size: Option, - on_duplicate: Option, - labels: L, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - T: TryInto + Send, - T::Error: Into + Send, - L: TryInto + Send, - L::Error: Into, - { - async move { - into!(key); - try_into!(timestamp, labels); - commands::timeseries::ts_add( - self, - key, - timestamp, - value, - retention, - encoding, - chunk_size, - on_duplicate, - labels, - ) - .await? - .convert() - } - } - - /// Update the retention, chunk size, duplicate policy, and labels of an existing time series. - /// - /// - fn ts_alter( - &self, - key: K, - retention: Option, - chunk_size: Option, - duplicate_policy: Option, - labels: L, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - L: TryInto + Send, - L::Error: Into, - { - async move { - into!(key); - try_into!(labels); - commands::timeseries::ts_alter(self, key, retention, chunk_size, duplicate_policy, labels) - .await? - .convert() - } - } - - /// Create a new time series. - /// - /// - fn ts_create( - &self, - key: K, - retention: Option, - encoding: Option, - chunk_size: Option, - duplicate_policy: Option, - labels: L, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - L: TryInto + Send, - L::Error: Into, - { - async move { - into!(key); - try_into!(labels); - commands::timeseries::ts_create(self, key, retention, encoding, chunk_size, duplicate_policy, labels) - .await? - .convert() - } - } - - /// Create a compaction rule. - /// - /// - fn ts_createrule( - &self, - src: S, - dest: D, - aggregation: (Aggregator, u64), - align_timestamp: Option, - ) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - D: Into + Send, - { - async move { - into!(src, dest); - commands::timeseries::ts_createrule(self, src, dest, aggregation, align_timestamp) - .await? - .convert() - } - } - - /// Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal - /// to the value of the sample with the maximum existing timestamp with a given decrement. - /// - /// - fn ts_decrby( - &self, - key: K, - subtrahend: f64, - timestamp: Option, - retention: Option, - uncompressed: bool, - chunk_size: Option, - labels: L, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - L: TryInto + Send, - L::Error: Into + Send, - { - async move { - into!(key); - try_into!(labels); - commands::timeseries::ts_decrby( - self, - key, - subtrahend, - timestamp, - retention, - uncompressed, - chunk_size, - labels, - ) - .await? - .convert() - } - } - - /// Delete all samples between two timestamps for a given time series. - /// - /// - fn ts_del(&self, key: K, from: i64, to: i64) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::timeseries::ts_del(self, key, from, to).await?.convert() - } - } - - /// Delete a compaction rule. - /// - /// - fn ts_deleterule(&self, src: S, dest: D) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - D: Into + Send, - { - async move { - into!(src, dest); - commands::timeseries::ts_deleterule(self, src, dest).await?.convert() - } - } - - /// Get the sample with the highest timestamp from a given time series. - /// - /// - fn ts_get(&self, key: K, latest: bool) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::timeseries::ts_get(self, key, latest).await?.convert() - } - } - - /// Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal - /// to the value of the sample with the maximum existing timestamp with a given increment. - /// - /// - fn ts_incrby( - &self, - key: K, - addend: f64, - timestamp: Option, - retention: Option, - uncompressed: bool, - chunk_size: Option, - labels: L, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - L: TryInto + Send, - L::Error: Into + Send, - { - async move { - into!(key); - try_into!(labels); - commands::timeseries::ts_incrby( - self, - key, - addend, - timestamp, - retention, - uncompressed, - chunk_size, - labels, - ) - .await? - .convert() - } - } - - /// Return information and statistics for a time series. - /// - /// - fn ts_info(&self, key: K, debug: bool) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - { - async move { - into!(key); - commands::timeseries::ts_info(self, key, debug).await?.convert() - } - } - - /// Append new samples to one or more time series. - /// - /// - fn ts_madd(&self, samples: I) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - I: IntoIterator + Send, - { - async move { - let samples: Vec<_> = samples - .into_iter() - .map(|(key, ts, val)| (key.into(), ts, val)) - .collect(); - - commands::timeseries::ts_madd(self, samples).await?.convert() - } - } - - /// Get the sample with the highest timestamp from each time series matching a specific filter. - /// - /// See [Resp2TimeSeriesValues](crate::types::Resp2TimeSeriesValues) and - /// [Resp3TimeSeriesValues](crate::types::Resp3TimeSeriesValues) for more information. - /// - /// - fn ts_mget( - &self, - latest: bool, - labels: Option, - filters: I, - ) -> impl Future> + Send - where - R: FromRedis, - L: Into + Send, - S: Into + Send, - I: IntoIterator + Send, - { - async move { - let labels = labels.map(|l| l.into()); - let filters = filters.into_iter().map(|s| s.into()).collect(); - - commands::timeseries::ts_mget(self, latest, labels, filters) - .await? - .convert() - } - } - - /// Query a range across multiple time series by filters in the forward direction. - /// - /// See [Resp2TimeSeriesValues](crate::types::Resp2TimeSeriesValues) and - /// [Resp3TimeSeriesValues](crate::types::Resp3TimeSeriesValues) for more information. - /// - /// - fn ts_mrange( - &self, - from: F, - to: T, - latest: bool, - filter_by_ts: I, - filter_by_value: Option<(i64, i64)>, - labels: Option, - count: Option, - aggregation: Option, - filters: J, - group_by: Option, - ) -> impl Future> + Send - where - R: FromRedis, - F: TryInto + Send, - F::Error: Into + Send, - T: TryInto + Send, - T::Error: Into + Send, - S: Into + Send, - I: IntoIterator + Send, - J: IntoIterator + Send, - { - async move { - try_into!(from, to); - let filters = filters.into_iter().map(|s| s.into()).collect(); - let filter_by_ts = filter_by_ts.into_iter().collect(); - - commands::timeseries::ts_mrange( - self, - from, - to, - latest, - filter_by_ts, - filter_by_value, - labels, - count, - aggregation, - filters, - group_by, - ) - .await? - .convert() - } - } - - /// Query a range across multiple time series by filters in the reverse direction. - /// - /// See [Resp2TimeSeriesValues](crate::types::Resp2TimeSeriesValues) and - /// [Resp3TimeSeriesValues](crate::types::Resp3TimeSeriesValues) for more information. - /// - /// - fn ts_mrevrange( - &self, - from: F, - to: T, - latest: bool, - filter_by_ts: I, - filter_by_value: Option<(i64, i64)>, - labels: Option, - count: Option, - aggregation: Option, - filters: J, - group_by: Option, - ) -> impl Future> + Send - where - R: FromRedis, - F: TryInto + Send, - F::Error: Into + Send, - T: TryInto + Send, - T::Error: Into + Send, - S: Into + Send, - I: IntoIterator + Send, - J: IntoIterator + Send, - { - async move { - try_into!(from, to); - let filters = filters.into_iter().map(|s| s.into()).collect(); - let filter_by_ts = filter_by_ts.into_iter().collect(); - - commands::timeseries::ts_mrevrange( - self, - from, - to, - latest, - filter_by_ts, - filter_by_value, - labels, - count, - aggregation, - filters, - group_by, - ) - .await? - .convert() - } - } - - /// Get all time series keys matching a filter list. - /// - /// - fn ts_queryindex(&self, filters: I) -> impl Future> + Send - where - R: FromRedis, - S: Into + Send, - I: IntoIterator + Send, - { - async move { - let filters = filters.into_iter().map(|s| s.into()).collect(); - commands::timeseries::ts_queryindex(self, filters).await?.convert() - } - } - - /// Query a range in forward direction. - /// - /// - fn ts_range( - &self, - key: K, - from: F, - to: T, - latest: bool, - filter_by_ts: I, - filter_by_value: Option<(i64, i64)>, - count: Option, - aggregation: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - F: TryInto + Send, - F::Error: Into + Send, - T: TryInto + Send, - T::Error: Into + Send, - I: IntoIterator + Send, - { - async move { - into!(key); - try_into!(from, to); - let filter_by_ts = filter_by_ts.into_iter().collect(); - - commands::timeseries::ts_range( - self, - key, - from, - to, - latest, - filter_by_ts, - filter_by_value, - count, - aggregation, - ) - .await? - .convert() - } - } - - /// Query a range in reverse direction. - /// - /// - fn ts_revrange( - &self, - key: K, - from: F, - to: T, - latest: bool, - filter_by_ts: I, - filter_by_value: Option<(i64, i64)>, - count: Option, - aggregation: Option, - ) -> impl Future> + Send - where - R: FromRedis, - K: Into + Send, - F: TryInto + Send, - F::Error: Into + Send, - T: TryInto + Send, - T::Error: Into + Send, - I: IntoIterator + Send, - { - async move { - into!(key); - try_into!(from, to); - let filter_by_ts = filter_by_ts.into_iter().collect(); - - commands::timeseries::ts_revrange( - self, - key, - from, - to, - latest, - filter_by_ts, - filter_by_value, - count, - aggregation, - ) - .await? - .convert() - } - } -} diff --git a/src/commands/interfaces/tracking.rs b/src/commands/interfaces/tracking.rs deleted file mode 100644 index a41eb149..00000000 --- a/src/commands/interfaces/tracking.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::{ - commands, - interfaces::ClientLike, - prelude::RedisResult, - runtime::{spawn, BroadcastReceiver, JoinHandle}, - types::{Invalidation, MultipleStrings}, -}; -use futures::Future; -use rm_send_macros::rm_send_if; - -/// A high level interface that supports [client side caching](https://redis.io/docs/manual/client-side-caching/) via the [client tracking](https://redis.io/commands/client-tracking/) interface. -#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] -#[rm_send_if(feature = "glommio")] -pub trait TrackingInterface: ClientLike + Sized { - /// Send the [CLIENT TRACKING](https://redis.io/commands/client-tracking/) command to all connected servers, subscribing to [invalidation messages](Self::on_invalidation) on the same connection. - /// - /// This interface requires the RESP3 protocol mode and supports all server deployment types (centralized, - /// clustered, and sentinel). - /// - /// See the basic [client tracking](crate::interfaces::ClientInterface::client_tracking) function for more - /// information on the underlying commands. - fn start_tracking

( - &self, - prefixes: P, - bcast: bool, - optin: bool, - optout: bool, - noloop: bool, - ) -> impl Future> + Send - where - P: Into + Send, - { - async move { - into!(prefixes); - commands::tracking::start_tracking(self, prefixes, bcast, optin, optout, noloop).await - } - } - - /// Disable client tracking on all connections. - fn stop_tracking(&self) -> impl Future> + Send { - async move { commands::tracking::stop_tracking(self).await } - } - - /// Spawn a task that processes invalidation messages from the server. - /// - /// See [invalidation_rx](Self::invalidation_rx) for a more flexible variation of this function. - fn on_invalidation(&self, func: F) -> JoinHandle> - where - F: Fn(Invalidation) -> RedisResult<()> + Send + 'static, - { - let mut invalidation_rx = self.invalidation_rx(); - - spawn(async move { - let mut result = Ok(()); - - while let Ok(invalidation) = invalidation_rx.recv().await { - if let Err(err) = func(invalidation) { - result = Err(err); - break; - } - } - result - }) - } - - /// Subscribe to invalidation messages from the server(s). - fn invalidation_rx(&self) -> BroadcastReceiver { - self.inner().notifications.invalidations.load().subscribe() - } -} diff --git a/src/commands/interfaces/transactions.rs b/src/commands/interfaces/transactions.rs deleted file mode 100644 index c7e79bf7..00000000 --- a/src/commands/interfaces/transactions.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::{clients::Transaction, interfaces::ClientLike}; - -/// Functions that implement the [transactions](https://redis.io/commands#transactions) interface. -/// -/// See the [Transaction](crate::clients::Transaction) client for more information; -#[cfg(feature = "transactions")] -#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))] -pub trait TransactionInterface: ClientLike + Sized { - /// Enter a MULTI block, executing subsequent commands as a transaction. - /// - /// - fn multi(&self) -> Transaction { - Transaction::from_inner(self.inner()) - } -} diff --git a/src/commands/mod.rs b/src/commands/mod.rs deleted file mode 100644 index b5596797..00000000 --- a/src/commands/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod impls; -pub mod interfaces; -pub use impls::*; diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 43eac000..00000000 --- a/src/error.rs +++ /dev/null @@ -1,426 +0,0 @@ -use bytes_utils::string::Utf8Error as BytesUtf8Error; -use futures::channel::oneshot::Canceled; -use redis_protocol::{error::RedisProtocolError, resp2::types::BytesFrame as Resp2Frame}; -use semver::Error as SemverError; -use std::{ - borrow::{Borrow, Cow}, - convert::Infallible, - error::Error, - fmt, - io::Error as IoError, - num::{ParseFloatError, ParseIntError}, - str, - str::Utf8Error, - string::FromUtf8Error, -}; -use url::ParseError; - -/// An enum representing the type of error from Redis. -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum RedisErrorKind { - /// A fatal client configuration error. These errors will shutdown a client and break out of any reconnection - /// attempts. - Config, - /// An authentication error. - Auth, - /// An IO error with the underlying connection. - IO, - /// An invalid command, such as trying to perform a `set` command on a client after calling `subscribe`. - InvalidCommand, - /// An invalid argument or set of arguments to a command. - InvalidArgument, - /// An invalid URL error. - Url, - /// A protocol error such as an invalid or unexpected frame from the server. - Protocol, - /// A TLS error. - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - #[cfg_attr( - docsrs, - doc(cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))) - )] - Tls, - /// An error indicating the request was canceled. - Canceled, - /// An unknown error. - Unknown, - /// A timeout error. - Timeout, - /// An error used to indicate that the cluster's state has changed. These errors will show up on the `on_error` - /// error stream even though the client will automatically attempt to recover. - Cluster, - /// A parser error. - Parse, - /// An error communicating with redis sentinel. - Sentinel, - /// An error indicating a value was not found, often used when trying to cast a `nil` response from the server to a - /// non-nullable type. - NotFound, - /// An error indicating that the caller should apply backpressure and retry the command. - Backpressure, - /// An error associated with a replica node. - #[cfg(feature = "replicas")] - #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] - Replica, -} - -impl RedisErrorKind { - pub fn to_str(&self) -> &'static str { - match *self { - RedisErrorKind::Auth => "Authentication Error", - RedisErrorKind::IO => "IO Error", - RedisErrorKind::InvalidArgument => "Invalid Argument", - RedisErrorKind::InvalidCommand => "Invalid Command", - RedisErrorKind::Url => "Url Error", - RedisErrorKind::Protocol => "Protocol Error", - RedisErrorKind::Unknown => "Unknown Error", - RedisErrorKind::Canceled => "Canceled", - RedisErrorKind::Cluster => "Cluster Error", - RedisErrorKind::Timeout => "Timeout Error", - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - RedisErrorKind::Tls => "TLS Error", - RedisErrorKind::Config => "Config Error", - RedisErrorKind::Parse => "Parse Error", - RedisErrorKind::Sentinel => "Sentinel Error", - RedisErrorKind::NotFound => "Not Found", - RedisErrorKind::Backpressure => "Backpressure", - #[cfg(feature = "replicas")] - RedisErrorKind::Replica => "Replica", - } - } -} - -/// An error from Redis. -pub struct RedisError { - /// Details about the specific error condition. - details: Cow<'static, str>, - /// The kind of error. - kind: RedisErrorKind, -} - -impl Clone for RedisError { - fn clone(&self) -> Self { - RedisError::new(self.kind.clone(), self.details.clone()) - } -} - -impl PartialEq for RedisError { - fn eq(&self, other: &Self) -> bool { - self.kind == other.kind && self.details == other.details - } -} - -impl Eq for RedisError {} - -impl fmt::Debug for RedisError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Redis Error - kind: {:?}, details: {}", self.kind, self.details) - } -} - -impl fmt::Display for RedisError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: {}", self.kind.to_str(), self.details) - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(e: RedisProtocolError) -> Self { - RedisError::new(RedisErrorKind::Protocol, format!("{}", e)) - } -} - -#[doc(hidden)] -impl From<()> for RedisError { - fn from(_: ()) -> Self { - RedisError::new(RedisErrorKind::Canceled, "Empty error.") - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(e: futures::channel::mpsc::SendError) -> Self { - RedisError::new(RedisErrorKind::Unknown, format!("{}", e)) - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(e: tokio::sync::oneshot::error::RecvError) -> Self { - RedisError::new(RedisErrorKind::Unknown, format!("{}", e)) - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(e: tokio::sync::broadcast::error::RecvError) -> Self { - RedisError::new(RedisErrorKind::Unknown, format!("{}", e)) - } -} - -#[doc(hidden)] -impl From> for RedisError { - fn from(e: tokio::sync::broadcast::error::SendError) -> Self { - RedisError::new(RedisErrorKind::Unknown, format!("{}", e)) - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(e: IoError) -> Self { - RedisError::new(RedisErrorKind::IO, format!("{:?}", e)) - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(e: ParseError) -> Self { - RedisError::new(RedisErrorKind::Url, format!("{:?}", e)) - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(_: ParseFloatError) -> Self { - RedisError::new(RedisErrorKind::Parse, "Invalid floating point number.") - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(_: ParseIntError) -> Self { - RedisError::new(RedisErrorKind::Parse, "Invalid integer string.") - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(_: FromUtf8Error) -> Self { - RedisError::new(RedisErrorKind::Parse, "Invalid UTF-8 string.") - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(_: Utf8Error) -> Self { - RedisError::new(RedisErrorKind::Parse, "Invalid UTF-8 string.") - } -} - -#[doc(hidden)] -impl From> for RedisError { - fn from(e: BytesUtf8Error) -> Self { - e.utf8_error().into() - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(e: fmt::Error) -> Self { - RedisError::new(RedisErrorKind::Unknown, format!("{:?}", e)) - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(e: Canceled) -> Self { - RedisError::new(RedisErrorKind::Canceled, format!("{}", e)) - } -} - -#[doc(hidden)] -#[cfg(not(feature = "glommio"))] -impl From for RedisError { - fn from(e: tokio::task::JoinError) -> Self { - RedisError::new(RedisErrorKind::Unknown, format!("Spawn Error: {:?}", e)) - } -} - -#[doc(hidden)] -#[cfg(feature = "glommio")] -impl From> for RedisError { - fn from(e: glommio::GlommioError) -> Self { - RedisError::new(RedisErrorKind::Unknown, format!("{:?}", e)) - } -} - -#[doc(hidden)] -#[cfg(feature = "glommio")] -impl From for RedisError { - fn from(_: oneshot::RecvError) -> Self { - RedisError::new_canceled() - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(e: SemverError) -> Self { - RedisError::new(RedisErrorKind::Protocol, format!("Invalid Redis version: {:?}", e)) - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(e: Infallible) -> Self { - warn!("Infallible error: {:?}", e); - RedisError::new(RedisErrorKind::Unknown, "Unknown error.") - } -} - -#[doc(hidden)] -impl From for RedisError { - fn from(e: Resp2Frame) -> Self { - match e { - Resp2Frame::SimpleString(s) => match str::from_utf8(&s).ok() { - Some("Canceled") => RedisError::new_canceled(), - _ => RedisError::new(RedisErrorKind::Unknown, "Unknown frame error."), - }, - _ => RedisError::new(RedisErrorKind::Unknown, "Unknown frame error."), - } - } -} - -#[doc(hidden)] -#[cfg(feature = "enable-native-tls")] -#[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))] -impl From for RedisError { - fn from(e: native_tls::Error) -> Self { - RedisError::new(RedisErrorKind::Tls, format!("{:?}", e)) - } -} - -#[doc(hidden)] -#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] -#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))] -impl From for RedisError { - fn from(e: rustls::pki_types::InvalidDnsNameError) -> Self { - RedisError::new(RedisErrorKind::Tls, format!("{:?}", e)) - } -} - -#[doc(hidden)] -#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] -#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))] -impl From for RedisError { - fn from(e: rustls::Error) -> Self { - RedisError::new(RedisErrorKind::Tls, format!("{:?}", e)) - } -} - -#[doc(hidden)] -#[cfg(feature = "trust-dns-resolver")] -#[cfg_attr(docsrs, doc(cfg(feature = "trust-dns-resolver")))] -impl From for RedisError { - fn from(e: trust_dns_resolver::error::ResolveError) -> Self { - RedisError::new(RedisErrorKind::IO, format!("{:?}", e)) - } -} - -#[doc(hidden)] -#[cfg(feature = "dns")] -#[cfg_attr(docsrs, doc(cfg(feature = "dns")))] -impl From for RedisError { - fn from(e: hickory_resolver::error::ResolveError) -> Self { - RedisError::new(RedisErrorKind::IO, format!("{:?}", e)) - } -} - -#[cfg(feature = "serde-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde-json")))] -impl From for RedisError { - fn from(e: serde_json::Error) -> Self { - RedisError::new(RedisErrorKind::Parse, format!("{:?}", e)) - } -} - -impl RedisError { - /// Create a new Redis error with the provided details. - pub fn new(kind: RedisErrorKind, details: T) -> RedisError - where - T: Into>, - { - RedisError { - kind, - details: details.into(), - } - } - - /// Read the type of error without any associated data. - pub fn kind(&self) -> &RedisErrorKind { - &self.kind - } - - /// Change the kind of the error. - pub fn change_kind(&mut self, kind: RedisErrorKind) { - self.kind = kind; - } - - /// Read details about the error. - pub fn details(&self) -> &str { - self.details.borrow() - } - - /// Create a new empty Canceled error. - pub fn new_canceled() -> Self { - RedisError::new(RedisErrorKind::Canceled, "Canceled.") - } - - /// Create a new parse error with the provided details. - pub(crate) fn new_parse(details: T) -> Self - where - T: Into>, - { - RedisError::new(RedisErrorKind::Parse, details) - } - - /// Create a new default backpressure error. - pub(crate) fn new_backpressure() -> Self { - RedisError::new(RedisErrorKind::Backpressure, "Max in-flight commands reached.") - } - - /// Whether reconnection logic should be skipped in all cases. - pub(crate) fn should_not_reconnect(&self) -> bool { - matches!(self.kind, RedisErrorKind::Config | RedisErrorKind::Url) - } - - /// Whether the error is a `Cluster` error. - pub fn is_cluster(&self) -> bool { - matches!(self.kind, RedisErrorKind::Cluster) - } - - /// Whether the error is a `Canceled` error. - pub fn is_canceled(&self) -> bool { - matches!(self.kind, RedisErrorKind::Canceled) - } - - /// Whether the error is a `Replica` error. - #[cfg(feature = "replicas")] - #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] - pub fn is_replica(&self) -> bool { - matches!(self.kind, RedisErrorKind::Replica) - } - - /// Whether the error is a `NotFound` error. - pub fn is_not_found(&self) -> bool { - matches!(self.kind, RedisErrorKind::NotFound) - } -} - -impl Error for RedisError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - None - } -} diff --git a/src/glommio/README.md b/src/glommio/README.md deleted file mode 100644 index f2e8be21..00000000 --- a/src/glommio/README.md +++ /dev/null @@ -1,67 +0,0 @@ -## Runtime Differences - -Tokio and Glommio have several important differences that can affect the public interface of client libraries like -`fred`, most notably that Tokio typically uses a work-stealing scheduler whereas Glommio intentionally -discourages moving tasks or state across threads. For more information see -the [Glommio Introduction](https://www.datadoghq.com/blog/engineering/introducing-glommio/). - -This fundamental difference manifests in several ways, for example: - -* [tokio::spawn](https://docs.rs/tokio/latest/tokio/task/fn.spawn.html) requires a `Send + 'static` bound on anything - that moves in to or out of the task being spawned. This makes sense since the Tokio scheduler may choose to move tasks - across thread boundaries for work stealing purposes. -* In general Glommio prefers that each scheduler thread share nothing with other scheduler threads, typically - implemented via the [sharding](https://docs.rs/glommio/latest/glommio/channels/sharding/index.html) interface. As a - result, the [task spawning interfaces](https://docs.rs/glommio/latest/glommio/index.html#functions) such - as [spawn_local](https://docs.rs/glommio/latest/glommio/fn.spawn_local.html) - and [spawn_local_into](https://docs.rs/glommio/latest/glommio/fn.spawn_local_into.html), are designed to - spawn tasks on the same thread and therefore do not have - a `Send` bound. - -`fred` was originally written with message-passing design patterns targeting a Tokio runtime and therefore the `Send` -bound from `tokio::spawn` leaked into all the public interfaces that send messages across tasks. This includes pretty -much everything in the public interface: - -* All the `*Interface` traits that implement Redis commands. At the moment this includes something like ~200 functions. -* The base `ClientLike` trait behind all the public client and pool types. - -See the [next section](#generic-interfaces-with-glommio) for more information. - -Additionally, Glommio uses or requires some different implementations of some core interfaces used inside `fred`, -such as: - -* [tokio::sync::oneshot](https://docs.rs/tokio/latest/tokio/sync/oneshot/index.html) must change - to [oneshot](https://crates.io/crates/oneshot). -* [tokio_util::codec](https://docs.rs/tokio-util/latest/tokio_util/codec/index.html) needs a compatibility layer or a - re-implementation of `Encoder+Decoder` on top of the `AsyncRead+AsyncWrite` traits in `futures-io`. Currently, Tokio - re-implements these traits internally. The compatibility layer in [src/glommio/io_compat.rs](./io_compat.rs) also - allows `tokio-native-tls` and `tokio-rustls` to work with Glommio's `TcpStream`. -* Most tokio message passing `send` functions are synchronous, but most glommio `send` functions are async. To work - around this the compat layer uses unbounded channels - with [try_send](https://docs.rs/glommio/latest/glommio/channels/local_channel/struct.LocalSender.html#method.try_send). - -Generally speaking callers won't be affected by these implementation details, but it is worth noting that `fred` still -requires some Tokio functionality (such as `select!`) when using Glommio. See [the compat layer](./compat.rs) for more -info. - -## Generic Interfaces with Glommio - -When building with `--features glommio` the public interfaces mentioned above will lose the `Send` bounds on all the -generic input parameters, where clause predicates, and `impl Trait` return types. This is done via -the [rm-send-macros](https://github.com/aembke/rm-send-macros) crate. The public docs will still show the `Send` bounds, -but callers can rebuild docs manually with the `glommio` feature to see the new function signatures. - -``` -cargo rustdoc --features "glommio i-all i-redis-stack" --open -- --cfg docsrs -``` - -In the future this approach may change to use separate interface traits for Glommio builds. - -The modules in this folder try to bridge any compatibility gaps between the various message passing interfaces in Tokio -and Glommio. - -## TODO - -* Counters no longer need to be atomic when ClientLike is not Send. -* All the parking_lot locks can change to RefCell. -* Support the `ExclusivePool` interface with Glommio. Currently there's no equivalent to Tokio's `OwnedMutexGuard`. \ No newline at end of file diff --git a/src/glommio/broadcast.rs b/src/glommio/broadcast.rs deleted file mode 100644 index 93424103..00000000 --- a/src/glommio/broadcast.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::error::RedisError; -use glommio::{ - channels::local_channel::{new_unbounded, LocalReceiver, LocalSender}, - GlommioError, - ResourceType, -}; -use std::{cell::RefCell, collections::BTreeMap, rc::Rc}; - -struct Inner { - pub counter: u64, - pub senders: BTreeMap>, -} - -/// A multi-producer multi-consumer channel receiver. -/// -/// See [LocalReceiver](glommio::channels::local_channel::LocalReceiver) for more information. -pub struct BroadcastReceiver { - id: u64, - inner: Rc>>, - rx: LocalReceiver, -} - -impl BroadcastReceiver { - /// Receives data from this channel. - /// - /// See [recv](glommio::channels::local_channel::LocalReceiver::recv) for more information. - pub async fn recv(&self) -> Result { - match self.rx.recv().await { - Some(v) => Ok(v), - None => Err(RedisError::new_canceled()), - } - } -} - -impl Drop for BroadcastReceiver { - fn drop(&mut self) { - self.inner.as_ref().borrow_mut().senders.remove(&self.id); - } -} - -#[derive(Clone)] -pub struct BroadcastSender { - inner: Rc>>, -} - -impl BroadcastSender { - pub fn new() -> Self { - BroadcastSender { - inner: Rc::new(RefCell::new(Inner { - counter: 0, - senders: BTreeMap::new(), - })), - } - } - - pub fn subscribe(&self) -> BroadcastReceiver { - let (tx, rx) = new_unbounded(); - let id = { - let mut guard = self.inner.as_ref().borrow_mut(); - let count = guard.counter.wrapping_add(1); - guard.counter = count; - guard.senders.insert(count, tx); - guard.counter - }; - - BroadcastReceiver { - id, - rx, - inner: self.inner.clone(), - } - } - - pub fn send(&self, msg: &T, func: F) { - let mut guard = self.inner.as_ref().borrow_mut(); - - let to_remove: Vec = guard - .senders - .iter() - .filter_map(|(id, tx)| { - if let Err(GlommioError::Closed(ResourceType::Channel(val))) = tx.try_send(msg.clone()) { - func(&val); - Some(*id) - } else { - None - } - }) - .collect(); - - for id in to_remove.into_iter() { - guard.senders.remove(&id); - } - } -} diff --git a/src/glommio/interfaces.rs b/src/glommio/interfaces.rs deleted file mode 100644 index 20d1225a..00000000 --- a/src/glommio/interfaces.rs +++ /dev/null @@ -1,354 +0,0 @@ -use crate::{ - clients::WithOptions, - commands, - error::RedisError, - interfaces::{RedisResult, Resp3Frame}, - modules::inner::RedisClientInner, - prelude::default_send_command, - protocol::command::RedisCommand, - router::commands as router_commands, - runtime::{spawn, BroadcastReceiver, JoinHandle, RefCount}, - types::{ - ClientState, - ConnectHandle, - ConnectionConfig, - CustomCommand, - FromRedis, - InfoKind, - Options, - PerformanceConfig, - ReconnectPolicy, - RedisConfig, - RedisValue, - Server, - }, - utils, -}; -use redis_protocol::resp3::types::RespVersion; -use semver::Version; -use std::future::Future; - -#[cfg(feature = "i-server")] -use crate::types::ShutdownFlags; - -#[cfg(any(feature = "dns", feature = "trust-dns-resolver"))] -use crate::protocol::types::Resolve; - -pub trait ClientLike: Clone + Sized { - #[doc(hidden)] - fn inner(&self) -> &RefCount; - - /// Helper function to intercept and modify a command without affecting how it is sent to the connection layer. - #[doc(hidden)] - fn change_command(&self, _: &mut RedisCommand) {} - - /// Helper function to intercept and customize how a command is sent to the connection layer. - #[doc(hidden)] - fn send_command(&self, command: C) -> Result<(), RedisError> - where - C: Into, - { - let mut command: RedisCommand = command.into(); - self.change_command(&mut command); - default_send_command(self.inner(), command) - } - - /// The unique ID identifying this client and underlying connections. - fn id(&self) -> &str { - &self.inner().id - } - - /// Read the config used to initialize the client. - fn client_config(&self) -> RedisConfig { - self.inner().config.as_ref().clone() - } - - /// Read the reconnect policy used to initialize the client. - fn client_reconnect_policy(&self) -> Option { - self.inner().policy.read().clone() - } - - /// Read the connection config used to initialize the client. - fn connection_config(&self) -> &ConnectionConfig { - self.inner().connection.as_ref() - } - - /// Read the RESP version used by the client when communicating with the server. - fn protocol_version(&self) -> RespVersion { - if self.inner().is_resp3() { - RespVersion::RESP3 - } else { - RespVersion::RESP2 - } - } - - /// Whether the client has a reconnection policy. - fn has_reconnect_policy(&self) -> bool { - self.inner().policy.read().is_some() - } - - /// Whether the client will automatically pipeline commands. - fn is_pipelined(&self) -> bool { - self.inner().is_pipelined() - } - - /// Whether the client is connected to a cluster. - fn is_clustered(&self) -> bool { - self.inner().config.server.is_clustered() - } - - /// Whether the client uses the sentinel interface. - fn uses_sentinels(&self) -> bool { - self.inner().config.server.is_sentinel() - } - - /// Update the internal [PerformanceConfig](crate::types::PerformanceConfig) in place with new values. - fn update_perf_config(&self, config: PerformanceConfig) { - self.inner().update_performance_config(config); - } - - /// Read the [PerformanceConfig](crate::types::PerformanceConfig) associated with this client. - fn perf_config(&self) -> PerformanceConfig { - self.inner().performance_config() - } - - /// Read the state of the underlying connection(s). - /// - /// If running against a cluster the underlying state will reflect the state of the least healthy connection. - fn state(&self) -> ClientState { - self.inner().state.read().clone() - } - - /// Whether all underlying connections are healthy. - fn is_connected(&self) -> bool { - *self.inner().state.read() == ClientState::Connected - } - - /// Read the set of active connections managed by the client. - fn active_connections(&self) -> impl Future, RedisError>> { - commands::server::active_connections(self) - } - - /// Read the server version, if known. - fn server_version(&self) -> Option { - self.inner().server_state.read().kind.server_version() - } - - /// Override the DNS resolution logic for the client. - #[cfg(feature = "dns")] - #[cfg_attr(docsrs, doc(cfg(feature = "dns")))] - fn set_resolver(&self, resolver: RefCount) -> impl Future { - async move { self.inner().set_resolver(resolver).await } - } - - /// Connect to the server. - /// - /// This function returns a `JoinHandle` to a task that drives the connection. It will not resolve until the - /// connection closes, or if a reconnection policy with unlimited attempts is provided then it will - /// run until `QUIT` is called. Callers should avoid calling [abort](tokio::task::JoinHandle::abort) on the returned - /// `JoinHandle` unless the client will no longer be used. - /// - /// **Calling this function more than once will drop all state associated with the previous connection(s).** Any - /// pending commands on the old connection(s) will either finish or timeout, but they will not be retried on the - /// new connection(s). - /// - /// See [init](Self::init) for an alternative shorthand. - fn connect(&self) -> ConnectHandle { - let inner = self.inner().clone(); - utils::reset_router_task(&inner); - - spawn(async move { - utils::clear_backchannel_state(&inner).await; - let result = router_commands::start(&inner).await; - // a canceled error means we intentionally closed the client - _trace!(inner, "Ending connection task with {:?}", result); - - if let Err(ref error) = result { - if !error.is_canceled() { - inner.notifications.broadcast_connect(Err(error.clone())); - } - } - - utils::check_and_set_client_state(&inner.state, ClientState::Disconnecting, ClientState::Disconnected); - result - }) - } - - /// Force a reconnection to the server(s). - /// - /// When running against a cluster this function will also refresh the cached cluster routing table. - fn force_reconnection(&self) -> impl Future> { - async move { commands::server::force_reconnection(self.inner()).await } - } - - /// Wait for the result of the next connection attempt. - /// - /// This can be used with `on_reconnect` to separate initialization logic that needs to occur only on the next - /// connection attempt vs all subsequent attempts. - fn wait_for_connect(&self) -> impl Future> { - async move { - if { utils::read_locked(&self.inner().state) } == ClientState::Connected { - debug!("{}: Client is already connected.", self.inner().id); - Ok(()) - } else { - let rx = { self.inner().notifications.connect.load().subscribe() }; - rx.recv().await? - } - } - } - - /// Initialize a new routing and connection task and wait for it to connect successfully. - /// - /// The returned [ConnectHandle](crate::types::ConnectHandle) refers to the task that drives the routing and - /// connection layer. It will not finish until the max reconnection count is reached. Callers should avoid calling - /// [abort](tokio::task::JoinHandle::abort) on the returned `JoinHandle` unless the client will no longer be used. - /// - /// Callers can also use [connect](Self::connect) and [wait_for_connect](Self::wait_for_connect) separately if - /// needed. - /// - /// ```rust - /// use fred::prelude::*; - /// - /// #[tokio::main] - /// async fn main() -> Result<(), RedisError> { - /// let client = RedisClient::default(); - /// let connection_task = client.init().await?; - /// - /// // ... - /// - /// client.quit().await?; - /// connection_task.await? - /// } - /// ``` - fn init(&self) -> impl Future> { - async move { - let rx = { self.inner().notifications.connect.load().subscribe() }; - let task = self.connect(); - let error = rx.recv().await.map_err(RedisError::from).and_then(|r| r).err(); - - if let Some(error) = error { - // the initial connection failed, so we should gracefully close the routing task - utils::reset_router_task(self.inner()); - Err(error) - } else { - Ok(task) - } - } - } - - /// Close the connection to the Redis server. The returned future resolves when the command has been written to the - /// socket, not when the connection has been fully closed. Some time after this future resolves the future - /// returned by [connect](Self::connect) will resolve which indicates that the connection has been fully closed. - /// - /// This function will also close all error, pubsub message, and reconnection event streams. - fn quit(&self) -> impl Future> { - async move { commands::server::quit(self).await } - } - - /// Shut down the server and quit the client. - /// - /// - #[cfg(feature = "i-server")] - #[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] - fn shutdown(&self, flags: Option) -> impl Future> { - async move { commands::server::shutdown(self, flags).await } - } - - /// Delete the keys in all databases. - /// - /// - fn flushall(&self, r#async: bool) -> impl Future> - where - R: FromRedis, - { - async move { commands::server::flushall(self, r#async).await?.convert() } - } - - /// Delete the keys on all nodes in the cluster. This is a special function that does not map directly to the Redis - /// interface. - fn flushall_cluster(&self) -> impl Future> { - async move { commands::server::flushall_cluster(self).await } - } - - /// Ping the Redis server. - /// - /// - fn ping(&self) -> impl Future> - where - R: FromRedis, - { - async move { commands::server::ping(self).await?.convert() } - } - - /// Read info about the server. - /// - /// - fn info(&self, section: Option) -> impl Future> - where - R: FromRedis, - { - async move { commands::server::info(self, section).await?.convert() } - } - - /// Run a custom command that is not yet supported via another interface on this client. This is most useful when - /// interacting with third party modules or extensions. - /// - /// Callers should use the re-exported [redis_keyslot](crate::util::redis_keyslot) function to hash the command's - /// key, if necessary. - /// - /// This interface should be used with caution as it may break the automatic pipeline features in the client if - /// command flags are not properly configured. - fn custom(&self, cmd: CustomCommand, args: Vec) -> impl Future> - where - R: FromRedis, - T: TryInto, - T::Error: Into, - { - async move { - let args = utils::try_into_vec(args)?; - commands::server::custom(self, cmd, args).await?.convert() - } - } - - /// Run a custom command similar to [custom](Self::custom), but return the response frame directly without any - /// parsing. - /// - /// Note: RESP2 frames from the server are automatically converted to the RESP3 format when parsed by the client. - fn custom_raw(&self, cmd: CustomCommand, args: Vec) -> impl Future> - where - T: TryInto, - T::Error: Into, - { - async move { - let args = utils::try_into_vec(args)?; - commands::server::custom_raw(self, cmd, args).await - } - } - - /// Customize various configuration options on commands. - fn with_options(&self, options: &Options) -> WithOptions { - WithOptions { - client: self.clone(), - options: options.clone(), - } - } -} - -pub fn spawn_event_listener(rx: BroadcastReceiver, func: F) -> JoinHandle> -where - T: Clone + 'static, - F: Fn(T) -> RedisResult<()> + 'static, -{ - spawn(async move { - let mut result = Ok(()); - - while let Ok(val) = rx.recv().await { - if let Err(err) = func(val) { - result = Err(err); - break; - } - } - - result - }) -} diff --git a/src/glommio/io_compat.rs b/src/glommio/io_compat.rs deleted file mode 100644 index c03d1e2a..00000000 --- a/src/glommio/io_compat.rs +++ /dev/null @@ -1,68 +0,0 @@ -/// Reuse the same approach used by gmf (https://github.com/EtaCassiopeia/gmf/blob/591037476e6a17f83954a20558ff0e1920d94301/gmf/src/server/tokio_interop.rs#L1). -/// -/// The `Framed` codec interface used by the `Connection` struct requires that `T: AsyncRead+AsyncWrite`. -/// These traits are defined in the tokio and futures_io/futures_lite crates, but the tokio_util::codec interface -/// uses the versions re-implemented in tokio. However, glommio's network interfaces implement -/// `AsyncRead+AsyncWrite` from the futures_io crate. There are several ways to work around this, including -/// either a re-implementation of the codec traits `Encoder+Decoder`, or a compatibility layer for the different -/// versions of `AsyncRead+AsyncWrite`. The `gmf` project used the second approach, which seems much easier than -/// re-implementing the `Framed` traits (https://github.com/tokio-rs/tokio/blob/1ac8dff213937088616dc84de9adc92b4b68c49a/tokio-util/src/codec/framed_impl.rs#L125). - -// ------------------- https://github.com/EtaCassiopeia/gmf/blob/591037476e6a17f83954a20558ff0e1920d94301/gmf/src/server/tokio_interop.rs - -/// This module provides interoperability with the Tokio async runtime. -/// It contains utilities to bridge between futures_lite and Tokio. -use std::io::{self}; -use std::{ - pin::Pin, - task::{Context, Poll}, -}; - -use futures_io::{AsyncRead, AsyncWrite}; -use tokio::io::ReadBuf; - -/// A wrapper type for AsyncRead + AsyncWrite + Unpin types, providing -/// interoperability with Tokio's AsyncRead and AsyncWrite traits. -#[pin_project::pin_project] // This generates a projection for the inner type. -pub struct TokioIO(#[pin] pub T) -where - T: AsyncRead + AsyncWrite + Unpin; - -impl tokio::io::AsyncWrite for TokioIO -where - T: AsyncRead + AsyncWrite + Unpin, -{ - /// Write some data into the inner type, returning how many bytes were written. - fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { - // This is the same as Pin::new(&mut self.0).poll_write(cx, buf) with the source type of `mut self` - // using projection makes it easier to read. - let this = self.project(); - this.0.poll_write(cx, buf) - } - - /// Flushes the inner type. - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.project().0.poll_flush(cx) - } - - /// Shuts down the inner type, flushing any buffered data. - fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.project().0.poll_close(cx) - } -} - -impl tokio::io::AsyncRead for TokioIO -where - T: AsyncRead + AsyncWrite + Unpin, -{ - /// Reads some data from the inner type, returning how many bytes were read. - fn poll_read(self: Pin<&mut Self>, cx: &mut Context, buf: &mut ReadBuf<'_>) -> Poll> { - self.project().0.poll_read(cx, buf.initialize_unfilled()).map(|n| { - if let Ok(n) = n { - buf.advance(n); - } - - Ok(()) - }) - } -} diff --git a/src/glommio/mod.rs b/src/glommio/mod.rs deleted file mode 100644 index 62aa2134..00000000 --- a/src/glommio/mod.rs +++ /dev/null @@ -1,122 +0,0 @@ -#[cfg(all(feature = "glommio", feature = "unix-sockets"))] -compile_error!("Cannot use glommio and unix-sockets features together."); - -pub(crate) mod broadcast; -pub(crate) mod interfaces; -pub(crate) mod io_compat; -pub(crate) mod mpsc; -pub(crate) mod sync; - -pub(crate) mod compat { - pub use super::{ - broadcast::{BroadcastReceiver, BroadcastSender}, - mpsc::{rx_stream, UnboundedReceiver, UnboundedSender}, - sync::*, - }; - use crate::error::RedisError; - use futures::Future; - use glommio::TaskQueueHandle; - pub use glommio::{ - channels::local_channel::new_unbounded as unbounded_channel, - task::JoinHandle as GlommioJoinHandle, - timer::sleep, - }; - pub use oneshot::{channel as oneshot_channel, Receiver as OneshotReceiver, Sender as OneshotSender}; - use std::{ - cell::RefCell, - pin::Pin, - rc::Rc, - task::{Context, Poll}, - }; - - /// The reference counting container type. - /// - /// This type may change based on the runtime feature flags used. - pub type RefCount = Rc; - - pub fn broadcast_send(tx: &BroadcastSender, msg: &T, func: F) { - tx.send(msg, func); - } - - pub fn broadcast_channel(_: usize) -> (BroadcastSender, BroadcastReceiver) { - let tx = BroadcastSender::new(); - let rx = tx.subscribe(); - (tx, rx) - } - - /// A wrapper type around [JoinHandle](glommio::task::JoinHandle) with an interface similar to Tokio's - /// [JoinHandle](tokio::task::JoinHandle) - pub struct JoinHandle { - pub(crate) inner: GlommioJoinHandle, - pub(crate) finished: Rc>, - } - - pub fn spawn(ft: impl Future + 'static) -> JoinHandle { - let finished = Rc::new(RefCell::new(false)); - let _finished = finished.clone(); - let inner = glommio::spawn_local(async move { - let result = ft.await; - _finished.replace(true); - result - }) - .detach(); - - JoinHandle { inner, finished } - } - - // TODO use with connection config task queues - #[allow(dead_code)] - pub fn spawn_into( - ft: impl Future + 'static, - tq: TaskQueueHandle, - ) -> Result, RedisError> { - let finished = Rc::new(RefCell::new(false)); - let _finished = finished.clone(); - let inner = glommio::spawn_local_into( - async move { - let result = ft.await; - _finished.replace(true); - result - }, - tq, - )? - .detach(); - - Ok(JoinHandle { inner, finished }) - } - - impl Future for JoinHandle { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - use futures_lite::FutureExt; - - let finished = self.finished.clone(); - let result = self - .get_mut() - .inner - .poll(cx) - .map(|result| result.ok_or(RedisError::new_canceled())); - - if let Poll::Ready(_) = result { - finished.replace(true); - } - result - } - } - - impl JoinHandle { - pub(crate) fn set_finished(&self) { - self.finished.replace(true); - } - - pub fn is_finished(&self) -> bool { - *self.finished.as_ref().borrow() - } - - pub fn abort(&self) { - self.inner.cancel(); - self.set_finished(); - } - } -} diff --git a/src/glommio/mpsc.rs b/src/glommio/mpsc.rs deleted file mode 100644 index 17b4122d..00000000 --- a/src/glommio/mpsc.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::error::{RedisError, RedisErrorKind}; -use futures::Stream; -use glommio::{ - channels::local_channel::{LocalReceiver, LocalSender}, - GlommioError, -}; -use std::{ - ops::Deref, - pin::Pin, - rc::Rc, - task::{Context, Poll}, -}; - -pub type UnboundedReceiver = LocalReceiver; - -pub struct UnboundedReceiverStream { - rx: LocalReceiver, -} - -impl From> for UnboundedReceiverStream { - fn from(rx: LocalReceiver) -> Self { - UnboundedReceiverStream { rx } - } -} - -impl UnboundedReceiverStream { - #[allow(dead_code)] - pub async fn recv(&mut self) -> Option { - self.rx.recv().await - } -} - -impl Stream for UnboundedReceiverStream { - type Item = T; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - use futures_lite::stream::StreamExt; - - // TODO make sure this is cancellation-safe. it's a bit unclear why the internal impl of ChannelStream does what - // it does. - self.rx.stream().poll_next(cx) - } -} - -pub struct UnboundedSender { - tx: Rc>, -} - -// https://github.com/rust-lang/rust/issues/26925 -impl Clone for UnboundedSender { - fn clone(&self) -> Self { - UnboundedSender { tx: self.tx.clone() } - } -} - -impl From> for UnboundedSender { - fn from(tx: LocalSender) -> Self { - UnboundedSender { tx: Rc::new(tx) } - } -} - -impl UnboundedSender { - pub fn try_send(&self, msg: T) -> Result<(), GlommioError> { - self.tx.try_send(msg) - } - - pub fn send(&self, msg: T) -> Result<(), RedisError> { - if let Err(_e) = self.tx.deref().try_send(msg) { - // shouldn't happen since we use unbounded channels - Err(RedisError::new( - RedisErrorKind::Canceled, - "Failed to send message on channel.", - )) - } else { - Ok(()) - } - } -} - -pub fn rx_stream(rx: LocalReceiver) -> impl Stream + 'static { - // what happens if we `join` the futures from `recv()` and `rx.stream().next()`? - UnboundedReceiverStream::from(rx) -} diff --git a/src/glommio/sync.rs b/src/glommio/sync.rs deleted file mode 100644 index 3ab17edc..00000000 --- a/src/glommio/sync.rs +++ /dev/null @@ -1,163 +0,0 @@ -use std::{ - cell::{Ref, RefCell, RefMut}, - fmt, - mem, - sync::atomic::Ordering, -}; - -pub struct RefSwap { - inner: RefCell, -} - -impl RefSwap { - pub fn new(val: T) -> Self { - RefSwap { - inner: RefCell::new(val), - } - } - - pub fn swap(&self, other: T) -> T { - mem::replace(&mut self.inner.borrow_mut(), other) - } - - pub fn store(&self, other: T) { - self.swap(other); - } - - pub fn load(&self) -> Ref<'_, T> { - self.inner.borrow() - } -} - -pub struct AsyncRwLock { - inner: glommio::sync::RwLock, -} - -impl AsyncRwLock { - pub fn new(val: T) -> Self { - AsyncRwLock { - inner: glommio::sync::RwLock::new(val), - } - } - - pub async fn write(&self) -> glommio::sync::RwLockWriteGuard { - self.inner.write().await.unwrap() - } -} - -#[derive(Debug)] -pub struct AtomicUsize { - inner: RefCell, -} - -impl AtomicUsize { - pub fn new(val: usize) -> Self { - AtomicUsize { - inner: RefCell::new(val), - } - } - - pub fn fetch_add(&self, val: usize, _: Ordering) -> usize { - let mut guard = self.inner.borrow_mut(); - - let new = guard.saturating_add(val); - *guard = new; - new - } - - pub fn fetch_sub(&self, val: usize, _: Ordering) -> usize { - let mut guard = self.inner.borrow_mut(); - - let new = guard.saturating_sub(val); - *guard = new; - new - } - - pub fn load(&self, _: Ordering) -> usize { - *self.inner.borrow() - } - - pub fn swap(&self, val: usize, _: Ordering) -> usize { - let mut guard = self.inner.borrow_mut(); - let old = *guard; - *guard = val; - old - } -} - -#[derive(Debug)] -pub struct AtomicBool { - inner: RefCell, -} - -impl AtomicBool { - pub fn new(val: bool) -> Self { - AtomicBool { - inner: RefCell::new(val), - } - } - - pub fn load(&self, _: Ordering) -> bool { - *self.inner.borrow() - } - - pub fn swap(&self, val: bool, _: Ordering) -> bool { - let mut guard = self.inner.borrow_mut(); - let old = *guard; - *guard = val; - old - } -} - -pub type MutexGuard<'a, T> = RefMut<'a, T>; - -pub struct Mutex { - inner: RefCell, -} - -impl fmt::Debug for Mutex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.inner) - } -} - -impl Mutex { - pub fn new(val: T) -> Self { - Mutex { - inner: RefCell::new(val), - } - } - - pub fn lock(&self) -> MutexGuard { - self.inner.borrow_mut() - } -} - -pub type RwLockReadGuard<'a, T> = Ref<'a, T>; -pub type RwLockWriteGuard<'a, T> = RefMut<'a, T>; - -pub struct RwLock { - inner: RefCell, -} - -impl fmt::Debug for RwLock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.inner) - } -} - -impl RwLock { - pub fn new(val: T) -> Self { - RwLock { - inner: RefCell::new(val), - } - } - - pub fn read(&self) -> RwLockReadGuard { - self.inner.borrow() - } - - pub fn write(&self) -> RwLockWriteGuard { - self.inner.borrow_mut() - } -} diff --git a/src/interfaces.rs b/src/interfaces.rs deleted file mode 100644 index 2617526e..00000000 --- a/src/interfaces.rs +++ /dev/null @@ -1,403 +0,0 @@ -use crate::{ - commands, - error::{RedisError, RedisErrorKind}, - modules::inner::RedisClientInner, - protocol::command::{RedisCommand, RouterCommand}, - runtime::{sleep, spawn, BroadcastReceiver, JoinHandle, RefCount}, - types::{ClientState, ClusterStateChange, KeyspaceEvent, Message, RespVersion, Server}, - utils, -}; -use bytes_utils::Str; -use futures::Future; -pub use redis_protocol::resp3::types::BytesFrame as Resp3Frame; -use rm_send_macros::rm_send_if; -use std::time::Duration; - -/// Type alias for `Result`. -pub type RedisResult = Result; - -/// Send a single `RedisCommand` to the router. -pub(crate) fn default_send_command(inner: &RefCount, command: C) -> Result<(), RedisError> -where - C: Into, -{ - let mut command: RedisCommand = command.into(); - _trace!( - inner, - "Sending command {} ({}) to router.", - command.kind.to_str_debug(), - command.debug_id() - ); - command.inherit_options(inner); - - send_to_router(inner, command.into()) -} - -/// Send a `RouterCommand` to the router. -pub(crate) fn send_to_router(inner: &RefCount, command: RouterCommand) -> Result<(), RedisError> { - #[allow(clippy::collapsible_if)] - if command.should_check_fail_fast() { - if utils::read_locked(&inner.state) != ClientState::Connected { - _debug!(inner, "Responding early after fail fast check."); - command.finish_with_error(RedisError::new( - RedisErrorKind::Canceled, - "Connection closed unexpectedly.", - )); - return Ok(()); - } - } - - let new_len = inner.counters.incr_cmd_buffer_len(); - let should_apply_backpressure = inner.connection.max_command_buffer_len > 0 - && new_len > inner.connection.max_command_buffer_len - && !command.should_skip_backpressure(); - - if should_apply_backpressure { - inner.counters.decr_cmd_buffer_len(); - command.finish_with_error(RedisError::new( - RedisErrorKind::Backpressure, - "Max command queue length exceeded.", - )); - return Ok(()); - } - - if let Err(e) = inner.send_command(command) { - // usually happens if the caller tries to send a command before calling `connect` or after calling `quit` - inner.counters.decr_cmd_buffer_len(); - - if let RouterCommand::Command(mut command) = e { - _warn!( - inner, - "Fatal error sending {} command to router. Client may be stopped or not yet initialized.", - command.kind.to_str_debug() - ); - - command.respond_to_caller(Err(RedisError::new( - RedisErrorKind::Unknown, - "Client is not initialized.", - ))); - } else { - _warn!( - inner, - "Fatal error sending command to router. Client may be stopped or not yet initialized." - ); - } - - Err(RedisError::new( - RedisErrorKind::Unknown, - "Failed to send command to router.", - )) - } else { - Ok(()) - } -} - -#[cfg(not(feature = "glommio"))] -pub use crate::_tokio::ClientLike; -#[cfg(feature = "glommio")] -pub use crate::glommio::interfaces::ClientLike; - -#[cfg(not(feature = "glommio"))] -pub use crate::_tokio::spawn_event_listener; -#[cfg(feature = "glommio")] -pub use crate::glommio::interfaces::spawn_event_listener; - -/// Functions that provide a connection heartbeat interface. -#[rm_send_if(feature = "glommio")] -pub trait HeartbeatInterface: ClientLike { - /// Return a future that will ping the server on an interval. - #[allow(unreachable_code)] - fn enable_heartbeat( - &self, - interval: Duration, - break_on_error: bool, - ) -> impl Future> + Send { - async move { - let _self = self.clone(); - - loop { - sleep(interval).await; - - if break_on_error { - let _: () = _self.ping().await?; - } else if let Err(e) = _self.ping::<()>().await { - warn!("{}: Heartbeat ping failed with error: {:?}", _self.inner().id, e); - } - } - - Ok(()) - } - } -} - -/// Functions for authenticating clients. -#[rm_send_if(feature = "glommio")] -pub trait AuthInterface: ClientLike { - /// Request for authentication in a password-protected Redis server. Returns ok if successful. - /// - /// The client will automatically authenticate with the default user if a password is provided in the associated - /// `RedisConfig` when calling [connect](crate::interfaces::ClientLike::connect). - /// - /// If running against clustered servers this function will authenticate all connections. - /// - /// - fn auth(&self, username: Option, password: S) -> impl Future> + Send - where - S: Into + Send, - { - async move { - into!(password); - commands::server::auth(self, username, password).await - } - } - - /// Switch to a different protocol, optionally authenticating in the process. - /// - /// If running against clustered servers this function will issue the HELLO command to each server concurrently. - /// - /// - fn hello( - &self, - version: RespVersion, - auth: Option<(Str, Str)>, - setname: Option, - ) -> impl Future> + Send { - async move { commands::server::hello(self, version, auth, setname).await } - } -} - -/// An interface that exposes various client and connection events. -/// -/// Calling [quit](crate::interfaces::ClientLike::quit) will close all event streams. -#[rm_send_if(feature = "glommio")] -pub trait EventInterface: ClientLike { - /// Spawn a task that runs the provided function on each publish-subscribe message. - /// - /// See [message_rx](Self::message_rx) for more information. - fn on_message(&self, func: F) -> JoinHandle> - where - F: Fn(Message) -> RedisResult<()> + Send + 'static, - { - let rx = self.message_rx(); - spawn_event_listener(rx, func) - } - - /// Spawn a task that runs the provided function on each keyspace event. - /// - /// - fn on_keyspace_event(&self, func: F) -> JoinHandle> - where - F: Fn(KeyspaceEvent) -> RedisResult<()> + Send + 'static, - { - let rx = self.keyspace_event_rx(); - spawn_event_listener(rx, func) - } - - /// Spawn a task that runs the provided function on each reconnection event. - /// - /// Errors returned by `func` will exit the task. - fn on_reconnect(&self, func: F) -> JoinHandle> - where - F: Fn(Server) -> RedisResult<()> + Send + 'static, - { - let rx = self.reconnect_rx(); - spawn_event_listener(rx, func) - } - - /// Spawn a task that runs the provided function on each cluster change event. - /// - /// Errors returned by `func` will exit the task. - fn on_cluster_change(&self, func: F) -> JoinHandle> - where - F: Fn(Vec) -> RedisResult<()> + Send + 'static, - { - let rx = self.cluster_change_rx(); - spawn_event_listener(rx, func) - } - - /// Spawn a task that runs the provided function on each connection error event. - /// - /// Errors returned by `func` will exit the task. - fn on_error(&self, func: F) -> JoinHandle> - where - F: Fn(RedisError) -> RedisResult<()> + Send + 'static, - { - let rx = self.error_rx(); - spawn_event_listener(rx, func) - } - - /// Spawn a task that runs the provided function whenever the client detects an unresponsive connection. - fn on_unresponsive(&self, func: F) -> JoinHandle> - where - F: Fn(Server) -> RedisResult<()> + Send + 'static, - { - let rx = self.unresponsive_rx(); - spawn_event_listener(rx, func) - } - - /// Spawn one task that listens for all connection management event types. - /// - /// Errors in any of the provided functions will exit the task. - fn on_any(&self, error_fn: Fe, reconnect_fn: Fr, cluster_change_fn: Fc) -> JoinHandle> - where - Fe: Fn(RedisError) -> RedisResult<()> + Send + 'static, - Fr: Fn(Server) -> RedisResult<()> + Send + 'static, - Fc: Fn(Vec) -> RedisResult<()> + Send + 'static, - { - let mut error_rx = self.error_rx(); - let mut reconnect_rx = self.reconnect_rx(); - let mut cluster_rx = self.cluster_change_rx(); - - spawn(async move { - #[allow(unused_assignments)] - let mut result = Ok(()); - - loop { - tokio::select! { - Ok(error) = error_rx.recv() => { - if let Err(err) = error_fn(error) { - result = Err(err); - break; - } - } - Ok(server) = reconnect_rx.recv() => { - if let Err(err) = reconnect_fn(server) { - result = Err(err); - break; - } - } - Ok(changes) = cluster_rx.recv() => { - if let Err(err) = cluster_change_fn(changes) { - result = Err(err); - break; - } - } - } - } - - result - }) - } - - /// Listen for messages on the publish-subscribe interface. - /// - /// **Keyspace events are not sent on this interface.** - /// - /// If the connection to the Redis server closes for any reason this function does not need to be called again. - /// Messages will start appearing on the original stream after - /// [subscribe](crate::interfaces::PubsubInterface::subscribe) is called again. - fn message_rx(&self) -> BroadcastReceiver { - self.inner().notifications.pubsub.load().subscribe() - } - - /// Listen for keyspace and keyevent notifications on the publish-subscribe interface. - /// - /// Callers still need to configure the server and subscribe to the relevant channels, but this interface will - /// parse and format the messages automatically. - /// - /// - fn keyspace_event_rx(&self) -> BroadcastReceiver { - self.inner().notifications.keyspace.load().subscribe() - } - - /// Listen for reconnection notifications. - /// - /// This function can be used to receive notifications whenever the client reconnects in order to - /// re-subscribe to channels, etc. - /// - /// A reconnection event is also triggered upon first connecting to the server. - fn reconnect_rx(&self) -> BroadcastReceiver { - self.inner().notifications.reconnect.load().subscribe() - } - - /// Listen for notifications whenever the cluster state changes. - /// - /// This is usually triggered in response to a `MOVED` error, but can also happen when connections close - /// unexpectedly. - fn cluster_change_rx(&self) -> BroadcastReceiver> { - self.inner().notifications.cluster_change.load().subscribe() - } - - /// Listen for protocol and connection errors. This stream can be used to more intelligently handle errors that may - /// not appear in the request-response cycle, and so cannot be handled by response futures. - fn error_rx(&self) -> BroadcastReceiver { - self.inner().notifications.errors.load().subscribe() - } - - /// Receive a message when the client initiates a reconnection after detecting an unresponsive connection. - fn unresponsive_rx(&self) -> BroadcastReceiver { - self.inner().notifications.unresponsive.load().subscribe() - } -} - -#[cfg(feature = "i-acl")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-acl")))] -pub use crate::commands::interfaces::acl::*; -#[cfg(feature = "i-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))] -pub use crate::commands::interfaces::client::*; -#[cfg(feature = "i-cluster")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))] -pub use crate::commands::interfaces::cluster::*; -#[cfg(feature = "i-config")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-config")))] -pub use crate::commands::interfaces::config::*; -#[cfg(feature = "i-geo")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))] -pub use crate::commands::interfaces::geo::*; -#[cfg(feature = "i-hashes")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hashes")))] -pub use crate::commands::interfaces::hashes::*; -#[cfg(feature = "i-hyperloglog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-hyperloglog")))] -pub use crate::commands::interfaces::hyperloglog::*; -#[cfg(feature = "i-keys")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-keys")))] -pub use crate::commands::interfaces::keys::*; -#[cfg(feature = "i-lists")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))] -pub use crate::commands::interfaces::lists::*; -#[cfg(feature = "i-scripts")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] -pub use crate::commands::interfaces::lua::*; -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -pub use crate::commands::interfaces::memory::*; -#[cfg(feature = "i-pubsub")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-pubsub")))] -pub use crate::commands::interfaces::pubsub::*; -#[cfg(feature = "i-redis-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))] -pub use crate::commands::interfaces::redis_json::RedisJsonInterface; -#[cfg(feature = "i-redisearch")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))] -pub use crate::commands::interfaces::redisearch::*; -#[cfg(feature = "sentinel-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))] -pub use crate::commands::interfaces::sentinel::SentinelInterface; -#[cfg(feature = "i-server")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))] -pub use crate::commands::interfaces::server::*; -#[cfg(feature = "i-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sets")))] -pub use crate::commands::interfaces::sets::*; -#[cfg(feature = "i-slowlog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))] -pub use crate::commands::interfaces::slowlog::*; -#[cfg(feature = "i-sorted-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))] -pub use crate::commands::interfaces::sorted_sets::*; -#[cfg(feature = "i-streams")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))] -pub use crate::commands::interfaces::streams::*; -#[cfg(feature = "i-time-series")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -pub use crate::commands::interfaces::timeseries::*; -#[cfg(feature = "i-tracking")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] -pub use crate::commands::interfaces::tracking::*; -#[cfg(feature = "transactions")] -#[cfg_attr(docsrs, doc(cfg(feature = "transactions")))] -pub use crate::commands::interfaces::transactions::*; - -pub use crate::commands::interfaces::metrics::MetricsInterface; diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index eb920c44..00000000 --- a/src/lib.rs +++ /dev/null @@ -1,193 +0,0 @@ -#![allow(clippy::unnecessary_fallible_conversions)] -#![allow(clippy::redundant_pattern_matching)] -#![allow(clippy::mutable_key_type)] -#![allow(clippy::derivable_impls)] -#![allow(clippy::enum_variant_names)] -#![allow(clippy::iter_kv_map)] -#![allow(clippy::len_without_is_empty)] -#![allow(clippy::vec_init_then_push)] -#![allow(clippy::while_let_on_iterator)] -#![allow(clippy::type_complexity)] -#![allow(clippy::too_many_arguments)] -#![allow(clippy::new_without_default)] -#![allow(clippy::assigning_clones)] -#![allow(clippy::manual_async_fn)] -#![warn(clippy::large_types_passed_by_value)] -#![warn(clippy::large_stack_frames)] -#![warn(clippy::large_futures)] -#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))] -#![cfg_attr(docsrs, feature(doc_cfg))] -#![cfg_attr(docsrs, allow(unused_attributes))] -#![doc = include_str!("../README.md")] - -#[cfg(any(feature = "dns", feature = "replicas"))] -#[macro_use] -extern crate async_trait; - -#[macro_use] -extern crate log; - -pub extern crate bytes; -pub extern crate bytes_utils; -#[cfg(feature = "enable-native-tls")] -#[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))] -pub extern crate native_tls; -#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] -#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))] -pub extern crate rustls; -#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] -#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))] -pub extern crate rustls_native_certs; -#[cfg(feature = "serde-json")] -pub extern crate serde_json; -pub extern crate socket2; -#[cfg(feature = "partial-tracing")] -#[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))] -pub extern crate tracing; -#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))] -extern crate tracing_futures; -#[macro_use] -mod macros; - -mod commands; -mod modules; -mod protocol; -mod router; -mod trace; -mod utils; - -/// Redis client implementations. -pub mod clients; -/// Error structs returned by Redis commands. -pub mod error; -/// Traits that implement portions of the Redis interface. -pub mod interfaces; -#[cfg(feature = "mocks")] -#[cfg_attr(docsrs, doc(cfg(feature = "mocks")))] -pub use modules::mocks; -/// An interface to run the `MONITOR` command. -#[cfg(feature = "monitor")] -#[cfg_attr(docsrs, doc(cfg(feature = "monitor")))] -pub mod monitor; -/// The structs and enums used by the Redis client. -pub mod types; - -#[cfg(feature = "glommio")] -mod glommio; -#[cfg(feature = "glommio")] -pub(crate) use glommio::compat as runtime; - -#[cfg(not(feature = "glommio"))] -mod _tokio; -#[cfg(not(feature = "glommio"))] -pub(crate) use _tokio as runtime; - -/// Various client utility functions. -pub mod util { - pub use crate::utils::{f64_to_redis_string, redis_string_to_f64, static_bytes, static_str}; - use crate::{error::RedisError, types::RedisKey}; - pub use redis_protocol::redis_keyslot; - use std::collections::{BTreeMap, VecDeque}; - - /// A convenience constant for `None` values used as generic arguments. - /// - /// Functions that take `Option` as an argument often require the caller to use a turbofish when the - /// variant is `None`. In many cases this constant can be used instead. - // pretty much everything in this crate supports From - pub const NONE: Option = None; - - /// Calculate the SHA1 hash output as a hex string. This is provided for clients that use the Lua interface to - /// manage their own script caches. - #[cfg(feature = "sha-1")] - #[cfg_attr(docsrs, doc(cfg(feature = "sha-1")))] - pub fn sha1_hash(input: &str) -> String { - use sha1::Digest; - - let mut hasher = sha1::Sha1::new(); - hasher.update(input.as_bytes()); - format!("{:x}", hasher.finalize()) - } - - /// Group the provided arguments by their cluster hash slot. - /// - /// This can be useful with commands that require all keys map to the same hash slot, such as `SSUBSCRIBE`, - /// `MGET`, etc. - /// - /// ```rust - /// # use fred::prelude::*; - /// async fn example(client: impl KeysInterface) -> Result<(), RedisError> { - /// let keys = vec!["foo", "bar", "baz", "a{1}", "b{1}", "c{1}"]; - /// let groups = fred::util::group_by_hash_slot(keys)?; - /// - /// for (slot, keys) in groups.into_iter() { - /// // `MGET` requires that all arguments map to the same hash slot - /// println!("{:?}", client.mget::, _>(keys).await?); - /// } - /// Ok(()) - /// } - /// ``` - pub fn group_by_hash_slot( - args: impl IntoIterator, - ) -> Result>, RedisError> - where - T: TryInto, - T::Error: Into, - { - let mut out = BTreeMap::new(); - - for arg in args.into_iter() { - let arg: RedisKey = to!(arg)?; - let slot = redis_keyslot(arg.as_bytes()); - - out.entry(slot).or_insert(VecDeque::new()).push_back(arg); - } - Ok(out) - } -} - -/// Convenience module to import a `RedisClient`, all possible interfaces, error types, and common argument types or -/// return value types. -pub mod prelude { - #[cfg(feature = "partial-tracing")] - #[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))] - pub use crate::types::TracingConfig; - - pub use crate::{ - clients::{RedisClient, RedisPool}, - error::{RedisError, RedisErrorKind}, - interfaces::*, - types::{ - Blocking, - Builder, - ConnectionConfig, - Expiration, - FromRedis, - Options, - PerformanceConfig, - ReconnectPolicy, - RedisConfig, - RedisKey, - RedisValue, - RedisValueKind, - Server, - ServerConfig, - SetOptions, - TcpConfig, - }, - }; - - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - #[cfg_attr( - docsrs, - doc(cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" - ))) - )] - pub use crate::types::{TlsConfig, TlsConnector}; -} diff --git a/src/macros.rs b/src/macros.rs deleted file mode 100644 index 26042c2b..00000000 --- a/src/macros.rs +++ /dev/null @@ -1,165 +0,0 @@ -#![allow(unused_macros)] - -macro_rules! to( - ($val:ident) => { - crate::utils::try_into($val) - } -); - -macro_rules! _trace( - ($inner:tt, $($arg:tt)*) => { { - if log::log_enabled!(log::Level::Trace) { - log::trace!("{}: {}", $inner.id, format!($($arg)*)) - } - } } -); - -macro_rules! _debug( - ($inner:tt, $($arg:tt)*) => { { - if log::log_enabled!(log::Level::Debug) { - log::debug!("{}: {}", $inner.id, format!($($arg)*)) - } - } } -); - -macro_rules! _error( - ($inner:tt, $($arg:tt)*) => { { - if log::log_enabled!(log::Level::Error) { - log::error!("{}: {}", $inner.id, format!($($arg)*)) - } - } } -); - -macro_rules! _warn( - ($inner:tt, $($arg:tt)*) => { { - if log::log_enabled!(log::Level::Warn) { - log::warn!("{}: {}", $inner.id, format!($($arg)*)) - } - } } -); - -macro_rules! _info( - ($inner:tt, $($arg:tt)*) => { { - if log::log_enabled!(log::Level::Info) { - log::info!("{}: {}", $inner.id, format!($($arg)*)) - } - } } -); - -/// Span used within the client that uses the command's span ID as the parent. -#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))] -macro_rules! fspan ( - ($cmd:ident, $lvl:expr, $($arg:tt)*) => { { - let _id = $cmd.traces.cmd.as_ref().and_then(|c| c.id()); - span_lvl!($lvl, parent: _id, $($arg)*) - } } -); - -macro_rules! span_lvl { - ($lvl:expr, $($args:tt)*) => {{ - match $lvl { - tracing::Level::ERROR => tracing::error_span!($($args)*), - tracing::Level::WARN => tracing::warn_span!($($args)*), - tracing::Level::INFO => tracing::info_span!($($args)*), - tracing::Level::DEBUG => tracing::debug_span!($($args)*), - tracing::Level::TRACE => tracing::trace_span!($($args)*), - } - }}; -} - -/// Fake span used within the client that uses the command's span ID as the parent. -#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))] -macro_rules! fspan ( - ($cmd:ident, $($arg:tt)*) => { - crate::trace::Span {} - } -); - -/// Similar to `try`/`?`, but `continue` instead of breaking out with an error. -macro_rules! try_or_continue ( - ($expr:expr) => { - match $expr { - Ok(val) => val, - Err(_) => continue - } - } -); - -/// A helper macro to wrap a string value in quotes via the [json](serde_json::json) macro. -/// -/// See the [RedisJSON interface](crate::interfaces::RedisJsonInterface) for more information. -#[cfg(feature = "i-redis-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redis-json")))] -#[macro_export] -macro_rules! json_quote( - ($($json:tt)+) => { - serde_json::json!($($json)+).to_string() - } -); - -/// Shorthand to create a [CustomCommand](crate::types::CustomCommand). -/// -/// ```rust no_run -/// # use fred::{cmd, types::{CustomCommand, ClusterHash}}; -/// let _cmd = cmd!("FOO.BAR"); -/// let _cmd = cmd!("FOO.BAR", blocking: true); -/// let _cmd = cmd!("FOO.BAR", hash: ClusterHash::FirstKey); -/// let _cmd = cmd!("FOO.BAR", hash: ClusterHash::FirstKey, blocking: true); -/// // which is shorthand for -/// let _cmd = CustomCommand::new("FOO.BAR", ClusterHash::FirstKey, true); -/// ``` -#[macro_export] -macro_rules! cmd( - ($name:expr) => { - fred::types::CustomCommand::new($name, fred::types::ClusterHash::FirstKey, false) - }; - ($name:expr, blocking: $blk:expr) => { - fred::types::CustomCommand::new($name, fred::types::ClusterHash::FirstKey, $blk) - }; - ($name:expr, hash: $hash:expr) => { - fred::types::CustomCommand::new($name, $hash, false) - }; - ($name:expr, hash: $hash:expr, blocking: $blk:expr) => { - fred::types::CustomCommand::new($name, $hash, $blk) - }; -); - -macro_rules! static_val( - ($val:expr) => { - RedisValue::from_static_str($val) - } -); - -macro_rules! into ( - ($val:ident) => (let $val = $val.into();); - ($v1:ident, $v2:ident) => ( - let ($v1, $v2) = ($v1.into(), $v2.into()); - ); - ($v1:ident, $v2:ident, $v3:ident) => ( - let ($v1, $v2, $v3) = ($v1.into(), $v2.into(), $v3.into()); - ); - ($v1:ident, $v2:ident, $v3:ident, $v4:ident) => ( - let ($v1, $v2, $v3, $v4) = ($v1.into(), $v2.into(), $v3.into(), $v4.into()); - ); - ($v1:ident, $v2:ident, $v3:ident, $v4:ident, $v5:ident) => ( - let ($v1, $v2, $v3, $v4, $v5) = ($v1.into(), $v2.into(), $v3.into(), $v4.into(), $v5.into()); - ); - // add to this as needed -); - -macro_rules! try_into ( - ($val:ident) => (let $val = to!($val)?;); - ($v1:ident, $v2:ident) => ( - let ($v1, $v2) = (to!($v1)?, to!($v2)?); - ); - ($v1:ident, $v2:ident, $v3:ident) => ( - let ($v1, $v2, $v3) = (to!($v1)?, to!($v2)?, to!($v3)?); - ); - ($v1:ident, $v2:ident, $v3:ident, $v4:ident) => ( - let ($v1, $v2, $v3, $v4) = (to!($v1)?, to!($v2)?, to!($v3)?, to!($v4)?); - ); - ($v1:ident, $v2:ident, $v3:ident, $v4:ident, $v5:ident) => ( - let ($v1, $v2, $v3, $v4, $v5) = (to!($v1)?, to!($v2)?, to!($v3)?, to!($v4)?, to!($v5)?); - ); - // add to this as needed -); diff --git a/src/modules/backchannel.rs b/src/modules/backchannel.rs deleted file mode 100644 index a6d10e6b..00000000 --- a/src/modules/backchannel.rs +++ /dev/null @@ -1,231 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - modules::inner::RedisClientInner, - protocol::{command::RedisCommand, connection, connection::RedisTransport, types::Server}, - router::Connections, - runtime::RefCount, - utils, -}; -use redis_protocol::resp3::types::BytesFrame as Resp3Frame; -use std::collections::HashMap; - -/// Check if an existing connection can be used to the provided `server`, otherwise create a new one. -/// -/// Returns whether a new connection was created. -async fn check_and_create_transport( - backchannel: &mut Backchannel, - inner: &RefCount, - server: &Server, -) -> Result { - if let Some(ref mut transport) = backchannel.transport { - if &transport.server == server && transport.ping(inner).await.is_ok() { - _debug!(inner, "Using existing backchannel connection to {}", server); - return Ok(false); - } - } - backchannel.transport = None; - - let mut transport = connection::create(inner, server, None).await?; - transport.setup(inner, None).await?; - backchannel.transport = Some(transport); - - Ok(true) -} - -/// A struct wrapping a separate connection to the server or cluster for client or cluster management commands. -#[derive(Default)] -pub struct Backchannel { - /// A connection to any of the servers. - pub transport: Option, - /// An identifier for the blocked connection, if any. - pub blocked: Option, - /// A map of server IDs to connection IDs, as managed by the router. - pub connection_ids: HashMap, -} - -impl Backchannel { - /// Check if the current server matches the provided server, and disconnect. - // TODO does this need to disconnect whenever the caller manually changes the RESP protocol mode? - pub async fn check_and_disconnect(&mut self, inner: &RefCount, server: Option<&Server>) { - let should_close = self - .current_server() - .map(|current| server.map(|server| *server == current).unwrap_or(true)) - .unwrap_or(false); - - if should_close { - if let Some(ref mut transport) = self.transport { - let _ = transport.disconnect(inner).await; - } - self.transport = None; - } - } - - /// Clear all local state that depends on the associated `Router` instance. - pub async fn clear_router_state(&mut self, inner: &RefCount) { - self.connection_ids.clear(); - self.blocked = None; - - if let Some(ref mut transport) = self.transport { - let _ = transport.disconnect(inner).await; - } - self.transport = None; - } - - /// Set the connection IDs from the router. - pub fn update_connection_ids(&mut self, connections: &Connections) { - self.connection_ids = connections.connection_ids(); - } - - /// Read the connection ID for the provided server. - pub fn connection_id(&self, server: &Server) -> Option { - self.connection_ids.get(server).cloned() - } - - /// Set the blocked flag to the provided server. - pub fn set_blocked(&mut self, server: &Server) { - self.blocked = Some(server.clone()); - } - - /// Remove the blocked flag. - pub fn set_unblocked(&mut self) { - self.blocked = None; - } - - /// Remove the blocked flag only if the server matches the blocked server. - pub fn check_and_set_unblocked(&mut self, server: &Server) { - let should_remove = self.blocked.as_ref().map(|blocked| blocked == server).unwrap_or(false); - if should_remove { - self.set_unblocked(); - } - } - - /// Whether the client is blocked on a command. - pub fn is_blocked(&self) -> bool { - self.blocked.is_some() - } - - /// Whether an open connection exists to the blocked server. - pub fn has_blocked_transport(&self) -> bool { - match self.blocked { - Some(ref server) => match self.transport { - Some(ref transport) => &transport.server == server, - None => false, - }, - None => false, - } - } - - /// Return the server ID of the blocked client connection, if found. - pub fn blocked_server(&self) -> Option { - self.blocked.clone() - } - - /// Return the server ID of the existing backchannel connection, if found. - pub fn current_server(&self) -> Option { - self.transport.as_ref().map(|t| t.server.clone()) - } - - /// Return a server ID, with the following preferences: - /// - /// 1. The server ID of the existing connection, if any. - /// 2. The blocked server ID, if any. - /// 3. A random server ID from the router's connection map. - pub fn any_server(&self) -> Option { - self - .current_server() - .or(self.blocked_server()) - .or(self.connection_ids.keys().next().cloned()) - } - - /// Whether the existing connection is to the currently blocked server. - pub fn current_server_is_blocked(&self) -> bool { - self - .current_server() - .and_then(|server| self.blocked_server().map(|blocked| server == blocked)) - .unwrap_or(false) - } - - /// Send the provided command to the provided server, creating a new connection if needed. - /// - /// If a new connection is created this function also sets it on `self` before returning. - pub async fn request_response( - &mut self, - inner: &RefCount, - server: &Server, - command: RedisCommand, - ) -> Result { - let _ = check_and_create_transport(self, inner, server).await?; - - if let Some(ref mut transport) = self.transport { - _debug!( - inner, - "Sending {} ({}) on backchannel to {}", - command.kind.to_str_debug(), - command.debug_id(), - server - ); - - utils::timeout( - transport.request_response(command, inner.is_resp3()), - inner.connection_timeout(), - ) - .await - } else { - Err(RedisError::new( - RedisErrorKind::Unknown, - "Failed to create backchannel connection.", - )) - } - } - - /// Find the server identifier that should receive the provided command. - /// - /// Servers are chosen with the following preference order: - /// - /// * If `use_blocked` is true and a connection is blocked then that server will be used. - /// * If the client is clustered and the command uses a hashing policy that specifies a specific server then that - /// will be used. - /// * If a backchannel connection already exists then that will be used. - /// * Failing all of the above a random server will be used. - pub fn find_server( - &self, - inner: &RefCount, - command: &RedisCommand, - use_blocked: bool, - ) -> Result { - if use_blocked { - if let Some(server) = self.blocked.as_ref() { - Ok(server.clone()) - } else { - // should this be more relaxed? - Err(RedisError::new(RedisErrorKind::Unknown, "No connections are blocked.")) - } - } else if inner.config.server.is_clustered() { - if command.kind.use_random_cluster_node() { - self - .any_server() - .ok_or_else(|| RedisError::new(RedisErrorKind::Unknown, "Failed to find backchannel server.")) - } else { - inner.with_cluster_state(|state| { - let slot = match command.cluster_hash() { - Some(slot) => slot, - None => { - return Err(RedisError::new( - RedisErrorKind::Cluster, - "Failed to find cluster hash slot.", - )) - }, - }; - state - .get_server(slot) - .cloned() - .ok_or_else(|| RedisError::new(RedisErrorKind::Cluster, "Failed to find cluster owner.")) - }) - } - } else { - self - .any_server() - .ok_or_else(|| RedisError::new(RedisErrorKind::Unknown, "Failed to find backchannel server.")) - } - } -} diff --git a/src/modules/inner.rs b/src/modules/inner.rs deleted file mode 100644 index 47c8943c..00000000 --- a/src/modules/inner.rs +++ /dev/null @@ -1,751 +0,0 @@ -use crate::{ - error::*, - interfaces, - modules::backchannel::Backchannel, - protocol::{ - command::{ResponseSender, RouterCommand}, - connection::RedisTransport, - types::{ClusterRouting, DefaultResolver, Resolve, Server}, - }, - runtime::{ - broadcast_channel, - broadcast_send, - sleep, - unbounded_channel, - AsyncRwLock, - AtomicBool, - AtomicUsize, - BroadcastSender, - Mutex, - RefCount, - RefSwap, - RwLock, - UnboundedReceiver, - UnboundedSender, - }, - types::*, - utils, -}; -use bytes_utils::Str; -use futures::future::{select, Either}; -use semver::Version; -use std::{ops::DerefMut, time::Duration}; - -#[cfg(feature = "metrics")] -use crate::modules::metrics::MovingStats; -#[cfg(feature = "replicas")] -use std::collections::HashMap; - -pub type CommandSender = UnboundedSender; -pub type CommandReceiver = UnboundedReceiver; - -#[cfg(feature = "i-tracking")] -use crate::types::Invalidation; - -pub struct Notifications { - /// The client ID. - pub id: Str, - /// A broadcast channel for the `on_error` interface. - pub errors: RefSwap>>, - /// A broadcast channel for the `on_message` interface. - pub pubsub: RefSwap>>, - /// A broadcast channel for the `on_keyspace_event` interface. - pub keyspace: RefSwap>>, - /// A broadcast channel for the `on_reconnect` interface. - pub reconnect: RefSwap>>, - /// A broadcast channel for the `on_cluster_change` interface. - pub cluster_change: RefSwap>>>, - /// A broadcast channel for the `on_connect` interface. - pub connect: RefSwap>>>, - /// A channel for events that should close all client tasks with `Canceled` errors. - /// - /// Emitted when QUIT, SHUTDOWN, etc are called. - pub close: BroadcastSender<()>, - /// A broadcast channel for the `on_invalidation` interface. - #[cfg(feature = "i-tracking")] - pub invalidations: RefSwap>>, - /// A broadcast channel for notifying callers when servers go unresponsive. - pub unresponsive: RefSwap>>, -} - -impl Notifications { - pub fn new(id: &Str, capacity: usize) -> Self { - Notifications { - id: id.clone(), - close: broadcast_channel(capacity).0, - errors: RefSwap::new(RefCount::new(broadcast_channel(capacity).0)), - pubsub: RefSwap::new(RefCount::new(broadcast_channel(capacity).0)), - keyspace: RefSwap::new(RefCount::new(broadcast_channel(capacity).0)), - reconnect: RefSwap::new(RefCount::new(broadcast_channel(capacity).0)), - cluster_change: RefSwap::new(RefCount::new(broadcast_channel(capacity).0)), - connect: RefSwap::new(RefCount::new(broadcast_channel(capacity).0)), - #[cfg(feature = "i-tracking")] - invalidations: RefSwap::new(RefCount::new(broadcast_channel(capacity).0)), - unresponsive: RefSwap::new(RefCount::new(broadcast_channel(capacity).0)), - } - } - - /// Replace the senders that have public receivers, closing the receivers in the process. - pub fn close_public_receivers(&self, capacity: usize) { - utils::swap_new_broadcast_channel(&self.errors, capacity); - utils::swap_new_broadcast_channel(&self.pubsub, capacity); - utils::swap_new_broadcast_channel(&self.keyspace, capacity); - utils::swap_new_broadcast_channel(&self.reconnect, capacity); - utils::swap_new_broadcast_channel(&self.cluster_change, capacity); - utils::swap_new_broadcast_channel(&self.connect, capacity); - #[cfg(feature = "i-tracking")] - utils::swap_new_broadcast_channel(&self.invalidations, capacity); - utils::swap_new_broadcast_channel(&self.unresponsive, capacity); - } - - pub fn broadcast_error(&self, error: RedisError) { - broadcast_send(self.errors.load().as_ref(), &error, |err| { - debug!("{}: No `on_error` listener. The error was: {err:?}", self.id); - }); - } - - pub fn broadcast_pubsub(&self, message: Message) { - broadcast_send(self.pubsub.load().as_ref(), &message, |_| { - debug!("{}: No `on_message` listeners.", self.id); - }); - } - - pub fn broadcast_keyspace(&self, event: KeyspaceEvent) { - broadcast_send(self.keyspace.load().as_ref(), &event, |_| { - debug!("{}: No `on_keyspace_event` listeners.", self.id); - }); - } - - pub fn broadcast_reconnect(&self, server: Server) { - broadcast_send(self.reconnect.load().as_ref(), &server, |_| { - debug!("{}: No `on_reconnect` listeners.", self.id); - }); - } - - pub fn broadcast_cluster_change(&self, changes: Vec) { - broadcast_send(self.cluster_change.load().as_ref(), &changes, |_| { - debug!("{}: No `on_cluster_change` listeners.", self.id); - }); - } - - pub fn broadcast_connect(&self, result: Result<(), RedisError>) { - broadcast_send(self.connect.load().as_ref(), &result, |_| { - debug!("{}: No `on_connect` listeners.", self.id); - }); - } - - /// Interrupt any tokio `sleep` calls. - //`RedisClientInner::wait_with_interrupt` hides the subscription part from callers. - pub fn broadcast_close(&self) { - broadcast_send(&self.close, &(), |_| { - debug!("{}: No `close` listeners.", self.id); - }); - } - - #[cfg(feature = "i-tracking")] - pub fn broadcast_invalidation(&self, msg: Invalidation) { - broadcast_send(self.invalidations.load().as_ref(), &msg, |_| { - debug!("{}: No `on_invalidation` listeners.", self.id); - }); - } - - pub fn broadcast_unresponsive(&self, server: Server) { - broadcast_send(self.unresponsive.load().as_ref(), &server, |_| { - debug!("{}: No unresponsive listeners", self.id); - }); - } -} - -#[derive(Clone)] -pub struct ClientCounters { - pub cmd_buffer_len: RefCount, - pub redelivery_count: RefCount, -} - -impl Default for ClientCounters { - fn default() -> Self { - ClientCounters { - cmd_buffer_len: RefCount::new(AtomicUsize::new(0)), - redelivery_count: RefCount::new(AtomicUsize::new(0)), - } - } -} - -impl ClientCounters { - pub fn incr_cmd_buffer_len(&self) -> usize { - utils::incr_atomic(&self.cmd_buffer_len) - } - - pub fn decr_cmd_buffer_len(&self) -> usize { - utils::decr_atomic(&self.cmd_buffer_len) - } - - pub fn incr_redelivery_count(&self) -> usize { - utils::incr_atomic(&self.redelivery_count) - } - - pub fn read_cmd_buffer_len(&self) -> usize { - utils::read_atomic(&self.cmd_buffer_len) - } - - pub fn read_redelivery_count(&self) -> usize { - utils::read_atomic(&self.redelivery_count) - } - - pub fn take_cmd_buffer_len(&self) -> usize { - utils::set_atomic(&self.cmd_buffer_len, 0) - } - - pub fn take_redelivery_count(&self) -> usize { - utils::set_atomic(&self.redelivery_count, 0) - } - - pub fn reset(&self) { - utils::set_atomic(&self.cmd_buffer_len, 0); - utils::set_atomic(&self.redelivery_count, 0); - } -} - -/// Cached state related to the server(s). -pub struct ServerState { - pub kind: ServerKind, - #[cfg(feature = "replicas")] - pub replicas: HashMap, -} - -impl ServerState { - pub fn new(config: &RedisConfig) -> Self { - ServerState { - kind: ServerKind::new(config), - #[cfg(feature = "replicas")] - replicas: HashMap::new(), - } - } - - #[cfg(feature = "replicas")] - pub fn update_replicas(&mut self, map: HashMap) { - self.replicas = map; - } -} - -/// Added state associated with different server deployment types, synchronized by the router task. -pub enum ServerKind { - Sentinel { - version: Option, - /// An updated set of known sentinel nodes. - sentinels: Vec, - /// The server host/port resolved from the sentinel nodes, if known. - primary: Option, - }, - Cluster { - version: Option, - /// The cached cluster routing table. - cache: Option, - }, - Centralized { - version: Option, - }, -} - -impl ServerKind { - /// Create a new, empty server state cache. - pub fn new(config: &RedisConfig) -> Self { - match config.server { - ServerConfig::Clustered { .. } => ServerKind::Cluster { - version: None, - cache: None, - }, - ServerConfig::Sentinel { ref hosts, .. } => ServerKind::Sentinel { - version: None, - sentinels: hosts.clone(), - primary: None, - }, - ServerConfig::Centralized { .. } => ServerKind::Centralized { version: None }, - #[cfg(feature = "unix-sockets")] - ServerConfig::Unix { .. } => ServerKind::Centralized { version: None }, - } - } - - pub fn set_server_version(&mut self, new_version: Version) { - match self { - ServerKind::Cluster { ref mut version, .. } => { - *version = Some(new_version); - }, - ServerKind::Centralized { ref mut version, .. } => { - *version = Some(new_version); - }, - ServerKind::Sentinel { ref mut version, .. } => { - *version = Some(new_version); - }, - } - } - - pub fn server_version(&self) -> Option { - match self { - ServerKind::Cluster { ref version, .. } => version.clone(), - ServerKind::Centralized { ref version, .. } => version.clone(), - ServerKind::Sentinel { ref version, .. } => version.clone(), - } - } - - pub fn update_cluster_state(&mut self, state: Option) { - if let ServerKind::Cluster { ref mut cache, .. } = *self { - *cache = state; - } - } - - pub fn num_cluster_nodes(&self) -> usize { - if let ServerKind::Cluster { ref cache, .. } = *self { - cache - .as_ref() - .map(|state| state.unique_primary_nodes().len()) - .unwrap_or(1) - } else { - 1 - } - } - - pub fn with_cluster_state(&self, func: F) -> Result - where - F: FnOnce(&ClusterRouting) -> Result, - { - if let ServerKind::Cluster { ref cache, .. } = *self { - if let Some(state) = cache.as_ref() { - func(state) - } else { - Err(RedisError::new( - RedisErrorKind::Cluster, - "Missing cluster routing state.", - )) - } - } else { - Err(RedisError::new( - RedisErrorKind::Cluster, - "Missing cluster routing state.", - )) - } - } - - pub fn update_sentinel_primary(&mut self, server: &Server) { - if let ServerKind::Sentinel { ref mut primary, .. } = *self { - *primary = Some(server.clone()); - } - } - - pub fn sentinel_primary(&self) -> Option { - if let ServerKind::Sentinel { ref primary, .. } = *self { - primary.clone() - } else { - None - } - } - - pub fn update_sentinel_nodes(&mut self, server: &Server, nodes: Vec) { - if let ServerKind::Sentinel { - ref mut sentinels, - ref mut primary, - .. - } = *self - { - *primary = Some(server.clone()); - *sentinels = nodes; - } - } - - pub fn read_sentinel_nodes(&self, config: &ServerConfig) -> Option> { - if let ServerKind::Sentinel { ref sentinels, .. } = *self { - if sentinels.is_empty() { - match config { - ServerConfig::Sentinel { ref hosts, .. } => Some(hosts.clone()), - _ => None, - } - } else { - Some(sentinels.clone()) - } - } else { - None - } - } -} - -// TODO make a config option for other defaults and extend this -fn create_resolver(id: &Str) -> RefCount { - RefCount::new(DefaultResolver::new(id)) -} - -pub struct RedisClientInner { - /// An internal lock used to sync certain select operations that should not run concurrently across tasks. - pub _lock: Mutex<()>, - /// The client ID used for logging and the default `CLIENT SETNAME` value. - pub id: Str, - /// Whether the client uses RESP3. - pub resp3: RefCount, - /// The state of the underlying connection. - pub state: RwLock, - /// Client configuration options. - pub config: RefCount, - /// Connection configuration options. - pub connection: RefCount, - /// Performance config options for the client. - pub performance: RefSwap>, - /// An optional reconnect policy. - pub policy: RwLock>, - /// Notification channels for the event interfaces. - pub notifications: RefCount, - /// Shared counters. - pub counters: ClientCounters, - /// The DNS resolver to use when establishing new connections. - pub resolver: AsyncRwLock>, - /// A backchannel that can be used to control the router connections even while the connections are blocked. - pub backchannel: RefCount>, - /// Server state cache for various deployment types. - pub server_state: RwLock, - - /// An mpsc sender for commands to the router. - pub command_tx: RefSwap>, - /// Temporary storage for the receiver half of the router command channel. - pub command_rx: RwLock>, - - /// Command latency metrics. - #[cfg(feature = "metrics")] - pub latency_stats: RwLock, - /// Network latency metrics. - #[cfg(feature = "metrics")] - pub network_latency_stats: RwLock, - /// Payload size metrics tracking for requests. - #[cfg(feature = "metrics")] - pub req_size_stats: RefCount>, - /// Payload size metrics tracking for responses - #[cfg(feature = "metrics")] - pub res_size_stats: RefCount>, -} - -impl RedisClientInner { - pub fn new( - config: RedisConfig, - perf: PerformanceConfig, - connection: ConnectionConfig, - policy: Option, - ) -> RefCount { - let id = Str::from(format!("fred-{}", utils::random_string(10))); - let resolver = AsyncRwLock::new(create_resolver(&id)); - let (command_tx, command_rx) = unbounded_channel(); - let notifications = RefCount::new(Notifications::new(&id, perf.broadcast_channel_capacity)); - let (config, policy) = (RefCount::new(config), RwLock::new(policy)); - let performance = RefSwap::new(RefCount::new(perf)); - let (counters, state) = (ClientCounters::default(), RwLock::new(ClientState::Disconnected)); - let command_rx = RwLock::new(Some(command_rx)); - let backchannel = RefCount::new(AsyncRwLock::new(Backchannel::default())); - let server_state = RwLock::new(ServerState::new(&config)); - let resp3 = if config.version == RespVersion::RESP3 { - RefCount::new(AtomicBool::new(true)) - } else { - RefCount::new(AtomicBool::new(false)) - }; - let connection = RefCount::new(connection); - - #[cfg(feature = "glommio")] - let command_tx = command_tx.into(); - let command_tx = RefSwap::new(RefCount::new(command_tx)); - - RefCount::new(RedisClientInner { - _lock: Mutex::new(()), - #[cfg(feature = "metrics")] - latency_stats: RwLock::new(MovingStats::default()), - #[cfg(feature = "metrics")] - network_latency_stats: RwLock::new(MovingStats::default()), - #[cfg(feature = "metrics")] - req_size_stats: RefCount::new(RwLock::new(MovingStats::default())), - #[cfg(feature = "metrics")] - res_size_stats: RefCount::new(RwLock::new(MovingStats::default())), - - backchannel, - command_rx, - server_state, - command_tx, - state, - counters, - config, - performance, - policy, - resp3, - notifications, - resolver, - connection, - id, - }) - } - - pub fn is_pipelined(&self) -> bool { - self.performance.load().as_ref().auto_pipeline - } - - #[cfg(feature = "replicas")] - pub fn ignore_replica_reconnect_errors(&self) -> bool { - self.connection.replica.ignore_reconnection_errors - } - - #[cfg(not(feature = "replicas"))] - pub fn ignore_replica_reconnect_errors(&self) -> bool { - true - } - - /// Swap the command channel sender, returning the old one. - pub fn swap_command_tx(&self, tx: CommandSender) -> RefCount { - self.command_tx.swap(RefCount::new(tx)) - } - - /// Whether the client has the command channel receiver stored. If not then the caller can assume another - /// connection/router instance is using it. - pub fn has_command_rx(&self) -> bool { - self.command_rx.read().is_some() - } - - pub fn reset_server_state(&self) { - #[cfg(feature = "replicas")] - self.server_state.write().replicas.clear() - } - - pub fn shared_resp3(&self) -> RefCount { - self.resp3.clone() - } - - pub fn log_client_name_fn(&self, level: log::Level, func: F) - where - F: FnOnce(&str), - { - if log_enabled!(level) { - func(&self.id) - } - } - - pub async fn set_resolver(&self, resolver: RefCount) { - let mut guard = self.resolver.write().await; - *guard = resolver; - } - - pub fn cluster_discovery_policy(&self) -> Option<&ClusterDiscoveryPolicy> { - match self.config.server { - ServerConfig::Clustered { ref policy, .. } => Some(policy), - _ => None, - } - } - - pub async fn get_resolver(&self) -> RefCount { - self.resolver.write().await.clone() - } - - pub fn client_name(&self) -> &str { - &self.id - } - - pub fn num_cluster_nodes(&self) -> usize { - self.server_state.read().kind.num_cluster_nodes() - } - - pub fn with_cluster_state(&self, func: F) -> Result - where - F: FnOnce(&ClusterRouting) -> Result, - { - self.server_state.read().kind.with_cluster_state(func) - } - - pub fn with_perf_config(&self, func: F) -> R - where - F: FnOnce(&PerformanceConfig) -> R, - { - let guard = self.performance.load(); - func(guard.as_ref()) - } - - #[cfg(feature = "partial-tracing")] - pub fn should_trace(&self) -> bool { - self.config.tracing.enabled - } - - #[cfg(feature = "partial-tracing")] - pub fn tracing_span_level(&self) -> tracing::Level { - self.config.tracing.default_tracing_level - } - - #[cfg(feature = "full-tracing")] - pub fn full_tracing_span_level(&self) -> tracing::Level { - self.config.tracing.full_tracing_level - } - - #[cfg(not(feature = "partial-tracing"))] - pub fn should_trace(&self) -> bool { - false - } - - pub fn take_command_rx(&self) -> Option { - self.command_rx.write().take() - } - - pub fn store_command_rx(&self, rx: CommandReceiver, force: bool) { - let mut guard = self.command_rx.write(); - if guard.is_none() || force { - *guard = Some(rx); - } - } - - pub fn is_resp3(&self) -> bool { - utils::read_bool_atomic(&self.resp3) - } - - pub fn switch_protocol_versions(&self, version: RespVersion) { - match version { - RespVersion::RESP3 => utils::set_bool_atomic(&self.resp3, true), - RespVersion::RESP2 => utils::set_bool_atomic(&self.resp3, false), - }; - } - - pub fn update_performance_config(&self, config: PerformanceConfig) { - self.performance.store(RefCount::new(config)); - } - - pub fn performance_config(&self) -> PerformanceConfig { - self.performance.load().as_ref().clone() - } - - pub fn connection_config(&self) -> ConnectionConfig { - self.connection.as_ref().clone() - } - - pub fn reconnect_policy(&self) -> Option { - self.policy.read().as_ref().cloned() - } - - pub fn reset_protocol_version(&self) { - let resp3 = match self.config.version { - RespVersion::RESP3 => true, - RespVersion::RESP2 => false, - }; - - utils::set_bool_atomic(&self.resp3, resp3); - } - - pub fn max_command_attempts(&self) -> u32 { - self.connection.max_command_attempts - } - - pub fn max_feed_count(&self) -> u64 { - self.performance.load().max_feed_count - } - - pub fn default_command_timeout(&self) -> Duration { - self.performance.load().default_command_timeout - } - - pub fn connection_timeout(&self) -> Duration { - self.connection.connection_timeout - } - - pub fn internal_command_timeout(&self) -> Duration { - self.connection.internal_command_timeout - } - - pub async fn set_blocked_server(&self, server: &Server) { - self.backchannel.write().await.set_blocked(server); - } - - pub fn should_reconnect(&self) -> bool { - let has_policy = self - .policy - .read() - .as_ref() - .map(|policy| policy.should_reconnect()) - .unwrap_or(false); - - // do not attempt a reconnection if the client is intentionally disconnecting. the QUIT and SHUTDOWN commands set - // this flag. - let is_disconnecting = utils::read_locked(&self.state) == ClientState::Disconnecting; - - debug!( - "{}: Checking reconnect state. Has policy: {}, Is intentionally disconnecting: {}", - self.id, has_policy, is_disconnecting, - ); - has_policy && !is_disconnecting - } - - pub fn send_reconnect( - self: &RefCount, - server: Option, - force: bool, - tx: Option, - ) { - debug!("{}: Sending reconnect message to router for {:?}", self.id, server); - - let cmd = RouterCommand::Reconnect { - server, - force, - tx, - #[cfg(feature = "replicas")] - replica: false, - }; - if let Err(_) = interfaces::send_to_router(self, cmd) { - warn!("{}: Error sending reconnect command to router.", self.id); - } - } - - #[cfg(feature = "replicas")] - pub fn send_replica_reconnect(self: &RefCount, server: &Server) { - debug!( - "{}: Sending replica reconnect message to router for {:?}", - self.id, server - ); - - let cmd = RouterCommand::Reconnect { - server: Some(server.clone()), - force: false, - tx: None, - replica: true, - }; - if let Err(_) = interfaces::send_to_router(self, cmd) { - warn!("{}: Error sending reconnect command to router.", self.id); - } - } - - pub fn reset_reconnection_attempts(&self) { - if let Some(policy) = self.policy.write().deref_mut() { - policy.reset_attempts(); - } - } - - pub fn should_cluster_sync(&self, error: &RedisError) -> bool { - self.config.server.is_clustered() && error.is_cluster() - } - - pub async fn update_backchannel(&self, transport: RedisTransport) { - self.backchannel.write().await.transport = Some(transport); - } - - pub async fn wait_with_interrupt(&self, duration: Duration) -> Result<(), RedisError> { - #[allow(unused_mut)] - let mut rx = self.notifications.close.subscribe(); - debug!("{}: Sleeping for {} ms", self.id, duration.as_millis()); - let (sleep_ft, recv_ft) = (sleep(duration), rx.recv()); - tokio::pin!(sleep_ft); - tokio::pin!(recv_ft); - - if let Either::Right((_, _)) = select(sleep_ft, recv_ft).await { - Err(RedisError::new(RedisErrorKind::Canceled, "Connection(s) closed.")) - } else { - Ok(()) - } - } - - #[cfg(not(feature = "glommio"))] - pub fn send_command(&self, command: RouterCommand) -> Result<(), RouterCommand> { - self.command_tx.load().send(command).map_err(|e| e.0) - } - - #[cfg(feature = "glommio")] - pub fn send_command(&self, command: RouterCommand) -> Result<(), RouterCommand> { - self.command_tx.load().try_send(command).map_err(|e| match e { - glommio::GlommioError::Closed(glommio::ResourceType::Channel(v)) => v, - glommio::GlommioError::WouldBlock(glommio::ResourceType::Channel(v)) => v, - _ => unreachable!(), - }) - } -} diff --git a/src/modules/metrics.rs b/src/modules/metrics.rs deleted file mode 100644 index 0688bd6f..00000000 --- a/src/modules/metrics.rs +++ /dev/null @@ -1,110 +0,0 @@ -#![allow(unused_variables)] -#![allow(dead_code)] - -use std::cmp; - -/// Stats describing a distribution of samples. -/// -/// Time units are in milliseconds, data size units are in bytes. -pub struct Stats { - pub min: i64, - pub max: i64, - pub avg: f64, - pub stddev: f64, - pub samples: u64, - pub sum: i64, -} - -/// Struct for tracking moving stats about network latency or request/response sizes. -/// -/// Time units are in milliseconds, data size units are in bytes. -pub struct MovingStats { - pub min: i64, - pub max: i64, - pub avg: f64, - pub variance: f64, - pub samples: u64, - pub sum: i64, - old_avg: f64, - s: f64, - old_s: f64, -} - -impl Default for MovingStats { - fn default() -> Self { - MovingStats { - min: 0, - max: 0, - avg: 0.0, - sum: 0, - variance: 0.0, - samples: 0, - s: 0.0, - old_s: 0.0, - old_avg: 0.0, - } - } -} - -impl MovingStats { - pub fn sample(&mut self, value: i64) { - self.samples += 1; - let num_samples = self.samples as f64; - let value_f = value as f64; - self.sum += value; - - if self.samples == 1 { - self.avg = value_f; - self.variance = 0.0; - self.old_avg = value_f; - self.old_s = 0.0; - self.min = value; - self.max = value; - } else { - self.avg = self.old_avg + (value_f - self.old_avg) / num_samples; - self.s = self.old_s + (value_f - self.old_avg) * (value_f - self.avg); - - self.old_avg = self.avg; - self.old_s = self.s; - self.variance = self.s / (num_samples - 1.0); - - self.min = cmp::min(self.min, value); - self.max = cmp::max(self.max, value); - } - } - - pub fn reset(&mut self) { - self.min = 0; - self.max = 0; - self.avg = 0.0; - self.variance = 0.0; - self.samples = 0; - self.sum = 0; - self.s = 0.0; - self.old_s = 0.0; - self.old_avg = 0.0; - } - - pub fn read_metrics(&self) -> Stats { - self.into() - } - - pub fn take_metrics(&mut self) -> Stats { - let metrics = self.read_metrics(); - self.reset(); - metrics - } -} - -impl<'a> From<&'a MovingStats> for Stats { - fn from(stats: &'a MovingStats) -> Stats { - Stats { - avg: stats.avg, - stddev: stats.variance.sqrt(), - min: stats.min, - max: stats.max, - samples: stats.samples, - sum: stats.sum, - } - } -} diff --git a/src/modules/mocks.rs b/src/modules/mocks.rs deleted file mode 100644 index 322b284e..00000000 --- a/src/modules/mocks.rs +++ /dev/null @@ -1,406 +0,0 @@ -//! An interface for mocking Redis commands. -//! -//! There are several patterns for utilizing a mocking layer in tests. In some cases a simple "echo" interface is -//! enough, or in others callers may need to buffer a series of commands before performing any assertions, etc. More -//! complicated test scenarios may require storing and operating on real values. -//! -//! This interface exposes several interfaces and structs for supporting the above use cases: -//! * `Echo` - A simple mocking struct that returns the provided arguments back to the caller. -//! * `SimpleMap` - A mocking struct that implements the basic `GET`, `SET`, and `DEL` commands. -//! * `Buffer` - A mocking struct that buffers commands internally, returning `QUEUED` to each command. Callers can -//! then drain or inspect the buffer later. -//! -//! The base `Mocks` trait is directly exposed so callers can implement their own mocking layer as well. - -use crate::{ - error::{RedisError, RedisErrorKind}, - runtime::Mutex, - types::{RedisKey, RedisValue}, -}; -use bytes_utils::Str; -use std::{ - collections::{HashMap, VecDeque}, - fmt::Debug, -}; - -/// A wrapper type for the parts of an internal Redis command. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct MockCommand { - /// The first word in the command string. For example: - /// * `SET` - `"SET"` - /// * `XGROUP CREATE` - `"XGROUP"` - /// * `INCRBY` - `"INCRBY"` - pub cmd: Str, - /// The optional subcommand string (or second word) in the command string. For example: - /// * `SET` - `None` - /// * `XGROUP CREATE` - `Some("CREATE")` - /// * `INCRBY` - `None` - pub subcommand: Option, - /// The ordered list of arguments to the command. - pub args: Vec, -} - -/// An interface for intercepting and processing Redis commands in a mocking layer. -#[allow(unused_variables)] -pub trait Mocks: Debug + Send + Sync + 'static { - /// Intercept and process a Redis command, returning any `RedisValue`. - /// - /// # Important - /// - /// The caller must ensure the response value makes sense in the context of the specific command(s) being mocked. - /// The parsing logic following each command on the public interface will still be applied. __Most__ commands - /// perform minimal parsing on the response, but some may require specific response formats to function correctly. - /// - /// `RedisValue::Queued` can be used to return a value that will work almost anywhere. - fn process_command(&self, command: MockCommand) -> Result; - - /// Intercept and process an entire transaction. The provided commands will **not** include `MULTI` or `EXEC`. - /// - /// Note: The default implementation redirects each command to the [process_command](Self::process_command) - /// function. The results of each call are buffered and returned as an array. - fn process_transaction(&self, commands: Vec) -> Result { - let mut out = Vec::with_capacity(commands.len()); - - for command in commands.into_iter() { - out.push(self.process_command(command)?); - } - Ok(RedisValue::Array(out)) - } -} - -/// An implementation of a mocking layer that returns the provided arguments to the caller. -/// -/// ```rust no_run -/// # use fred::prelude::*; -/// #[tokio::test] -/// async fn should_use_echo_mock() { -/// let config = RedisConfig { -/// mocks: Some(Arc::new(Echo)), -/// ..Default::default() -/// }; -/// let client = Builder::from_config(config).build().unwrap(); -/// client.init().await.expect("Failed to connect"); -/// -/// let actual: Vec = client -/// .set( -/// "foo", -/// "bar", -/// Some(Expiration::EX(100)), -/// Some(SetOptions::NX), -/// false, -/// ) -/// .await -/// .expect("Failed to call SET"); -/// -/// let expected: Vec = vec![ -/// "foo".into(), -/// "bar".into(), -/// "EX".into(), -/// 100.into(), -/// "NX".into(), -/// ]; -/// assert_eq!(actual, expected); -/// } -/// ``` -#[derive(Debug)] -pub struct Echo; - -impl Mocks for Echo { - fn process_command(&self, command: MockCommand) -> Result { - Ok(RedisValue::Array(command.args)) - } -} - -/// A struct that implements some of the basic mapping functions. If callers require a mocking layer that stores and -/// operates on real values then this struct is a good place to start. -/// -/// Note: This does **not** support expirations or `NX|XX` qualifiers. -/// -/// ```rust no_run -/// #[tokio::test] -/// async fn should_use_echo_mock() { -/// let config = RedisConfig { -/// mocks: Some(Arc::new(SimpleMap::new())), -/// ..Default::default() -/// }; -/// let client = Builder::from_config(config).build().unwrap(); -/// client.init().await.expect("Failed to connect"); -/// -/// let actual: String = client -/// .set("foo", "bar", None, None, false) -/// .await -/// .expect("Failed to call SET"); -/// assert_eq!(actual, "OK"); -/// -/// let actual: String = client.get("foo").await.expect("Failed to call GET"); -/// assert_eq!(actual, "bar"); -/// } -/// ``` -#[derive(Debug)] -pub struct SimpleMap { - values: Mutex>, -} - -impl SimpleMap { - /// Create a new empty `SimpleMap`. - pub fn new() -> Self { - SimpleMap { - values: Mutex::new(HashMap::new()), - } - } - - /// Clear the inner map. - pub fn clear(&self) { - self.values.lock().clear(); - } - - /// Take the inner map. - pub fn take(&self) -> HashMap { - self.values.lock().drain().collect() - } - - /// Read a copy of the inner map. - pub fn inner(&self) -> HashMap { - self.values.lock().iter().map(|(k, v)| (k.clone(), v.clone())).collect() - } - - /// Perform a `GET` operation. - pub fn get(&self, args: Vec) -> Result { - let key: RedisKey = match args.first() { - Some(key) => key.clone().try_into()?, - None => return Err(RedisError::new(RedisErrorKind::InvalidArgument, "Missing key.")), - }; - - Ok(self.values.lock().get(&key).cloned().unwrap_or(RedisValue::Null)) - } - - /// Perform a `SET` operation. - pub fn set(&self, mut args: Vec) -> Result { - args.reverse(); - let key: RedisKey = match args.pop() { - Some(key) => key.try_into()?, - None => return Err(RedisError::new(RedisErrorKind::InvalidArgument, "Missing key.")), - }; - let value = match args.pop() { - Some(value) => value, - None => return Err(RedisError::new(RedisErrorKind::InvalidArgument, "Missing value.")), - }; - - let _ = self.values.lock().insert(key, value); - Ok(RedisValue::new_ok()) - } - - /// Perform a `DEL` operation. - pub fn del(&self, args: Vec) -> Result { - let mut guard = self.values.lock(); - let mut count = 0; - - for arg in args.into_iter() { - let key: RedisKey = arg.try_into()?; - if guard.remove(&key).is_some() { - count += 1; - } - } - - Ok(count.into()) - } -} - -impl Mocks for SimpleMap { - fn process_command(&self, command: MockCommand) -> Result { - match &*command.cmd { - "GET" => self.get(command.args), - "SET" => self.set(command.args), - "DEL" => self.del(command.args), - _ => Err(RedisError::new(RedisErrorKind::Unknown, "Unimplemented.")), - } - } -} - -/// A mocking layer that buffers the commands internally and returns `QUEUED` to the caller. -/// -/// ```rust -/// #[tokio::test] -/// async fn should_use_buffer_mock() { -/// let buffer = Arc::new(Buffer::new()); -/// let config = RedisConfig { -/// mocks: Some(buffer.clone()), -/// ..Default::default() -/// }; -/// let client = Builder::from_config(config).build().unwrap(); -/// client.init().await.expect("Failed to connect"); -/// -/// let actual: String = client -/// .set("foo", "bar", None, None, false) -/// .await -/// .expect("Failed to call SET"); -/// assert_eq!(actual, "QUEUED"); -/// -/// let actual: String = client.get("foo").await.expect("Failed to call GET"); -/// assert_eq!(actual, "QUEUED"); -/// -/// // note: values that act as keys use the `RedisValue::Bytes` variant internally -/// let expected = vec![ -/// MockCommand { -/// cmd: "SET".into(), -/// subcommand: None, -/// args: vec!["foo".as_bytes().into(), "bar".into()], -/// }, -/// MockCommand { -/// cmd: "GET".into(), -/// subcommand: None, -/// args: vec!["foo".as_bytes().into()], -/// }, -/// ]; -/// assert_eq!(buffer.take(), expected); -/// } -/// ``` -#[derive(Debug)] -pub struct Buffer { - commands: Mutex>, -} - -impl Buffer { - /// Create a new empty `Buffer`. - pub fn new() -> Self { - Buffer { - commands: Mutex::new(VecDeque::new()), - } - } - - /// Read the length of the internal buffer. - pub fn len(&self) -> usize { - self.commands.lock().len() - } - - /// Clear the inner buffer. - pub fn clear(&self) { - self.commands.lock().clear(); - } - - /// Drain and return the internal command buffer. - pub fn take(&self) -> Vec { - self.commands.lock().drain(..).collect() - } - - /// Read a copy of the internal command buffer without modifying the contents. - pub fn inner(&self) -> Vec { - self.commands.lock().iter().cloned().collect() - } - - /// Push a new command onto the back of the internal buffer. - pub fn push_back(&self, command: MockCommand) { - self.commands.lock().push_back(command); - } - - /// Pop a command from the back of the internal buffer. - pub fn pop_back(&self) -> Option { - self.commands.lock().pop_back() - } - - /// Push a new command onto the front of the internal buffer. - pub fn push_front(&self, command: MockCommand) { - self.commands.lock().push_front(command); - } - - /// Pop a command from the front of the internal buffer. - pub fn pop_front(&self) -> Option { - self.commands.lock().pop_front() - } -} - -impl Mocks for Buffer { - fn process_command(&self, command: MockCommand) -> Result { - self.push_back(command); - Ok(RedisValue::Queued) - } -} - -#[cfg(test)] -#[cfg(all(feature = "mocks", feature = "i-keys"))] -mod tests { - use super::*; - use crate::{ - clients::RedisClient, - error::RedisError, - interfaces::{ClientLike, KeysInterface}, - mocks::{Buffer, Echo, Mocks, SimpleMap}, - prelude::Expiration, - types::{RedisConfig, RedisValue, SetOptions}, - }; - use std::sync::Arc; - use tokio::task::JoinHandle; - - async fn create_mock_client(mocks: Arc) -> (RedisClient, JoinHandle>) { - let config = RedisConfig { - mocks: Some(mocks), - ..Default::default() - }; - let client = RedisClient::new(config, None, None, None); - let jh = client.connect(); - client.wait_for_connect().await.expect("Failed to connect"); - - (client, jh) - } - - #[tokio::test] - async fn should_create_mock_config_and_client() { - let _ = create_mock_client(Arc::new(Echo)).await; - } - - #[tokio::test] - async fn should_use_echo_mock() { - let (client, _) = create_mock_client(Arc::new(Echo)).await; - - let actual: Vec = client - .set("foo", "bar", Some(Expiration::EX(100)), Some(SetOptions::NX), false) - .await - .expect("Failed to call SET"); - - let expected: Vec = vec!["foo".into(), "bar".into(), "EX".into(), 100.into(), "NX".into()]; - assert_eq!(actual, expected); - } - - #[tokio::test] - async fn should_use_simple_map_mock() { - let (client, _) = create_mock_client(Arc::new(SimpleMap::new())).await; - - let actual: String = client - .set("foo", "bar", None, None, false) - .await - .expect("Failed to call SET"); - assert_eq!(actual, "OK"); - - let actual: String = client.get("foo").await.expect("Failed to call GET"); - assert_eq!(actual, "bar"); - } - - #[tokio::test] - async fn should_use_buffer_mock() { - let buffer = Arc::new(Buffer::new()); - let (client, _) = create_mock_client(buffer.clone()).await; - - let actual: String = client - .set("foo", "bar", None, None, false) - .await - .expect("Failed to call SET"); - assert_eq!(actual, "QUEUED"); - - let actual: String = client.get("foo").await.expect("Failed to call GET"); - assert_eq!(actual, "QUEUED"); - - let expected = vec![ - MockCommand { - cmd: "SET".into(), - subcommand: None, - args: vec!["foo".as_bytes().into(), "bar".into()], - }, - MockCommand { - cmd: "GET".into(), - subcommand: None, - args: vec!["foo".as_bytes().into()], - }, - ]; - assert_eq!(buffer.take(), expected); - } -} diff --git a/src/modules/mod.rs b/src/modules/mod.rs deleted file mode 100644 index 5ba4f37b..00000000 --- a/src/modules/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod backchannel; -/// Utility functions for reading or changing global config values. -pub mod inner; -pub mod metrics; -pub mod response; - -#[cfg(feature = "mocks")] -#[cfg_attr(docsrs, doc(cfg(feature = "mocks")))] -pub mod mocks; diff --git a/src/modules/response.rs b/src/modules/response.rs deleted file mode 100644 index 021359f0..00000000 --- a/src/modules/response.rs +++ /dev/null @@ -1,992 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - types::{RedisKey, RedisValue, QUEUED}, -}; -use bytes::Bytes; -use bytes_utils::Str; -use std::{ - collections::{BTreeMap, BTreeSet, HashMap, HashSet}, - hash::{BuildHasher, Hash}, -}; - -#[cfg(feature = "i-cluster")] -use crate::types::ClusterInfo; -#[cfg(feature = "i-geo")] -use crate::types::GeoPosition; -#[cfg(feature = "i-slowlog")] -use crate::types::SlowlogEntry; -#[cfg(feature = "i-memory")] -use crate::types::{DatabaseMemoryStats, MemoryStats}; - -#[allow(unused_imports)] -use std::any::type_name; - -#[cfg(feature = "serde-json")] -use serde_json::{Map, Value}; - -macro_rules! debug_type( - ($($arg:tt)*) => { - #[cfg(feature="network-logs")] - log::trace!($($arg)*); - } -); - -macro_rules! check_single_bulk_reply( - ($v:expr) => { - if $v.is_single_element_vec() { - return Self::from_value($v.pop_or_take()); - } - }; - ($t:ty, $v:expr) => { - if $v.is_single_element_vec() { - return $t::from_value($v.pop_or_take()); - } - } -); - -macro_rules! to_signed_number( - ($t:ty, $v:expr) => { - match $v { - RedisValue::Double(f) => Ok(f as $t), - RedisValue::Integer(i) => Ok(i as $t), - RedisValue::String(s) => s.parse::<$t>().map_err(|e| e.into()), - RedisValue::Array(mut a) => if a.len() == 1 { - match a.pop().unwrap() { - RedisValue::Integer(i) => Ok(i as $t), - RedisValue::String(s) => s.parse::<$t>().map_err(|e| e.into()), - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Ok(0), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => Err(RedisError::new(RedisErrorKind::NotFound, "Cannot convert nil to number.")), - _ => Err(RedisError::new_parse("Cannot convert to number.")) - } - }else{ - Err(RedisError::new_parse("Cannot convert array to number.")) - } - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Ok(0), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => Err(RedisError::new(RedisErrorKind::NotFound, "Cannot convert nil to number.")), - _ => Err(RedisError::new_parse("Cannot convert to number.")), - } - } -); - -macro_rules! to_unsigned_number( - ($t:ty, $v:expr) => { - match $v { - RedisValue::Double(f) => if f.is_sign_negative() { - Err(RedisError::new_parse("Cannot convert from negative number.")) - }else{ - Ok(f as $t) - }, - RedisValue::Integer(i) => if i < 0 { - Err(RedisError::new_parse("Cannot convert from negative number.")) - }else{ - Ok(i as $t) - }, - RedisValue::String(s) => s.parse::<$t>().map_err(|e| e.into()), - RedisValue::Array(mut a) => if a.len() == 1 { - match a.pop().unwrap() { - RedisValue::Integer(i) => if i < 0 { - Err(RedisError::new_parse("Cannot convert from negative number.")) - }else{ - Ok(i as $t) - }, - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Ok(0), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => Err(RedisError::new(RedisErrorKind::NotFound, "Cannot convert nil to number.")), - RedisValue::String(s) => s.parse::<$t>().map_err(|e| e.into()), - _ => Err(RedisError::new_parse("Cannot convert to number.")) - } - }else{ - Err(RedisError::new_parse("Cannot convert array to number.")) - }, - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Ok(0), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => Err(RedisError::new(RedisErrorKind::NotFound, "Cannot convert nil to number.")), - _ => Err(RedisError::new_parse("Cannot convert to number.")), - } - } -); - -macro_rules! impl_signed_number ( - ($t:ty) => { - impl FromRedis for $t { - fn from_value(value: RedisValue) -> Result<$t, RedisError> { - check_single_bulk_reply!(value); - to_signed_number!($t, value) - } - } - } -); - -macro_rules! impl_unsigned_number ( - ($t:ty) => { - impl FromRedis for $t { - fn from_value(value: RedisValue) -> Result<$t, RedisError> { - check_single_bulk_reply!(value); - to_unsigned_number!($t, value) - } - } - } -); - -/// A trait used to [convert](RedisValue::convert) various forms of [RedisValue](RedisValue) into different types. -/// -/// ## Examples -/// -/// ```rust -/// # use fred::types::RedisValue; -/// # use std::collections::HashMap; -/// let foo: usize = RedisValue::String("123".into()).convert()?; -/// let foo: i64 = RedisValue::String("123".into()).convert()?; -/// let foo: String = RedisValue::String("123".into()).convert()?; -/// let foo: Vec = RedisValue::Bytes(vec![102, 111, 111].into()).convert()?; -/// let foo: Vec = RedisValue::String("foo".into()).convert()?; -/// let foo: Vec = RedisValue::Array(vec!["a".into(), "b".into()]).convert()?; -/// let foo: HashMap = -/// RedisValue::Array(vec!["a".into(), 1.into(), "b".into(), 2.into()]).convert()?; -/// let foo: (String, i64) = RedisValue::Array(vec!["a".into(), 1.into()]).convert()?; -/// let foo: Vec<(String, i64)> = -/// RedisValue::Array(vec!["a".into(), 1.into(), "b".into(), 2.into()]).convert()?; -/// // ... -/// ``` -/// -/// ## Bulk Values -/// -/// This interface can also convert single-element vectors to scalar values in certain scenarios. This is often -/// useful with commands that conditionally return bulk values, or where the number of elements in the response -/// depends on the number of arguments (`MGET`, etc). -/// -/// For example: -/// -/// ```rust -/// # use fred::types::RedisValue; -/// let _: String = RedisValue::Array(vec![]).convert()?; // error -/// let _: String = RedisValue::Array(vec!["a".into()]).convert()?; // "a" -/// let _: String = RedisValue::Array(vec!["a".into(), "b".into()]).convert()?; // error -/// let _: Option = RedisValue::Array(vec![]).convert()?; // None -/// let _: Option = RedisValue::Array(vec!["a".into()]).convert()?; // Some("a") -/// let _: Option = RedisValue::Array(vec!["a".into(), "b".into()]).convert()?; // error -/// ``` -/// -/// ## The `default-nil-types` Feature Flag -/// -/// By default a `nil` value cannot be converted directly into any of the scalar types (`u8`, `String`, `Bytes`, -/// etc). In practice this often requires callers to use an `Option` or `Vec` container with commands that can return -/// `nil`. -/// -/// The `default-nil-types` feature flag can enable some further type conversion branches that treat `nil` values as -/// default values for the relevant type. For `RedisValue::Null` these include: -/// -/// * `impl FromRedis` for `String` or `Str` returns an empty string. -/// * `impl FromRedis` for `Bytes` or `Vec` returns an empty array. -/// * `impl FromRedis` for any integer or float type returns `0` -/// * `impl FromRedis` for `bool` returns `false` -/// * `impl FromRedis` for map or set types return an empty map or set. -pub trait FromRedis: Sized { - fn from_value(value: RedisValue) -> Result; - - #[doc(hidden)] - fn from_values(values: Vec) -> Result, RedisError> { - values.into_iter().map(|v| Self::from_value(v)).collect() - } - - #[doc(hidden)] - // FIXME if/when specialization is stable - fn from_owned_bytes(_: Vec) -> Option> { - None - } - #[doc(hidden)] - fn is_tuple() -> bool { - false - } -} - -impl FromRedis for RedisValue { - fn from_value(value: RedisValue) -> Result { - Ok(value) - } -} - -impl FromRedis for () { - fn from_value(_: RedisValue) -> Result { - Ok(()) - } -} - -impl_signed_number!(i8); -impl_signed_number!(i16); -impl_signed_number!(i32); -impl_signed_number!(i64); -impl_signed_number!(i128); -impl_signed_number!(isize); - -impl FromRedis for u8 { - fn from_value(value: RedisValue) -> Result { - check_single_bulk_reply!(value); - to_unsigned_number!(u8, value) - } - - fn from_owned_bytes(d: Vec) -> Option> { - Some(d) - } -} - -impl_unsigned_number!(u16); -impl_unsigned_number!(u32); -impl_unsigned_number!(u64); -impl_unsigned_number!(u128); -impl_unsigned_number!(usize); - -impl FromRedis for String { - fn from_value(value: RedisValue) -> Result { - debug_type!("FromRedis(String): {:?}", value); - check_single_bulk_reply!(value); - - value - .into_string() - .ok_or(RedisError::new_parse("Could not convert to string.")) - } -} - -impl FromRedis for Str { - fn from_value(value: RedisValue) -> Result { - debug_type!("FromRedis(Str): {:?}", value); - check_single_bulk_reply!(value); - - value - .into_bytes_str() - .ok_or(RedisError::new_parse("Could not convert to string.")) - } -} - -impl FromRedis for f64 { - fn from_value(value: RedisValue) -> Result { - debug_type!("FromRedis(f64): {:?}", value); - check_single_bulk_reply!(value); - - value - .as_f64() - .ok_or(RedisError::new_parse("Could not convert to double.")) - } -} - -impl FromRedis for f32 { - fn from_value(value: RedisValue) -> Result { - debug_type!("FromRedis(f32): {:?}", value); - check_single_bulk_reply!(value); - - value - .as_f64() - .map(|f| f as f32) - .ok_or(RedisError::new_parse("Could not convert to float.")) - } -} - -impl FromRedis for bool { - fn from_value(value: RedisValue) -> Result { - debug_type!("FromRedis(bool): {:?}", value); - check_single_bulk_reply!(value); - - if let Some(val) = value.as_bool() { - Ok(val) - } else { - // it's not obvious how to convert the value to a bool in this block, so we go with a - // tried and true approach that i'm sure we'll never regret - JS semantics - Ok(match value { - RedisValue::String(s) => !s.is_empty(), - RedisValue::Bytes(b) => !b.is_empty(), - // everything else should be covered by `as_bool` above - _ => return Err(RedisError::new_parse("Could not convert to bool.")), - }) - } - } -} - -impl FromRedis for Option -where - T: FromRedis, -{ - fn from_value(value: RedisValue) -> Result, RedisError> { - debug_type!("FromRedis(Option<{}>): {:?}", type_name::(), value); - - if let Some(0) = value.array_len() { - Ok(None) - } else if value.is_null() { - Ok(None) - } else { - Ok(Some(T::from_value(value)?)) - } - } -} - -impl FromRedis for Bytes { - fn from_value(value: RedisValue) -> Result { - debug_type!("FromRedis(Bytes): {:?}", value); - check_single_bulk_reply!(value); - - value - .into_bytes() - .ok_or(RedisError::new_parse("Cannot parse into bytes.")) - } -} - -impl FromRedis for Vec -where - T: FromRedis, -{ - fn from_value(value: RedisValue) -> Result, RedisError> { - debug_type!("FromRedis(Vec<{}>): {:?}", type_name::(), value); - - match value { - RedisValue::Bytes(bytes) => { - T::from_owned_bytes(bytes.to_vec()).ok_or(RedisError::new_parse("Cannot convert from bytes")) - }, - RedisValue::String(string) => { - // hacky way to check if T is bytes without consuming `string` - if T::from_owned_bytes(Vec::new()).is_some() { - T::from_owned_bytes(string.into_inner().to_vec()) - .ok_or(RedisError::new_parse("Could not convert string to bytes.")) - } else { - Ok(vec![T::from_value(RedisValue::String(string))?]) - } - }, - RedisValue::Array(values) => { - if !values.is_empty() { - if let RedisValue::Array(_) = &values[0] { - values.into_iter().map(|x| T::from_value(x)).collect() - } else { - T::from_values(values) - } - } else { - Ok(vec![]) - } - }, - RedisValue::Map(map) => { - // not being able to use collect() here is unfortunate - let out = Vec::with_capacity(map.len() * 2); - map.inner().into_iter().try_fold(out, |mut out, (key, value)| { - if T::is_tuple() { - // try to convert to a 2-element tuple since that's a common use case from `HGETALL`, etc - out.push(T::from_value(RedisValue::Array(vec![key.into(), value]))?); - } else { - out.push(T::from_value(key.into())?); - out.push(T::from_value(value)?); - } - - Ok(out) - }) - }, - RedisValue::Integer(i) => Ok(vec![T::from_value(RedisValue::Integer(i))?]), - RedisValue::Double(f) => Ok(vec![T::from_value(RedisValue::Double(f))?]), - RedisValue::Boolean(b) => Ok(vec![T::from_value(RedisValue::Boolean(b))?]), - RedisValue::Queued => Ok(vec![T::from_value(RedisValue::from_static_str(QUEUED))?]), - RedisValue::Null => Ok(Vec::new()), - } - } -} - -impl FromRedis for [T; N] -where - T: FromRedis, -{ - fn from_value(value: RedisValue) -> Result<[T; N], RedisError> { - debug_type!("FromRedis([{}; {}]): {:?}", type_name::(), N, value); - // use the `from_value` impl for Vec - let value: Vec = value.convert()?; - let len = value.len(); - - value - .try_into() - .map_err(|_| RedisError::new_parse(format!("Failed to convert to array. Expected {}, found {}.", N, len))) - } -} - -impl FromRedis for HashMap -where - K: FromRedisKey + Eq + Hash, - V: FromRedis, - S: BuildHasher + Default, -{ - fn from_value(value: RedisValue) -> Result { - debug_type!( - "FromRedis(HashMap<{}, {}>): {:?}", - type_name::(), - type_name::(), - value - ); - - let as_map = if value.is_array() || value.is_map() || value.is_null() { - value - .into_map() - .map_err(|_| RedisError::new_parse("Cannot convert to map."))? - } else { - return Err(RedisError::new_parse("Cannot convert to map.")); - }; - - as_map - .inner() - .into_iter() - .map(|(k, v)| Ok((K::from_key(k)?, V::from_value(v)?))) - .collect() - } -} - -impl FromRedis for HashSet -where - V: FromRedis + Hash + Eq, - S: BuildHasher + Default, -{ - fn from_value(value: RedisValue) -> Result { - debug_type!("FromRedis(HashSet<{}>): {:?}", type_name::(), value); - value.into_set()?.into_iter().map(|v| V::from_value(v)).collect() - } -} - -impl FromRedis for BTreeMap -where - K: FromRedisKey + Ord, - V: FromRedis, -{ - fn from_value(value: RedisValue) -> Result { - debug_type!( - "FromRedis(BTreeMap<{}, {}>): {:?}", - type_name::(), - type_name::(), - value - ); - let as_map = if value.is_array() || value.is_map() || value.is_null() { - value - .into_map() - .map_err(|_| RedisError::new_parse("Cannot convert to map."))? - } else { - return Err(RedisError::new_parse("Cannot convert to map.")); - }; - - as_map - .inner() - .into_iter() - .map(|(k, v)| Ok((K::from_key(k)?, V::from_value(v)?))) - .collect() - } -} - -impl FromRedis for BTreeSet -where - V: FromRedis + Ord, -{ - fn from_value(value: RedisValue) -> Result { - debug_type!("FromRedis(BTreeSet<{}>): {:?}", type_name::(), value); - value.into_set()?.into_iter().map(|v| V::from_value(v)).collect() - } -} - -// adapted from mitsuhiko -macro_rules! impl_from_redis_tuple { - () => (); - ($($name:ident,)+) => ( - #[doc(hidden)] - impl<$($name: FromRedis),*> FromRedis for ($($name,)*) { - fn is_tuple() -> bool { - true - } - - #[allow(non_snake_case, unused_variables)] - fn from_value(v: RedisValue) -> Result<($($name,)*), RedisError> { - if let RedisValue::Array(mut values) = v { - let mut n = 0; - $(let $name = (); n += 1;)* - debug_type!("FromRedis({}-tuple): {:?}", n, values); - if values.len() != n { - return Err(RedisError::new_parse(format!("Invalid tuple dimension. Expected {}, found {}.", n, values.len()))); - } - - // since we have ownership over the values we have some freedom in how to implement this - values.reverse(); - Ok(($({let $name = (); values - .pop() - .ok_or(RedisError::new_parse("Expected value, found none."))? - .convert()? - },)*)) - }else{ - Err(RedisError::new_parse("Could not convert to tuple.")) - } - } - - #[allow(non_snake_case, unused_variables)] - fn from_values(mut values: Vec) -> Result, RedisError> { - let mut n = 0; - $(let $name = (); n += 1;)* - debug_type!("FromRedis({}-tuple): {:?}", n, values); - if values.len() % n != 0 { - return Err(RedisError::new_parse(format!("Invalid tuple dimension. Expected {}, found {}.", n, values.len()))); - } - - let mut out = Vec::with_capacity(values.len() / n); - // this would be cleaner if there were an owned `chunks` variant - for chunk in values.chunks_exact_mut(n) { - match chunk { - [$($name),*] => out.push(($($name.take().convert()?),*),), - _ => unreachable!(), - } - } - - Ok(out) - } - } - impl_from_redis_peel!($($name,)*); - ) -} - -macro_rules! impl_from_redis_peel { - ($name:ident, $($other:ident,)*) => (impl_from_redis_tuple!($($other,)*);) -} - -impl_from_redis_tuple! { T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, } - -macro_rules! impl_from_str_from_redis_key ( - ($t:ty) => { - impl FromRedisKey for $t { - fn from_key(value: RedisKey) -> Result<$t, RedisError> { - value - .as_str() - .and_then(|k| k.parse::<$t>().ok()) - .ok_or(RedisError::new_parse("Cannot parse key from bytes.")) - } - } - } -); - -#[cfg(feature = "serde-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde-json")))] -impl FromRedis for Value { - fn from_value(value: RedisValue) -> Result { - let value = match value { - RedisValue::Null => Value::Null, - RedisValue::Queued => QUEUED.into(), - RedisValue::String(s) => { - // check for nested json. this is particularly useful with JSON.GET - serde_json::from_str(&s).ok().unwrap_or_else(|| s.to_string().into()) - }, - RedisValue::Bytes(b) => { - let val = RedisValue::String(Str::from_inner(b)?); - Self::from_value(val)? - }, - RedisValue::Integer(i) => i.into(), - RedisValue::Double(f) => f.into(), - RedisValue::Boolean(b) => b.into(), - RedisValue::Array(v) => { - let mut out = Vec::with_capacity(v.len()); - for value in v.into_iter() { - out.push(Self::from_value(value)?); - } - Value::Array(out) - }, - RedisValue::Map(v) => { - let mut out = Map::with_capacity(v.len()); - for (key, value) in v.inner().into_iter() { - let key = key - .into_string() - .ok_or(RedisError::new_parse("Cannot convert key to string."))?; - let value = Self::from_value(value)?; - - out.insert(key, value); - } - Value::Object(out) - }, - }; - - Ok(value) - } -} - -#[cfg(feature = "i-geo")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))] -impl FromRedis for GeoPosition { - fn from_value(value: RedisValue) -> Result { - GeoPosition::try_from(value) - } -} - -#[cfg(feature = "i-slowlog")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-slowlog")))] -impl FromRedis for SlowlogEntry { - fn from_value(value: RedisValue) -> Result { - SlowlogEntry::try_from(value) - } -} - -#[cfg(feature = "i-cluster")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))] -impl FromRedis for ClusterInfo { - fn from_value(value: RedisValue) -> Result { - ClusterInfo::try_from(value) - } -} - -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -impl FromRedis for MemoryStats { - fn from_value(value: RedisValue) -> Result { - MemoryStats::try_from(value) - } -} - -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -impl FromRedis for DatabaseMemoryStats { - fn from_value(value: RedisValue) -> Result { - DatabaseMemoryStats::try_from(value) - } -} - -impl FromRedis for RedisKey { - fn from_value(value: RedisValue) -> Result { - let key = match value { - RedisValue::Boolean(b) => b.into(), - RedisValue::Integer(i) => i.into(), - RedisValue::Double(f) => f.into(), - RedisValue::String(s) => s.into(), - RedisValue::Bytes(b) => b.into(), - RedisValue::Queued => RedisKey::from_static_str(QUEUED), - RedisValue::Map(_) | RedisValue::Array(_) => { - return Err(RedisError::new_parse("Cannot convert aggregate type to key.")) - }, - RedisValue::Null => return Err(RedisError::new(RedisErrorKind::NotFound, "Cannot convert nil to key.")), - }; - - Ok(key) - } -} - -/// A trait used to convert [RedisKey](crate::types::RedisKey) values to various types. -/// -/// See the [convert](crate::types::RedisKey::convert) documentation for more information. -pub trait FromRedisKey: Sized { - fn from_key(value: RedisKey) -> Result; -} - -impl_from_str_from_redis_key!(u8); -impl_from_str_from_redis_key!(u16); -impl_from_str_from_redis_key!(u32); -impl_from_str_from_redis_key!(u64); -impl_from_str_from_redis_key!(u128); -impl_from_str_from_redis_key!(usize); -impl_from_str_from_redis_key!(i8); -impl_from_str_from_redis_key!(i16); -impl_from_str_from_redis_key!(i32); -impl_from_str_from_redis_key!(i64); -impl_from_str_from_redis_key!(i128); -impl_from_str_from_redis_key!(isize); -impl_from_str_from_redis_key!(f32); -impl_from_str_from_redis_key!(f64); - -impl FromRedisKey for () { - fn from_key(_: RedisKey) -> Result { - Ok(()) - } -} - -impl FromRedisKey for RedisValue { - fn from_key(value: RedisKey) -> Result { - Ok(RedisValue::Bytes(value.into_bytes())) - } -} - -impl FromRedisKey for RedisKey { - fn from_key(value: RedisKey) -> Result { - Ok(value) - } -} - -impl FromRedisKey for String { - fn from_key(value: RedisKey) -> Result { - value - .into_string() - .ok_or(RedisError::new_parse("Cannot parse key as string.")) - } -} - -impl FromRedisKey for Str { - fn from_key(value: RedisKey) -> Result { - Ok(Str::from_inner(value.into_bytes())?) - } -} - -impl FromRedisKey for Vec { - fn from_key(value: RedisKey) -> Result { - Ok(value.into_bytes().to_vec()) - } -} - -impl FromRedisKey for Bytes { - fn from_key(value: RedisKey) -> Result { - Ok(value.into_bytes()) - } -} - -#[cfg(test)] -mod tests { - use crate::types::RedisValue; - use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; - - #[cfg(not(feature = "default-nil-types"))] - use crate::error::RedisError; - - #[test] - fn should_convert_signed_numeric_types() { - let _foo: i8 = RedisValue::String("123".into()).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: i8 = RedisValue::Integer(123).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: i16 = RedisValue::String("123".into()).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: i16 = RedisValue::Integer(123).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: i32 = RedisValue::String("123".into()).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: i32 = RedisValue::Integer(123).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: i64 = RedisValue::String("123".into()).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: i64 = RedisValue::Integer(123).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: i128 = RedisValue::String("123".into()).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: i128 = RedisValue::Integer(123).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: isize = RedisValue::String("123".into()).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: isize = RedisValue::Integer(123).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: f32 = RedisValue::String("123.5".into()).convert().unwrap(); - assert_eq!(_foo, 123.5); - let _foo: f64 = RedisValue::String("123.5".into()).convert().unwrap(); - assert_eq!(_foo, 123.5); - } - - #[test] - fn should_convert_unsigned_numeric_types() { - let _foo: u8 = RedisValue::String("123".into()).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: u8 = RedisValue::Integer(123).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: u16 = RedisValue::String("123".into()).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: u16 = RedisValue::Integer(123).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: u32 = RedisValue::String("123".into()).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: u32 = RedisValue::Integer(123).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: u64 = RedisValue::String("123".into()).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: u64 = RedisValue::Integer(123).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: u128 = RedisValue::String("123".into()).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: u128 = RedisValue::Integer(123).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: usize = RedisValue::String("123".into()).convert().unwrap(); - assert_eq!(_foo, 123); - let _foo: usize = RedisValue::Integer(123).convert().unwrap(); - assert_eq!(_foo, 123); - } - - #[test] - #[cfg(not(feature = "default-nil-types"))] - fn should_return_not_found_with_null_number_types() { - let result: Result = RedisValue::Null.convert(); - assert!(result.unwrap_err().is_not_found()); - let result: Result = RedisValue::Null.convert(); - assert!(result.unwrap_err().is_not_found()); - let result: Result = RedisValue::Null.convert(); - assert!(result.unwrap_err().is_not_found()); - let result: Result = RedisValue::Null.convert(); - assert!(result.unwrap_err().is_not_found()); - let result: Result = RedisValue::Null.convert(); - assert!(result.unwrap_err().is_not_found()); - let result: Result = RedisValue::Null.convert(); - assert!(result.unwrap_err().is_not_found()); - let result: Result = RedisValue::Null.convert(); - assert!(result.unwrap_err().is_not_found()); - let result: Result = RedisValue::Null.convert(); - assert!(result.unwrap_err().is_not_found()); - let result: Result = RedisValue::Null.convert(); - assert!(result.unwrap_err().is_not_found()); - let result: Result = RedisValue::Null.convert(); - assert!(result.unwrap_err().is_not_found()); - let result: Result = RedisValue::Null.convert(); - assert!(result.unwrap_err().is_not_found()); - let result: Result = RedisValue::Null.convert(); - assert!(result.unwrap_err().is_not_found()); - } - - #[test] - #[cfg(feature = "default-nil-types")] - fn should_return_zero_with_null_number_types() { - assert_eq!(0, RedisValue::Null.convert::().unwrap()); - assert_eq!(0, RedisValue::Null.convert::().unwrap()); - assert_eq!(0, RedisValue::Null.convert::().unwrap()); - assert_eq!(0, RedisValue::Null.convert::().unwrap()); - assert_eq!(0, RedisValue::Null.convert::().unwrap()); - assert_eq!(0, RedisValue::Null.convert::().unwrap()); - assert_eq!(0, RedisValue::Null.convert::().unwrap()); - assert_eq!(0, RedisValue::Null.convert::().unwrap()); - assert_eq!(0, RedisValue::Null.convert::().unwrap()); - assert_eq!(0, RedisValue::Null.convert::().unwrap()); - assert_eq!(0, RedisValue::Null.convert::().unwrap()); - assert_eq!(0, RedisValue::Null.convert::().unwrap()); - assert_eq!(0.0, RedisValue::Null.convert::().unwrap()); - assert_eq!(0.0, RedisValue::Null.convert::().unwrap()); - } - - #[test] - #[cfg(feature = "default-nil-types")] - fn should_convert_null_to_false() { - assert!(!RedisValue::Null.convert::().unwrap()); - } - - #[test] - #[should_panic] - #[cfg(not(feature = "default-nil-types"))] - fn should_not_convert_null_to_false() { - assert!(!RedisValue::Null.convert::().unwrap()); - } - - #[test] - fn should_convert_strings() { - let _foo: String = RedisValue::String("foo".into()).convert().unwrap(); - assert_eq!(_foo, "foo".to_owned()); - } - - #[test] - fn should_convert_numbers_to_bools() { - let foo: bool = RedisValue::Integer(0).convert().unwrap(); - assert!(!foo); - let foo: bool = RedisValue::Integer(1).convert().unwrap(); - assert!(foo); - let foo: bool = RedisValue::String("0".into()).convert().unwrap(); - assert!(!foo); - let foo: bool = RedisValue::String("1".into()).convert().unwrap(); - assert!(foo); - } - - #[test] - fn should_convert_bytes() { - let foo: Vec = RedisValue::Bytes("foo".as_bytes().to_vec().into()).convert().unwrap(); - assert_eq!(foo, "foo".as_bytes().to_vec()); - let foo: Vec = RedisValue::String("foo".into()).convert().unwrap(); - assert_eq!(foo, "foo".as_bytes().to_vec()); - let foo: Vec = RedisValue::Array(vec![102.into(), 111.into(), 111.into()]) - .convert() - .unwrap(); - assert_eq!(foo, "foo".as_bytes().to_vec()); - } - - #[test] - fn should_convert_arrays() { - let foo: Vec = RedisValue::Array(vec!["a".into(), "b".into()]).convert().unwrap(); - assert_eq!(foo, vec!["a".to_owned(), "b".to_owned()]); - } - - #[test] - fn should_convert_hash_maps() { - let foo: HashMap = RedisValue::Array(vec!["a".into(), 1.into(), "b".into(), 2.into()]) - .convert() - .unwrap(); - - let mut expected = HashMap::new(); - expected.insert("a".to_owned(), 1); - expected.insert("b".to_owned(), 2); - assert_eq!(foo, expected); - } - - #[test] - fn should_convert_hash_sets() { - let foo: HashSet = RedisValue::Array(vec!["a".into(), "b".into()]).convert().unwrap(); - - let mut expected = HashSet::new(); - expected.insert("a".to_owned()); - expected.insert("b".to_owned()); - assert_eq!(foo, expected); - } - - #[test] - fn should_convert_btree_maps() { - let foo: BTreeMap = RedisValue::Array(vec!["a".into(), 1.into(), "b".into(), 2.into()]) - .convert() - .unwrap(); - - let mut expected = BTreeMap::new(); - expected.insert("a".to_owned(), 1); - expected.insert("b".to_owned(), 2); - assert_eq!(foo, expected); - } - - #[test] - fn should_convert_btree_sets() { - let foo: BTreeSet = RedisValue::Array(vec!["a".into(), "b".into()]).convert().unwrap(); - - let mut expected = BTreeSet::new(); - expected.insert("a".to_owned()); - expected.insert("b".to_owned()); - assert_eq!(foo, expected); - } - - #[test] - fn should_convert_tuples() { - let foo: (String, i64) = RedisValue::Array(vec!["a".into(), 1.into()]).convert().unwrap(); - assert_eq!(foo, ("a".to_owned(), 1)); - } - - #[test] - fn should_convert_array_tuples() { - let foo: Vec<(String, i64)> = RedisValue::Array(vec!["a".into(), 1.into(), "b".into(), 2.into()]) - .convert() - .unwrap(); - assert_eq!(foo, vec![("a".to_owned(), 1), ("b".to_owned(), 2)]); - } - - #[test] - fn should_handle_single_element_vector_to_scalar() { - assert!(RedisValue::Array(vec![]).convert::().is_err()); - assert_eq!( - RedisValue::Array(vec!["foo".into()]).convert::(), - Ok("foo".into()) - ); - assert!(RedisValue::Array(vec!["foo".into(), "bar".into()]) - .convert::() - .is_err()); - - assert_eq!(RedisValue::Array(vec![]).convert::>(), Ok(None)); - assert_eq!( - RedisValue::Array(vec!["foo".into()]).convert::>(), - Ok(Some("foo".into())) - ); - assert!(RedisValue::Array(vec!["foo".into(), "bar".into()]) - .convert::>() - .is_err()); - } - - #[test] - fn should_convert_null_to_empty_array() { - assert_eq!(Vec::::new(), RedisValue::Null.convert::>().unwrap()); - assert_eq!(Vec::::new(), RedisValue::Null.convert::>().unwrap()); - } - - #[test] - fn should_convert_to_fixed_arrays() { - let foo: [i64; 2] = RedisValue::Array(vec![1.into(), 2.into()]).convert().unwrap(); - assert_eq!(foo, [1, 2]); - - assert!(RedisValue::Array(vec![1.into(), 2.into()]) - .convert::<[i64; 3]>() - .is_err()); - assert!(RedisValue::Array(vec![]).convert::<[i64; 3]>().is_err()); - } -} diff --git a/src/monitor/mod.rs b/src/monitor/mod.rs deleted file mode 100644 index 4b1adf38..00000000 --- a/src/monitor/mod.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::{ - error::RedisError, - types::{RedisConfig, RedisValue}, - utils as client_utils, -}; -use futures::Stream; -use std::fmt; - -mod parser; -mod utils; - -/// A command parsed from a [MONITOR](https://redis.io/commands/monitor) stream. -/// -/// Formatting with the [Display](https://doc.rust-lang.org/std/fmt/trait.Display.html) trait will print the same output as `redis-cli`. -#[derive(Clone, Debug)] -pub struct Command { - /// The command run by the server. - pub command: String, - /// Arguments passed to the command. - pub args: Vec, - /// When the command was run on the server. - pub timestamp: f64, - /// The database against which the command was run. - pub db: u8, - /// The host and port of the client that ran the command, or `lua` when run from a script. - pub client: String, -} - -impl PartialEq for Command { - fn eq(&self, other: &Self) -> bool { - client_utils::f64_eq(self.timestamp, other.timestamp) - && self.client == other.client - && self.db == other.db - && self.command == other.command - && self.args == other.args - } -} - -impl Eq for Command {} - -impl fmt::Display for Command { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{:.6} [{} {}] \"{}\"", - self.timestamp, self.db, self.client, self.command - )?; - - for arg in self.args.iter() { - write!(f, " \"{}\"", arg.as_str().unwrap_or("unknown".into()))?; - } - - Ok(()) - } -} - -/// Run the [MONITOR](https://redis.io/commands/monitor) command against the provided server. -/// -/// Currently only centralized configurations are supported. -pub async fn run(config: RedisConfig) -> Result, RedisError> { - utils::start(config).await -} diff --git a/src/monitor/parser.rs b/src/monitor/parser.rs deleted file mode 100644 index f7021527..00000000 --- a/src/monitor/parser.rs +++ /dev/null @@ -1,208 +0,0 @@ -use crate::{modules::inner::RedisClientInner, monitor::Command, runtime::RefCount, types::RedisValue}; -use nom::{ - bytes::complete::{escaped as nom_escaped, tag as nom_tag, take as nom_take, take_until as nom_take_until}, - character::complete::none_of as nom_none_of, - combinator::{map_res as nom_map_res, opt as nom_opt}, - multi::many0 as nom_many0, - sequence::{delimited as nom_delimited, preceded as nom_preceded, terminated as nom_terminated}, - IResult, -}; -use redis_protocol::{ - error::RedisParseError, - resp3::types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame}, -}; -use std::str; - -const EMPTY_SPACE: &str = " "; -const RIGHT_BRACKET: &str = "]"; -const QUOTE: &str = "\""; - -fn to_f64(s: &str) -> Result> { - s.parse::() - .map_err(|e| RedisParseError::new_custom("to_f64", format!("{:?}", e))) -} - -fn to_u8(s: &str) -> Result> { - s.parse::() - .map_err(|e| RedisParseError::new_custom("to_u8", format!("{:?}", e))) -} - -fn to_redis_value(s: &[u8]) -> Result> { - // TODO make this smarter in the future - if let Ok(value) = str::from_utf8(s) { - Ok(RedisValue::String(value.into())) - } else { - Ok(RedisValue::Bytes(s.to_vec().into())) - } -} - -fn to_str(input: &[u8]) -> Result<&str, RedisParseError<&[u8]>> { - str::from_utf8(input).map_err(|e| RedisParseError::new_custom("to_str", format!("{:?}", e))) -} - -fn d_parse_timestamp(input: &[u8]) -> IResult<&[u8], f64, RedisParseError<&[u8]>> { - nom_map_res( - nom_map_res(nom_terminated(nom_take_until(EMPTY_SPACE), nom_take(1_usize)), to_str), - to_f64, - )(input) -} - -fn d_parse_db(input: &[u8]) -> IResult<&[u8], u8, RedisParseError<&[u8]>> { - nom_map_res( - nom_map_res( - nom_preceded( - nom_take(1_usize), - nom_terminated(nom_take_until(EMPTY_SPACE), nom_take(1_usize)), - ), - to_str, - ), - to_u8, - )(input) -} - -fn d_parse_client(input: &[u8]) -> IResult<&[u8], String, RedisParseError<&[u8]>> { - let (input, client) = nom_map_res(nom_terminated(nom_take_until(RIGHT_BRACKET), nom_take(2_usize)), to_str)(input)?; - Ok((input, client.to_owned())) -} - -fn d_parse_command(input: &[u8]) -> IResult<&[u8], String, RedisParseError<&[u8]>> { - let (input, command) = nom_map_res( - nom_terminated( - nom_delimited(nom_tag(QUOTE), nom_take_until(QUOTE), nom_tag(QUOTE)), - // args are optional after the command string, including the empty space separating the command and args - nom_opt(nom_take(1_usize)), - ), - to_str, - )(input)?; - - Ok((input, command.to_owned())) -} - -fn d_parse_arg(input: &[u8]) -> IResult<&[u8], RedisValue, RedisParseError<&[u8]>> { - let escaped_parser = nom_escaped(nom_none_of("\\\""), '\\', nom_tag(QUOTE)); - nom_map_res( - nom_terminated( - nom_delimited(nom_tag(QUOTE), escaped_parser, nom_tag(QUOTE)), - nom_opt(nom_take(1_usize)), - ), - to_redis_value, - )(input) -} - -fn d_parse_args(input: &[u8]) -> IResult<&[u8], Vec, RedisParseError<&[u8]>> { - nom_many0(d_parse_arg)(input) -} - -fn d_parse_frame(input: &[u8]) -> Result> { - let (input, timestamp) = d_parse_timestamp(input)?; - let (input, db) = d_parse_db(input)?; - let (input, client) = d_parse_client(input)?; - let (input, command) = d_parse_command(input)?; - let (_, args) = d_parse_args(input)?; - - Ok(Command { - timestamp, - db, - client, - command, - args, - }) -} - -#[cfg(feature = "network-logs")] -fn log_frame(inner: &RefCount, frame: &[u8]) { - if let Ok(s) = str::from_utf8(frame) { - _trace!(inner, "Monitor frame: {}", s); - } else { - _trace!(inner, "Monitor frame: {:?}", frame); - } -} - -#[cfg(not(feature = "network-logs"))] -fn log_frame(_: &RefCount, _: &[u8]) {} - -pub fn parse(inner: &RefCount, frame: Resp3Frame) -> Option { - let frame_bytes = match frame { - Resp3Frame::SimpleString { ref data, .. } => data, - Resp3Frame::BlobString { ref data, .. } => data, - Resp3Frame::VerbatimString { ref data, .. } => data, - _ => { - _warn!(inner, "Unexpected frame type on monitor stream: {:?}", frame.kind()); - return None; - }, - }; - - log_frame(inner, frame_bytes); - d_parse_frame(frame_bytes).ok() -} - -#[cfg(test)] -mod tests { - use crate::monitor::{parser::d_parse_frame, Command}; - - #[test] - fn should_parse_frame_without_spaces_or_quotes() { - let input = "1631469940.785623 [0 127.0.0.1:46998] \"SET\" \"foo\" \"2\""; - let expected = Command { - timestamp: 1631469940.785623, - db: 0, - client: "127.0.0.1:46998".into(), - command: "SET".into(), - args: vec!["foo".into(), "2".into()], - }; - - let actual = d_parse_frame(input.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_frame_with_inner_spaces() { - let input = "1631469940.785623 [0 127.0.0.1:46998] \"SET\" \"foo bar\" \"2\""; - let expected = Command { - timestamp: 1631469940.785623, - db: 0, - client: "127.0.0.1:46998".into(), - command: "SET".into(), - args: vec!["foo bar".into(), "2".into()], - }; - - let actual = d_parse_frame(input.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_frame_with_inner_quotes() { - let input = "1631475365.563304 [0 127.0.0.1:47438] \"SET\" \"foo\" \"0 - \\\"abc\\\"\" \"1 - \\\"def\\\"\" \"2 \ - - \\\"ghi\\\" \\\"jkl\\\"\""; - let expected = Command { - timestamp: 1631475365.563304, - db: 0, - client: "127.0.0.1:47438".into(), - command: "SET".into(), - args: vec![ - "foo".into(), - "0 - \\\"abc\\\"".into(), - "1 - \\\"def\\\"".into(), - "2 - \\\"ghi\\\" \\\"jkl\\\"".into(), - ], - }; - - let actual = d_parse_frame(input.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_frame_without_args() { - let input = "1631469940.785623 [0 127.0.0.1:46998] \"KEYS\""; - let expected = Command { - timestamp: 1631469940.785623, - db: 0, - client: "127.0.0.1:46998".into(), - command: "KEYS".into(), - args: vec![], - }; - - let actual = d_parse_frame(input.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } -} diff --git a/src/monitor/utils.rs b/src/monitor/utils.rs deleted file mode 100644 index 9dc5a8db..00000000 --- a/src/monitor/utils.rs +++ /dev/null @@ -1,159 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - modules::inner::RedisClientInner, - monitor::{parser, Command}, - protocol::{ - codec::RedisCodec, - command::{RedisCommand, RedisCommandKind}, - connection::{self, ConnectionKind, RedisTransport}, - types::ProtocolFrame, - utils as protocol_utils, - }, - runtime::{rx_stream, spawn, unbounded_channel, RefCount, UnboundedSender}, - types::{ConnectionConfig, PerformanceConfig, RedisConfig, ServerConfig}, -}; -use futures::stream::{Stream, StreamExt}; -use tokio::io::{AsyncRead, AsyncWrite}; -use tokio_util::codec::Framed; - -#[cfg(not(feature = "glommio"))] -use tokio_stream::wrappers::UnboundedReceiverStream; - -#[cfg(all(feature = "blocking-encoding", not(feature = "glommio")))] -async fn handle_monitor_frame( - inner: &RefCount, - frame: Result, -) -> Option { - let frame = match frame { - Ok(frame) => frame.into_resp3(), - Err(e) => { - _error!(inner, "Error on monitor stream: {:?}", e); - return None; - }, - }; - let frame_size = frame.encode_len(); - - if frame_size >= inner.with_perf_config(|c| c.blocking_encode_threshold) { - // since this isn't called from the Encoder/Decoder trait we can use spawn_blocking here - _trace!( - inner, - "Parsing monitor frame with blocking task with size {}", - frame_size - ); - - let inner = inner.clone(); - tokio::task::spawn_blocking(move || parser::parse(&inner, frame)) - .await - .ok() - .flatten() - } else { - parser::parse(inner, frame) - } -} - -#[cfg(any(not(feature = "blocking-encoding"), feature = "glommio"))] -async fn handle_monitor_frame( - inner: &RefCount, - frame: Result, -) -> Option { - let frame = match frame { - Ok(frame) => frame.into_resp3(), - Err(e) => { - _error!(inner, "Error on monitor stream: {:?}", e); - return None; - }, - }; - - parser::parse(inner, frame) -} - -async fn send_monitor_command( - inner: &RefCount, - mut connection: RedisTransport, -) -> Result { - _debug!(inner, "Sending MONITOR command."); - - let command = RedisCommand::new(RedisCommandKind::Monitor, vec![]); - let frame = connection.request_response(command, inner.is_resp3()).await?; - - _trace!(inner, "Recv MONITOR response: {:?}", frame); - let response = protocol_utils::frame_to_results(frame)?; - protocol_utils::expect_ok(&response)?; - Ok(connection) -} - -async fn forward_results( - inner: &RefCount, - tx: UnboundedSender, - mut framed: Framed, -) where - T: AsyncRead + AsyncWrite + Unpin + 'static, -{ - while let Some(frame) = framed.next().await { - if let Some(command) = handle_monitor_frame(inner, frame).await { - if let Err(_) = tx.send(command) { - _warn!(inner, "Stopping monitor stream."); - return; - } - } else { - _debug!(inner, "Skipping invalid monitor frame."); - } - } -} - -async fn process_stream( - inner: &RefCount, - tx: UnboundedSender, - connection: RedisTransport, -) { - _debug!(inner, "Starting monitor stream processing..."); - - match connection.transport { - ConnectionKind::Tcp(framed) => forward_results(inner, tx, framed).await, - #[cfg(feature = "enable-rustls")] - ConnectionKind::Rustls(framed) => forward_results(inner, tx, framed).await, - #[cfg(feature = "enable-native-tls")] - ConnectionKind::NativeTls(framed) => forward_results(inner, tx, framed).await, - #[cfg(feature = "unix-sockets")] - ConnectionKind::Unix(framed) => forward_results(inner, tx, framed).await, - }; - - _warn!(inner, "Stopping monitor stream."); -} - -pub async fn start(config: RedisConfig) -> Result, RedisError> { - let perf = PerformanceConfig { - auto_pipeline: false, - ..Default::default() - }; - let connection = ConnectionConfig::default(); - let server = match config.server { - ServerConfig::Centralized { ref server } => server.clone(), - _ => { - return Err(RedisError::new( - RedisErrorKind::Config, - "Expected centralized server config.", - )) - }, - }; - - let inner = RedisClientInner::new(config, perf, connection, None); - let mut connection = connection::create(&inner, &server, None).await?; - connection.setup(&inner, None).await?; - let connection = send_monitor_command(&inner, connection).await?; - - // there isn't really a mechanism to surface backpressure to the server for the MONITOR stream, so we use a - // background task with a channel to process the frames so that the server can keep sending data even if the - // stream consumer slows down processing the frames. - let (tx, rx) = unbounded_channel(); - #[cfg(feature = "glommio")] - let tx = tx.into(); - spawn(async move { - process_stream(&inner, tx, connection).await; - }); - - #[cfg(feature = "glommio")] - return Ok(rx_stream(rx)); - #[cfg(not(feature = "glommio"))] - return Ok(UnboundedReceiverStream::new(rx)); -} diff --git a/src/protocol/cluster.rs b/src/protocol/cluster.rs deleted file mode 100644 index 6fcf2679..00000000 --- a/src/protocol/cluster.rs +++ /dev/null @@ -1,1053 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - modules::inner::RedisClientInner, - protocol::types::{Server, SlotRange}, - runtime::RefCount, - types::RedisValue, - utils, -}; -use bytes_utils::Str; -use std::{collections::HashMap, net::IpAddr, str::FromStr}; - -#[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" -))] -use crate::protocol::tls::TlsHostMapping; - -fn parse_as_u16(value: RedisValue) -> Result { - match value { - RedisValue::Integer(i) => { - if i < 0 || i > u16::MAX as i64 { - Err(RedisError::new(RedisErrorKind::Parse, "Invalid cluster slot integer.")) - } else { - Ok(i as u16) - } - }, - RedisValue::String(s) => s.parse::().map_err(|e| e.into()), - _ => Err(RedisError::new( - RedisErrorKind::Parse, - "Could not parse value as cluster slot.", - )), - } -} - -fn is_ip_address(value: &Str) -> bool { - IpAddr::from_str(value).is_ok() -} - -fn check_metadata_hostname(data: &HashMap) -> Option<&Str> { - data - .get(&utils::static_str("hostname")) - .filter(|&hostname| !hostname.is_empty()) -} - -/// Find the correct hostname for the server, preferring hostnames over IP addresses for TLS purposes. -/// -/// The format of `server` is `[|null, , , ]`. However, in Redis <=6 the -/// `metadata` value will not be present. -/// -/// The implementation here does the following: -/// 1. If `server[0]` is a hostname then use that. -/// 2. If `server[0]` is an IP address, then check `server[3]` for a "hostname" metadata field and use that if found. -/// Otherwise use the IP address in `server[0]`. -/// 3. If `server[0]` is null, but `server[3]` has a "hostname" metadata field, then use the metadata field. Otherwise -/// use `default_host`. -/// -/// The `default_host` is the host that returned the `CLUSTER SLOTS` response. -/// -/// -fn parse_cluster_slot_hostname(server: &[RedisValue], default_host: &Str) -> Result { - if server.is_empty() { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Invalid CLUSTER SLOTS server block.", - )); - } - let should_parse_metadata = server.len() >= 4 && !server[3].is_null() && server[3].array_len().unwrap_or(0) > 0; - - let metadata: HashMap = if should_parse_metadata { - // all the variants with data on the heap are ref counted (`Bytes`, `Str`, etc) - server[3].clone().convert()? - } else { - HashMap::new() - }; - if server[0].is_null() { - // option 3 - Ok(check_metadata_hostname(&metadata).unwrap_or(default_host).clone()) - } else { - let preferred_host = match server[0].clone().convert::() { - Ok(host) => host, - Err(_) => { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Invalid CLUSTER SLOTS server block hostname.", - )) - }, - }; - - if is_ip_address(&preferred_host) { - // option 2 - Ok(check_metadata_hostname(&metadata).unwrap_or(&preferred_host).clone()) - } else { - // option 1 - Ok(preferred_host) - } - } -} - -/// Read the node block with format `|null, , , [metadata]` -fn parse_node_block(data: &[RedisValue], default_host: &Str) -> Option<(Str, u16, Str, Str)> { - if data.len() < 3 { - return None; - } - - let hostname = match parse_cluster_slot_hostname(data, default_host) { - Ok(host) => host, - Err(_) => return None, - }; - let port: u16 = match parse_as_u16(data[1].clone()) { - Ok(port) => port, - Err(_) => return None, - }; - let primary = Str::from(format!("{}:{}", hostname, port)); - let id = data[2].as_bytes_str()?; - - Some((hostname, port, primary, id)) -} - -/// Parse the optional trailing replica nodes in each `CLUSTER SLOTS` slot range block. -#[cfg(feature = "replicas")] -fn parse_cluster_slot_replica_nodes(slot_range: Vec, default_host: &Str) -> Vec { - slot_range - .into_iter() - .filter_map(|value| { - let server_block: Vec = match value.convert() { - Ok(v) => v, - Err(_) => { - warn!("Skip replica CLUSTER SLOTS block from {}", default_host); - return None; - }, - }; - - let (host, port) = match parse_node_block(&server_block, default_host) { - Some((h, p, _, _)) => (h, p), - None => { - warn!("Skip replica CLUSTER SLOTS block from {}", default_host); - return None; - }, - }; - - Some(Server { - host, - port, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }) - }) - .collect() -} - -/// Parse the cluster slot range and associated server blocks. -fn parse_cluster_slot_nodes(mut slot_range: Vec, default_host: &Str) -> Result { - if slot_range.len() < 3 { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Invalid CLUSTER SLOTS response.", - )); - } - slot_range.reverse(); - // length checked above - let start = parse_as_u16(slot_range.pop().unwrap())?; - let end = parse_as_u16(slot_range.pop().unwrap())?; - - // the third value is the primary node, following values are optional replica nodes - // length checked above. format is `|null, , , [metadata]` - let server_block: Vec = slot_range.pop().unwrap().convert()?; - let (host, port, id) = match parse_node_block(&server_block, default_host) { - Some((h, p, _, i)) => (h, p, i), - None => { - trace!("Failed to parse CLUSTER SLOTS response: {:?}", server_block); - return Err(RedisError::new( - RedisErrorKind::Cluster, - "Invalid CLUSTER SLOTS response.", - )); - }, - }; - - Ok(SlotRange { - start, - end, - id, - primary: Server { - host, - port, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - #[cfg(feature = "replicas")] - replicas: parse_cluster_slot_replica_nodes(slot_range, default_host), - }) -} - -/// Parse the entire CLUSTER SLOTS response with the provided `default_host` of the connection used to send the -/// command. -pub fn parse_cluster_slots(frame: RedisValue, default_host: &Str) -> Result, RedisError> { - let slot_ranges: Vec> = frame.convert()?; - let mut out: Vec = Vec::with_capacity(slot_ranges.len()); - - for slot_range in slot_ranges.into_iter() { - out.push(parse_cluster_slot_nodes(slot_range, default_host)?); - } - - out.shrink_to_fit(); - Ok(out) -} - -#[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" -))] -fn replace_tls_server_names(policy: &TlsHostMapping, ranges: &mut [SlotRange], default_host: &Str) { - for slot_range in ranges.iter_mut() { - slot_range.primary.set_tls_server_name(policy, default_host); - - #[cfg(feature = "replicas")] - for server in slot_range.replicas.iter_mut() { - server.set_tls_server_name(policy, default_host); - } - } -} - -/// Modify the `CLUSTER SLOTS` command according to the hostname mapping policy in the `TlsHostMapping`. -#[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" -))] -pub fn modify_cluster_slot_hostnames( - inner: &RefCount, - ranges: &mut [SlotRange], - default_host: &Str, -) { - let policy = match inner.config.tls { - Some(ref config) => &config.hostnames, - None => { - _trace!(inner, "Skip modifying TLS hostnames."); - return; - }, - }; - if *policy == TlsHostMapping::None { - _trace!(inner, "Skip modifying TLS hostnames."); - return; - } - - replace_tls_server_names(policy, ranges, default_host); -} - -#[cfg(not(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" -)))] -pub fn modify_cluster_slot_hostnames(inner: &RefCount, _: &mut Vec, _: &Str) { - _trace!(inner, "Skip modifying TLS hostnames.") -} - -#[cfg(test)] -mod tests { - use super::*; - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - use crate::protocol::tls::{HostMapping, TlsHostMapping}; - use crate::protocol::types::SlotRange; - - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - #[derive(Debug)] - struct FakeHostMapper; - - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - impl HostMapping for FakeHostMapper { - fn map(&self, _: &IpAddr, _: &str) -> Option { - Some("foobarbaz".into()) - } - } - - fn fake_cluster_slots_without_metadata() -> RedisValue { - let first_slot_range = RedisValue::Array(vec![ - 0.into(), - 5460.into(), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30001.into(), - "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(), - ]), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30004.into(), - "821d8ca00d7ccf931ed3ffc7e3db0599d2271abf".into(), - ]), - ]); - let second_slot_range = RedisValue::Array(vec![ - 5461.into(), - 10922.into(), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30002.into(), - "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(), - ]), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30005.into(), - "faadb3eb99009de4ab72ad6b6ed87634c7ee410f".into(), - ]), - ]); - let third_slot_range = RedisValue::Array(vec![ - 10923.into(), - 16383.into(), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30003.into(), - "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(), - ]), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30006.into(), - "58e6e48d41228013e5d9c1c37c5060693925e97e".into(), - ]), - ]); - - RedisValue::Array(vec![first_slot_range, second_slot_range, third_slot_range]) - } - - fn fake_cluster_slots_with_metadata() -> RedisValue { - let first_slot_range = RedisValue::Array(vec![ - 0.into(), - 5460.into(), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30001.into(), - "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(), - RedisValue::Array(vec!["hostname".into(), "host-1.redis.example.com".into()]), - ]), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30004.into(), - "821d8ca00d7ccf931ed3ffc7e3db0599d2271abf".into(), - RedisValue::Array(vec!["hostname".into(), "host-2.redis.example.com".into()]), - ]), - ]); - let second_slot_range = RedisValue::Array(vec![ - 5461.into(), - 10922.into(), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30002.into(), - "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(), - RedisValue::Array(vec!["hostname".into(), "host-3.redis.example.com".into()]), - ]), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30005.into(), - "faadb3eb99009de4ab72ad6b6ed87634c7ee410f".into(), - RedisValue::Array(vec!["hostname".into(), "host-4.redis.example.com".into()]), - ]), - ]); - let third_slot_range = RedisValue::Array(vec![ - 10923.into(), - 16383.into(), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30003.into(), - "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(), - RedisValue::Array(vec!["hostname".into(), "host-5.redis.example.com".into()]), - ]), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30006.into(), - "58e6e48d41228013e5d9c1c37c5060693925e97e".into(), - RedisValue::Array(vec!["hostname".into(), "host-6.redis.example.com".into()]), - ]), - ]); - - RedisValue::Array(vec![first_slot_range, second_slot_range, third_slot_range]) - } - - #[test] - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - fn should_modify_cluster_slot_hostnames_default_host_without_metadata() { - let policy = TlsHostMapping::DefaultHost; - let fake_data = fake_cluster_slots_without_metadata(); - let mut ranges = parse_cluster_slots(fake_data, &Str::from("default-host")).unwrap(); - replace_tls_server_names(&policy, &mut ranges, &Str::from("default-host")); - - for slot_range in ranges.iter() { - assert_ne!(slot_range.primary.host, "default-host"); - assert_eq!(slot_range.primary.tls_server_name, Some("default-host".into())); - - #[cfg(feature = "replicas")] - for replica in slot_range.replicas.iter() { - assert_ne!(replica.host, "default-host"); - assert_eq!(replica.tls_server_name, Some("default-host".into())); - } - } - } - - #[test] - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - fn should_not_modify_cluster_slot_hostnames_default_host_with_metadata() { - let policy = TlsHostMapping::DefaultHost; - let fake_data = fake_cluster_slots_with_metadata(); - let mut ranges = parse_cluster_slots(fake_data, &Str::from("default-host")).unwrap(); - replace_tls_server_names(&policy, &mut ranges, &Str::from("default-host")); - - for slot_range in ranges.iter() { - assert_ne!(slot_range.primary.host, "default-host"); - // since there's a metadata hostname then expect that instead of the default host - assert_ne!(slot_range.primary.tls_server_name, Some("default-host".into())); - - #[cfg(feature = "replicas")] - for replica in slot_range.replicas.iter() { - assert_ne!(replica.host, "default-host"); - assert_ne!(replica.tls_server_name, Some("default-host".into())); - } - } - } - - #[test] - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - fn should_modify_cluster_slot_hostnames_custom() { - let policy = TlsHostMapping::Custom(RefCount::new(FakeHostMapper)); - let fake_data = fake_cluster_slots_without_metadata(); - let mut ranges = parse_cluster_slots(fake_data, &Str::from("default-host")).unwrap(); - replace_tls_server_names(&policy, &mut ranges, &Str::from("default-host")); - - for slot_range in ranges.iter() { - assert_ne!(slot_range.primary.host, "default-host"); - assert_eq!(slot_range.primary.tls_server_name, Some("foobarbaz".into())); - - #[cfg(feature = "replicas")] - for replica in slot_range.replicas.iter() { - assert_ne!(replica.host, "default-host"); - assert_eq!(replica.tls_server_name, Some("foobarbaz".into())); - } - } - } - - #[test] - fn should_parse_cluster_slots_example_metadata_hostnames() { - let input = fake_cluster_slots_with_metadata(); - - let actual = parse_cluster_slots(input, &Str::from("bad-host")).expect("Failed to parse input"); - let expected = vec![ - SlotRange { - start: 0, - end: 5460, - primary: Server { - host: "host-1.redis.example.com".into(), - port: 30001, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "host-2.redis.example.com".into(), - port: 30004, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - SlotRange { - start: 5461, - end: 10922, - primary: Server { - host: "host-3.redis.example.com".into(), - port: 30002, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "host-4.redis.example.com".into(), - port: 30005, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - SlotRange { - start: 10923, - end: 16383, - primary: Server { - host: "host-5.redis.example.com".into(), - port: 30003, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "host-6.redis.example.com".into(), - port: 30006, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - ]; - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_cluster_slots_example_no_metadata() { - let input = fake_cluster_slots_without_metadata(); - - let actual = parse_cluster_slots(input, &Str::from("bad-host")).expect("Failed to parse input"); - let expected = vec![ - SlotRange { - start: 0, - end: 5460, - primary: Server { - host: "127.0.0.1".into(), - port: 30001, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "127.0.0.1".into(), - port: 30004, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - SlotRange { - start: 5461, - end: 10922, - primary: Server { - host: "127.0.0.1".into(), - port: 30002, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "127.0.0.1".into(), - port: 30005, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - SlotRange { - start: 10923, - end: 16383, - primary: Server { - host: "127.0.0.1".into(), - port: 30003, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "127.0.0.1".into(), - port: 30006, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - ]; - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_cluster_slots_example_empty_metadata() { - let first_slot_range = RedisValue::Array(vec![ - 0.into(), - 5460.into(), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30001.into(), - "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(), - RedisValue::Array(vec![]), - ]), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30004.into(), - "821d8ca00d7ccf931ed3ffc7e3db0599d2271abf".into(), - RedisValue::Array(vec![]), - ]), - ]); - let second_slot_range = RedisValue::Array(vec![ - 5461.into(), - 10922.into(), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30002.into(), - "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(), - RedisValue::Array(vec![]), - ]), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30005.into(), - "faadb3eb99009de4ab72ad6b6ed87634c7ee410f".into(), - RedisValue::Array(vec![]), - ]), - ]); - let third_slot_range = RedisValue::Array(vec![ - 10923.into(), - 16383.into(), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30003.into(), - "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(), - RedisValue::Array(vec![]), - ]), - RedisValue::Array(vec![ - "127.0.0.1".into(), - 30006.into(), - "58e6e48d41228013e5d9c1c37c5060693925e97e".into(), - RedisValue::Array(vec![]), - ]), - ]); - let input = RedisValue::Array(vec![first_slot_range, second_slot_range, third_slot_range]); - - let actual = parse_cluster_slots(input, &Str::from("bad-host")).expect("Failed to parse input"); - let expected = vec![ - SlotRange { - start: 0, - end: 5460, - primary: Server { - host: "127.0.0.1".into(), - port: 30001, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "127.0.0.1".into(), - port: 30004, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - SlotRange { - start: 5461, - end: 10922, - primary: Server { - host: "127.0.0.1".into(), - port: 30002, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "127.0.0.1".into(), - port: 30005, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - SlotRange { - start: 10923, - end: 16383, - primary: Server { - host: "127.0.0.1".into(), - port: 30003, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "127.0.0.1".into(), - port: 30006, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - ]; - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_cluster_slots_example_null_hostname() { - let first_slot_range = RedisValue::Array(vec![ - 0.into(), - 5460.into(), - RedisValue::Array(vec![ - RedisValue::Null, - 30001.into(), - "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(), - RedisValue::Array(vec![]), - ]), - RedisValue::Array(vec![ - RedisValue::Null, - 30004.into(), - "821d8ca00d7ccf931ed3ffc7e3db0599d2271abf".into(), - RedisValue::Array(vec![]), - ]), - ]); - let second_slot_range = RedisValue::Array(vec![ - 5461.into(), - 10922.into(), - RedisValue::Array(vec![ - RedisValue::Null, - 30002.into(), - "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(), - RedisValue::Array(vec![]), - ]), - RedisValue::Array(vec![ - RedisValue::Null, - 30005.into(), - "faadb3eb99009de4ab72ad6b6ed87634c7ee410f".into(), - RedisValue::Array(vec![]), - ]), - ]); - let third_slot_range = RedisValue::Array(vec![ - 10923.into(), - 16383.into(), - RedisValue::Array(vec![ - RedisValue::Null, - 30003.into(), - "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(), - RedisValue::Array(vec![]), - ]), - RedisValue::Array(vec![ - RedisValue::Null, - 30006.into(), - "58e6e48d41228013e5d9c1c37c5060693925e97e".into(), - RedisValue::Array(vec![]), - ]), - ]); - let input = RedisValue::Array(vec![first_slot_range, second_slot_range, third_slot_range]); - - let actual = parse_cluster_slots(input, &Str::from("fake-host")).expect("Failed to parse input"); - let expected = vec![ - SlotRange { - start: 0, - end: 5460, - primary: Server { - host: "fake-host".into(), - port: 30001, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "fake-host".into(), - port: 30004, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - SlotRange { - start: 5461, - end: 10922, - primary: Server { - host: "fake-host".into(), - port: 30002, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "fake-host".into(), - port: 30005, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - SlotRange { - start: 10923, - end: 16383, - primary: Server { - host: "fake-host".into(), - port: 30003, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "fake-host".into(), - port: 30006, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - ]; - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_cluster_slots_example_empty_hostname() { - let first_slot_range = RedisValue::Array(vec![ - 0.into(), - 5460.into(), - RedisValue::Array(vec![ - RedisValue::Null, - 30001.into(), - "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(), - RedisValue::Array(vec!["hostname".into(), "".into()]), - ]), - RedisValue::Array(vec![ - RedisValue::Null, - 30004.into(), - "821d8ca00d7ccf931ed3ffc7e3db0599d2271abf".into(), - RedisValue::Array(vec!["hostname".into(), "".into()]), - ]), - ]); - let second_slot_range = RedisValue::Array(vec![ - 5461.into(), - 10922.into(), - RedisValue::Array(vec![ - RedisValue::Null, - 30002.into(), - "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(), - RedisValue::Array(vec!["hostname".into(), "".into()]), - ]), - RedisValue::Array(vec![ - RedisValue::Null, - 30005.into(), - "faadb3eb99009de4ab72ad6b6ed87634c7ee410f".into(), - RedisValue::Array(vec!["hostname".into(), "".into()]), - ]), - ]); - let third_slot_range = RedisValue::Array(vec![ - 10923.into(), - 16383.into(), - RedisValue::Array(vec![ - RedisValue::Null, - 30003.into(), - "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(), - RedisValue::Array(vec!["hostname".into(), "".into()]), - ]), - RedisValue::Array(vec![ - RedisValue::Null, - 30006.into(), - "58e6e48d41228013e5d9c1c37c5060693925e97e".into(), - RedisValue::Array(vec!["hostname".into(), "".into()]), - ]), - ]); - let input = RedisValue::Array(vec![first_slot_range, second_slot_range, third_slot_range]); - - let actual = parse_cluster_slots(input, &Str::from("fake-host")).expect("Failed to parse input"); - let expected = vec![ - SlotRange { - start: 0, - end: 5460, - primary: Server { - host: "fake-host".into(), - port: 30001, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "09dbe9720cda62f7865eabc5fd8857c5d2678366".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "fake-host".into(), - port: 30004, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - SlotRange { - start: 5461, - end: 10922, - primary: Server { - host: "fake-host".into(), - port: 30002, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "c9d93d9f2c0c524ff34cc11838c2003d8c29e013".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "fake-host".into(), - port: 30005, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - SlotRange { - start: 10923, - end: 16383, - primary: Server { - host: "fake-host".into(), - port: 30003, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }, - id: "044ec91f325b7595e76dbcb18cc688b6a5b434a1".into(), - #[cfg(feature = "replicas")] - replicas: vec![Server { - host: "fake-host".into(), - port: 30006, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }], - }, - ]; - assert_eq!(actual, expected); - } -} diff --git a/src/protocol/codec.rs b/src/protocol/codec.rs deleted file mode 100644 index a69315f7..00000000 --- a/src/protocol/codec.rs +++ /dev/null @@ -1,244 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - modules::inner::RedisClientInner, - protocol::{ - types::{ProtocolFrame, Server}, - utils as protocol_utils, - }, - runtime::{AtomicBool, RefCount}, - utils, -}; -use bytes::BytesMut; -use bytes_utils::Str; -use redis_protocol::{ - resp2::{ - decode::decode_bytes_mut as resp2_decode, - encode::extend_encode as resp2_encode, - types::BytesFrame as Resp2Frame, - }, - resp3::{ - decode::streaming::decode_bytes_mut as resp3_decode, - encode::complete::extend_encode as resp3_encode, - types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame, StreamedFrame}, - }, -}; -use tokio_util::codec::{Decoder, Encoder}; - -#[cfg(feature = "metrics")] -use crate::modules::metrics::MovingStats; -#[cfg(feature = "metrics")] -use crate::runtime::RwLock; - -#[cfg(not(feature = "network-logs"))] -fn log_resp2_frame(_: &str, _: &Resp2Frame, _: bool) {} -#[cfg(not(feature = "network-logs"))] -fn log_resp3_frame(_: &str, _: &Resp3Frame, _: bool) {} -#[cfg(feature = "network-logs")] -pub use crate::protocol::debug::log_resp2_frame; -#[cfg(feature = "network-logs")] -pub use crate::protocol::debug::log_resp3_frame; - -#[cfg(feature = "metrics")] -fn sample_stats(codec: &RedisCodec, decode: bool, value: i64) { - if decode { - codec.res_size_stats.write().sample(value); - } else { - codec.req_size_stats.write().sample(value); - } -} - -#[cfg(not(feature = "metrics"))] -fn sample_stats(_: &RedisCodec, _: bool, _: i64) {} - -fn resp2_encode_frame(codec: &RedisCodec, item: Resp2Frame, dst: &mut BytesMut) -> Result<(), RedisError> { - let offset = dst.len(); - - let res = resp2_encode(dst, &item)?; - let len = res.saturating_sub(offset); - - trace!( - "{}: Encoded {} bytes to {}. Buffer len: {} (RESP2)", - codec.name, - len, - codec.server, - res - ); - log_resp2_frame(&codec.name, &item, true); - sample_stats(codec, false, len as i64); - - Ok(()) -} - -fn resp2_decode_frame(codec: &RedisCodec, src: &mut BytesMut) -> Result, RedisError> { - trace!( - "{}: Recv {} bytes from {} (RESP2).", - codec.name, - src.len(), - codec.server - ); - if src.is_empty() { - return Ok(None); - } - - if let Some((frame, amt, _)) = resp2_decode(src)? { - trace!("{}: Parsed {} bytes from {}", codec.name, amt, codec.server); - log_resp2_frame(&codec.name, &frame, false); - sample_stats(codec, true, amt as i64); - - Ok(Some(protocol_utils::check_resp2_auth_error(codec, frame))) - } else { - Ok(None) - } -} - -fn resp3_encode_frame(codec: &RedisCodec, item: Resp3Frame, dst: &mut BytesMut) -> Result<(), RedisError> { - let offset = dst.len(); - - let res = resp3_encode(dst, &item)?; - let len = res.saturating_sub(offset); - - trace!( - "{}: Encoded {} bytes to {}. Buffer len: {} (RESP3)", - codec.name, - len, - codec.server, - res - ); - log_resp3_frame(&codec.name, &item, true); - sample_stats(codec, false, len as i64); - - Ok(()) -} - -fn resp3_decode_frame(codec: &mut RedisCodec, src: &mut BytesMut) -> Result, RedisError> { - trace!( - "{}: Recv {} bytes from {} (RESP3).", - codec.name, - src.len(), - codec.server - ); - if src.is_empty() { - return Ok(None); - } - - if let Some((frame, amt, _)) = resp3_decode(src)? { - sample_stats(codec, true, amt as i64); - - if codec.streaming_state.is_some() && frame.is_streaming() { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Cannot start a stream while already inside a stream.", - )); - } - - let result = if let Some(ref mut streamed_frame) = codec.streaming_state { - // we started receiving streamed data earlier - let frame = frame.into_complete_frame()?; - streamed_frame.add_frame(frame); - - if streamed_frame.is_finished() { - let frame = streamed_frame.take()?; - trace!("{}: Ending {:?} stream", codec.name, frame.kind()); - log_resp3_frame(&codec.name, &frame, false); - Some(frame) - } else { - trace!("{}: Continuing {:?} stream", codec.name, streamed_frame.kind); - None - } - } else { - // we're processing a complete frame or starting a new streamed frame - if frame.is_streaming() { - let frame = frame.into_streaming_frame()?; - trace!("{}: Starting {:?} stream", codec.name, frame.kind); - codec.streaming_state = Some(frame); - None - } else { - // we're not in the middle of a stream and we found a complete frame - let frame = frame.into_complete_frame()?; - log_resp3_frame(&codec.name, &frame, false); - Some(protocol_utils::check_resp3_auth_error(codec, frame)) - } - }; - - if result.is_some() { - let _ = codec.streaming_state.take(); - } - Ok(result) - } else { - Ok(None) - } -} - -/// Attempt to decode with RESP2, and if that fails try once with RESP3. -/// -/// This is useful when handling HELLO commands sent in the middle of a RESP2 command sequence. -fn resp2_decode_with_fallback( - codec: &mut RedisCodec, - src: &mut BytesMut, -) -> Result, RedisError> { - let resp2_result = resp2_decode_frame(codec, src).map(|f| f.map(|f| f.into())); - if resp2_result.is_err() { - let resp3_result = resp3_decode_frame(codec, src).map(|f| f.map(|f| f.into())); - if resp3_result.is_ok() { - resp3_result - } else { - resp2_result - } - } else { - resp2_result - } -} - -pub struct RedisCodec { - pub name: Str, - pub server: Server, - pub resp3: RefCount, - pub streaming_state: Option>, - #[cfg(feature = "metrics")] - pub req_size_stats: RefCount>, - #[cfg(feature = "metrics")] - pub res_size_stats: RefCount>, -} - -impl RedisCodec { - pub fn new(inner: &RefCount, server: &Server) -> Self { - RedisCodec { - server: server.clone(), - name: inner.id.clone(), - resp3: inner.shared_resp3(), - streaming_state: None, - #[cfg(feature = "metrics")] - req_size_stats: inner.req_size_stats.clone(), - #[cfg(feature = "metrics")] - res_size_stats: inner.res_size_stats.clone(), - } - } - - pub fn is_resp3(&self) -> bool { - utils::read_bool_atomic(&self.resp3) - } -} - -impl Encoder for RedisCodec { - type Error = RedisError; - - fn encode(&mut self, item: ProtocolFrame, dst: &mut BytesMut) -> Result<(), Self::Error> { - match item { - ProtocolFrame::Resp2(frame) => resp2_encode_frame(self, frame, dst), - ProtocolFrame::Resp3(frame) => resp3_encode_frame(self, frame, dst), - } - } -} - -impl Decoder for RedisCodec { - type Error = RedisError; - type Item = ProtocolFrame; - - fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { - if self.is_resp3() { - resp3_decode_frame(self, src).map(|f| f.map(|f| f.into())) - } else { - resp2_decode_with_fallback(self, src) - } - } -} diff --git a/src/protocol/command.rs b/src/protocol/command.rs deleted file mode 100644 index f3be7781..00000000 --- a/src/protocol/command.rs +++ /dev/null @@ -1,2285 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - interfaces::Resp3Frame, - modules::inner::RedisClientInner, - protocol::{ - hashers::ClusterHash, - responders::ResponseKind, - types::{ProtocolFrame, Server}, - utils as protocol_utils, - }, - runtime::{oneshot_channel, AtomicBool, Mutex, OneshotReceiver, OneshotSender, RefCount}, - trace, - types::{CustomCommand, RedisValue}, - utils as client_utils, - utils, -}; -use bytes_utils::Str; -use redis_protocol::resp3::types::RespVersion; -use std::{ - convert::TryFrom, - fmt, - fmt::Formatter, - mem, - str, - time::{Duration, Instant}, -}; - -#[cfg(feature = "mocks")] -use crate::modules::mocks::MockCommand; -#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))] -use crate::trace::CommandTraces; - -#[cfg(feature = "debug-ids")] -static COMMAND_COUNTER: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); -#[cfg(feature = "debug-ids")] -pub fn command_counter() -> usize { - COMMAND_COUNTER - .fetch_add(1, std::sync::atomic::Ordering::SeqCst) - .saturating_add(1) -} - -/// A command interface for communication between connection reader tasks and the router. -/// -/// Use of this interface assumes that a command was **not** pipelined. The reader task may instead -/// choose to communicate with the router via the shared command queue if no channel exists on -/// which to send this command. -#[derive(Debug)] -pub enum RouterResponse { - /// Continue with the next command. - Continue, - /// Retry the command immediately against the provided server, but with an `ASKING` prefix. - /// - /// Typically used with transactions to retry the entire transaction against a different node. - /// - /// Reader tasks will attempt to use the router channel first when handling cluster errors, but - /// may fall back to communication via the command channel in the context of pipelined commands. - Ask((u16, Server, RedisCommand)), - /// Retry the command immediately against the provided server, updating the cached routing table first. - /// - /// Reader tasks will attempt to use the router channel first when handling cluster errors, but - /// may fall back to communication via the command channel in the context of pipelined commands. - Moved((u16, Server, RedisCommand)), - /// Indicate to the router that the provided transaction command failed with the associated error. - /// - /// The router is responsible for responding to the caller with the error, if needed. Transaction commands are - /// never pipelined. - TransactionError((RedisError, RedisCommand)), - /// Indicates to the router that the transaction finished with the associated result. - TransactionResult(Resp3Frame), - /// Indicates that the connection closed while the command was in-flight. - /// - /// This is only used for non-pipelined commands where the router task is blocked on a response before - /// checking the next command. - ConnectionClosed((RedisError, RedisCommand)), -} - -/// A channel for communication between connection reader tasks and futures returned to the caller. -pub type ResponseSender = OneshotSender>; -/// A sender channel for communication between connection reader tasks and the router. -pub type RouterSender = OneshotSender; -/// A receiver channel for communication between connection reader tasks and the router. -pub type RouterReceiver = OneshotReceiver; - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClusterErrorKind { - Moved, - Ask, -} - -impl fmt::Display for ClusterErrorKind { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - ClusterErrorKind::Moved => write!(f, "MOVED"), - ClusterErrorKind::Ask => write!(f, "ASK"), - } - } -} - -impl<'a> TryFrom<&'a str> for ClusterErrorKind { - type Error = RedisError; - - fn try_from(value: &'a str) -> Result { - match value { - "MOVED" => Ok(ClusterErrorKind::Moved), - "ASK" => Ok(ClusterErrorKind::Ask), - _ => Err(RedisError::new( - RedisErrorKind::Protocol, - "Expected MOVED or ASK error.", - )), - } - } -} - -// TODO organize these and gate them w/ the appropriate feature flags -#[derive(Clone, Eq, PartialEq)] -pub enum RedisCommandKind { - AclLoad, - AclSave, - AclList, - AclUsers, - AclGetUser, - AclSetUser, - AclDelUser, - AclCat, - AclGenPass, - AclWhoAmI, - AclLog, - AclHelp, - Append, - Auth, - Asking, - BgreWriteAof, - BgSave, - BitCount, - BitField, - BitOp, - BitPos, - BlPop, - BlMove, - BrPop, - BrPopLPush, - BzPopMin, - BzPopMax, - BlmPop, - BzmPop, - ClientID, - ClientInfo, - ClientKill, - ClientList, - ClientGetName, - ClientPause, - ClientUnpause, - ClientUnblock, - ClientReply, - ClientSetname, - ClientGetRedir, - ClientTracking, - ClientTrackingInfo, - ClientCaching, - ClusterAddSlots, - ClusterCountFailureReports, - ClusterCountKeysInSlot, - ClusterDelSlots, - ClusterFailOver, - ClusterForget, - ClusterFlushSlots, - ClusterGetKeysInSlot, - ClusterInfo, - ClusterKeySlot, - ClusterMeet, - ClusterMyID, - ClusterNodes, - ClusterReplicate, - ClusterReset, - ClusterSaveConfig, - ClusterSetConfigEpoch, - ClusterBumpEpoch, - ClusterSetSlot, - ClusterReplicas, - ClusterSlots, - ConfigGet, - ConfigRewrite, - ConfigSet, - ConfigResetStat, - Copy, - DBSize, - Decr, - DecrBy, - Del, - Discard, - Dump, - Echo, - Eval, - EvalSha, - Exec, - Exists, - Expire, - ExpireAt, - Failover, - FlushAll, - FlushDB, - GeoAdd, - GeoHash, - GeoPos, - GeoDist, - GeoRadius, - GeoRadiusByMember, - GeoSearch, - GeoSearchStore, - Get, - GetBit, - GetDel, - GetRange, - GetSet, - HDel, - HExists, - HGet, - HGetAll, - HIncrBy, - HIncrByFloat, - HKeys, - HLen, - HMGet, - HMSet, - HSet, - HSetNx, - HStrLen, - HVals, - HRandField, - Incr, - IncrBy, - IncrByFloat, - Info, - Keys, - LastSave, - LIndex, - LInsert, - LLen, - LMove, - LPop, - LPos, - LPush, - LPushX, - LRange, - LMPop, - LRem, - LSet, - LTrim, - Lcs, - MemoryDoctor, - MemoryHelp, - MemoryMallocStats, - MemoryPurge, - MemoryStats, - MemoryUsage, - Mget, - Migrate, - Monitor, - Move, - Mset, - Msetnx, - Multi, - Object, - Persist, - Pexpire, - Pexpireat, - Pfadd, - Pfcount, - Pfmerge, - Ping, - Psetex, - Pttl, - Quit, - Randomkey, - Readonly, - Readwrite, - Rename, - Renamenx, - Restore, - Role, - Rpop, - Rpoplpush, - Rpush, - Rpushx, - Sadd, - Save, - Scard, - Sdiff, - Sdiffstore, - Select, - Sentinel, - Set, - Setbit, - Setex, - Setnx, - Setrange, - Shutdown, - Sinter, - Sinterstore, - Sismember, - Replicaof, - Slowlog, - Smembers, - Smismember, - Smove, - Sort, - SortRo, - Spop, - Srandmember, - Srem, - Strlen, - Sunion, - Sunionstore, - Swapdb, - Sync, - Time, - Touch, - Ttl, - Type, - Unlink, - Unwatch, - Wait, - Watch, - // Streams - XinfoConsumers, - XinfoGroups, - XinfoStream, - Xadd, - Xtrim, - Xdel, - Xrange, - Xrevrange, - Xlen, - Xread, - Xgroupcreate, - XgroupCreateConsumer, - XgroupDelConsumer, - XgroupDestroy, - XgroupSetId, - Xreadgroup, - Xack, - Xclaim, - Xautoclaim, - Xpending, - // Sorted Sets - Zadd, - Zcard, - Zcount, - Zdiff, - Zdiffstore, - Zincrby, - Zinter, - Zinterstore, - Zlexcount, - Zrandmember, - Zrange, - Zrangestore, - Zrangebylex, - Zrangebyscore, - Zrank, - Zrem, - Zremrangebylex, - Zremrangebyrank, - Zremrangebyscore, - Zrevrange, - Zrevrangebylex, - Zrevrangebyscore, - Zrevrank, - Zscore, - Zmscore, - Zunion, - Zunionstore, - Zpopmax, - Zpopmin, - Zmpop, - // Scripts - ScriptLoad, - ScriptDebug, - ScriptExists, - ScriptFlush, - ScriptKill, - // Scanning - Scan, - Sscan, - Hscan, - Zscan, - // Function - Fcall, - FcallRO, - FunctionDelete, - FunctionDump, - FunctionFlush, - FunctionKill, - FunctionList, - FunctionLoad, - FunctionRestore, - FunctionStats, - // Pubsub - Publish, - PubsubChannels, - PubsubNumpat, - PubsubNumsub, - PubsubShardchannels, - PubsubShardnumsub, - Spublish, - Ssubscribe, - Sunsubscribe, - Unsubscribe, - Subscribe, - Psubscribe, - Punsubscribe, - // RedisJSON - JsonArrAppend, - JsonArrIndex, - JsonArrInsert, - JsonArrLen, - JsonArrPop, - JsonArrTrim, - JsonClear, - JsonDebugMemory, - JsonDel, - JsonGet, - JsonMerge, - JsonMGet, - JsonMSet, - JsonNumIncrBy, - JsonObjKeys, - JsonObjLen, - JsonResp, - JsonSet, - JsonStrAppend, - JsonStrLen, - JsonToggle, - JsonType, - // Time Series - TsAdd, - TsAlter, - TsCreate, - TsCreateRule, - TsDecrBy, - TsDel, - TsDeleteRule, - TsGet, - TsIncrBy, - TsInfo, - TsMAdd, - TsMGet, - TsMRange, - TsMRevRange, - TsQueryIndex, - TsRange, - TsRevRange, - // RediSearch - FtList, - FtAggregate, - FtSearch, - FtCreate, - FtAlter, - FtAliasAdd, - FtAliasDel, - FtAliasUpdate, - FtConfigGet, - FtConfigSet, - FtCursorDel, - FtCursorRead, - FtDictAdd, - FtDictDel, - FtDictDump, - FtDropIndex, - FtExplain, - FtInfo, - FtSpellCheck, - FtSugAdd, - FtSugDel, - FtSugGet, - FtSugLen, - FtSynDump, - FtSynUpdate, - FtTagVals, - // Commands with custom state or commands that don't map directly to the server's command interface. - _Hello(RespVersion), - _AuthAllCluster, - _HelloAllCluster(RespVersion), - _FlushAllCluster, - _ScriptFlushCluster, - _ScriptLoadCluster, - _ScriptKillCluster, - _FunctionLoadCluster, - _FunctionFlushCluster, - _FunctionDeleteCluster, - _FunctionRestoreCluster, - // When in RESP3 mode and **not** using the `bcast` arg then we send the command on all cluster node connections - _ClientTrackingCluster, - _Custom(CustomCommand), -} - -impl fmt::Debug for RedisCommandKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_str_debug()) - } -} - -impl RedisCommandKind { - pub fn is_scan(&self) -> bool { - matches!(*self, RedisCommandKind::Scan) - } - - pub fn is_hscan(&self) -> bool { - matches!(*self, RedisCommandKind::Hscan) - } - - pub fn is_sscan(&self) -> bool { - matches!(*self, RedisCommandKind::Sscan) - } - - pub fn is_zscan(&self) -> bool { - matches!(*self, RedisCommandKind::Zscan) - } - - pub fn is_hello(&self) -> bool { - matches!( - *self, - RedisCommandKind::_Hello(_) | RedisCommandKind::_HelloAllCluster(_) - ) - } - - pub fn is_auth(&self) -> bool { - matches!(*self, RedisCommandKind::Auth) - } - - pub fn is_value_scan(&self) -> bool { - matches!( - *self, - RedisCommandKind::Zscan | RedisCommandKind::Hscan | RedisCommandKind::Sscan - ) - } - - pub fn is_multi(&self) -> bool { - matches!(*self, RedisCommandKind::Multi) - } - - pub fn is_exec(&self) -> bool { - matches!(*self, RedisCommandKind::Exec) - } - - pub fn is_discard(&self) -> bool { - matches!(*self, RedisCommandKind::Discard) - } - - pub fn ends_transaction(&self) -> bool { - matches!(*self, RedisCommandKind::Exec | RedisCommandKind::Discard) - } - - pub fn is_mset(&self) -> bool { - matches!(*self, RedisCommandKind::Mset | RedisCommandKind::Msetnx) - } - - pub fn is_custom(&self) -> bool { - matches!(*self, RedisCommandKind::_Custom(_)) - } - - pub fn closes_connection(&self) -> bool { - matches!(*self, RedisCommandKind::Quit | RedisCommandKind::Shutdown) - } - - pub fn custom_hash_slot(&self) -> Option { - match self { - RedisCommandKind::_Custom(ref cmd) => match cmd.cluster_hash { - ClusterHash::Custom(ref val) => Some(*val), - _ => None, - }, - _ => None, - } - } - - /// Read the command's protocol string without panicking. - /// - /// Typically used for logging or debugging. - pub fn to_str_debug(&self) -> &str { - match *self { - RedisCommandKind::AclLoad => "ACL LOAD", - RedisCommandKind::AclSave => "ACL SAVE", - RedisCommandKind::AclList => "ACL LIST", - RedisCommandKind::AclUsers => "ACL USERS", - RedisCommandKind::AclGetUser => "ACL GETUSER", - RedisCommandKind::AclSetUser => "ACL SETUSER", - RedisCommandKind::AclDelUser => "ACL DELUSER", - RedisCommandKind::AclCat => "ACL CAT", - RedisCommandKind::AclGenPass => "ACL GENPASS", - RedisCommandKind::AclWhoAmI => "ACL WHOAMI", - RedisCommandKind::AclLog => "ACL LOG", - RedisCommandKind::AclHelp => "ACL HELP", - RedisCommandKind::Append => "APPEND", - RedisCommandKind::Auth => "AUTH", - RedisCommandKind::Asking => "ASKING", - RedisCommandKind::BgreWriteAof => "BGREWRITEAOF", - RedisCommandKind::BgSave => "BGSAVE", - RedisCommandKind::BitCount => "BITCOUNT", - RedisCommandKind::BitField => "BITFIELD", - RedisCommandKind::BitOp => "BITOP", - RedisCommandKind::BitPos => "BITPOS", - RedisCommandKind::BlPop => "BLPOP", - RedisCommandKind::BlMove => "BLMOVE", - RedisCommandKind::BrPop => "BRPOP", - RedisCommandKind::BzmPop => "BZMPOP", - RedisCommandKind::BlmPop => "BLMPOP", - RedisCommandKind::BrPopLPush => "BRPOPLPUSH", - RedisCommandKind::BzPopMin => "BZPOPMIN", - RedisCommandKind::BzPopMax => "BZPOPMAX", - RedisCommandKind::ClientID => "CLIENT ID", - RedisCommandKind::ClientInfo => "CLIENT INFO", - RedisCommandKind::ClientKill => "CLIENT KILL", - RedisCommandKind::ClientList => "CLIENT LIST", - RedisCommandKind::ClientGetName => "CLIENT GETNAME", - RedisCommandKind::ClientPause => "CLIENT PAUSE", - RedisCommandKind::ClientUnpause => "CLIENT UNPAUSE", - RedisCommandKind::ClientUnblock => "CLIENT UNBLOCK", - RedisCommandKind::ClientReply => "CLIENT REPLY", - RedisCommandKind::ClientSetname => "CLIENT SETNAME", - RedisCommandKind::ClientGetRedir => "CLIENT GETREDIR", - RedisCommandKind::ClientTracking => "CLIENT TRACKING", - RedisCommandKind::ClientTrackingInfo => "CLIENT TRACKINGINFO", - RedisCommandKind::ClientCaching => "CLIENT CACHING", - RedisCommandKind::ClusterAddSlots => "CLUSTER ADDSLOTS", - RedisCommandKind::ClusterCountFailureReports => "CLUSTER COUNT-FAILURE-REPORTS", - RedisCommandKind::ClusterCountKeysInSlot => "CLUSTER COUNTKEYSINSLOT", - RedisCommandKind::ClusterDelSlots => "CLUSTER DEL SLOTS", - RedisCommandKind::ClusterFailOver => "CLUSTER FAILOVER", - RedisCommandKind::ClusterForget => "CLUSTER FORGET", - RedisCommandKind::ClusterGetKeysInSlot => "CLUSTER GETKEYSINSLOTS", - RedisCommandKind::ClusterInfo => "CLUSTER INFO", - RedisCommandKind::ClusterKeySlot => "CLUSTER KEYSLOT", - RedisCommandKind::ClusterMeet => "CLUSTER MEET", - RedisCommandKind::ClusterNodes => "CLUSTER NODES", - RedisCommandKind::ClusterReplicate => "CLUSTER REPLICATE", - RedisCommandKind::ClusterReset => "CLUSTER RESET", - RedisCommandKind::ClusterSaveConfig => "CLUSTER SAVECONFIG", - RedisCommandKind::ClusterSetConfigEpoch => "CLUSTER SET-CONFIG-EPOCH", - RedisCommandKind::ClusterSetSlot => "CLUSTER SETSLOT", - RedisCommandKind::ClusterReplicas => "CLUSTER REPLICAS", - RedisCommandKind::ClusterSlots => "CLUSTER SLOTS", - RedisCommandKind::ClusterBumpEpoch => "CLUSTER BUMPEPOCH", - RedisCommandKind::ClusterFlushSlots => "CLUSTER FLUSHSLOTS", - RedisCommandKind::ClusterMyID => "CLUSTER MYID", - RedisCommandKind::ConfigGet => "CONFIG GET", - RedisCommandKind::ConfigRewrite => "CONFIG REWRITE", - RedisCommandKind::ConfigSet => "CONFIG SET", - RedisCommandKind::ConfigResetStat => "CONFIG RESETSTAT", - RedisCommandKind::Copy => "COPY", - RedisCommandKind::DBSize => "DBSIZE", - RedisCommandKind::Decr => "DECR", - RedisCommandKind::DecrBy => "DECRBY", - RedisCommandKind::Del => "DEL", - RedisCommandKind::Discard => "DISCARD", - RedisCommandKind::Dump => "DUMP", - RedisCommandKind::Echo => "ECHO", - RedisCommandKind::Eval => "EVAL", - RedisCommandKind::EvalSha => "EVALSHA", - RedisCommandKind::Exec => "EXEC", - RedisCommandKind::Exists => "EXISTS", - RedisCommandKind::Expire => "EXPIRE", - RedisCommandKind::ExpireAt => "EXPIREAT", - RedisCommandKind::Failover => "FAILOVER", - RedisCommandKind::FlushAll => "FLUSHALL", - RedisCommandKind::FlushDB => "FLUSHDB", - RedisCommandKind::GeoAdd => "GEOADD", - RedisCommandKind::GeoHash => "GEOHASH", - RedisCommandKind::GeoPos => "GEOPOS", - RedisCommandKind::GeoDist => "GEODIST", - RedisCommandKind::GeoRadius => "GEORADIUS", - RedisCommandKind::GeoRadiusByMember => "GEORADIUSBYMEMBER", - RedisCommandKind::GeoSearch => "GEOSEARCH", - RedisCommandKind::GeoSearchStore => "GEOSEARCHSTORE", - RedisCommandKind::Get => "GET", - RedisCommandKind::GetDel => "GETDEL", - RedisCommandKind::GetBit => "GETBIT", - RedisCommandKind::GetRange => "GETRANGE", - RedisCommandKind::GetSet => "GETSET", - RedisCommandKind::HDel => "HDEL", - RedisCommandKind::_Hello(_) => "HELLO", - RedisCommandKind::HExists => "HEXISTS", - RedisCommandKind::HGet => "HGET", - RedisCommandKind::HGetAll => "HGETALL", - RedisCommandKind::HIncrBy => "HINCRBY", - RedisCommandKind::HIncrByFloat => "HINCRBYFLOAT", - RedisCommandKind::HKeys => "HKEYS", - RedisCommandKind::HLen => "HLEN", - RedisCommandKind::HMGet => "HMGET", - RedisCommandKind::HMSet => "HMSET", - RedisCommandKind::HSet => "HSET", - RedisCommandKind::HSetNx => "HSETNX", - RedisCommandKind::HStrLen => "HSTRLEN", - RedisCommandKind::HRandField => "HRANDFIELD", - RedisCommandKind::HVals => "HVALS", - RedisCommandKind::Incr => "INCR", - RedisCommandKind::IncrBy => "INCRBY", - RedisCommandKind::IncrByFloat => "INCRBYFLOAT", - RedisCommandKind::Info => "INFO", - RedisCommandKind::Keys => "KEYS", - RedisCommandKind::LastSave => "LASTSAVE", - RedisCommandKind::LIndex => "LINDEX", - RedisCommandKind::LInsert => "LINSERT", - RedisCommandKind::LLen => "LLEN", - RedisCommandKind::LMove => "LMOVE", - RedisCommandKind::LPop => "LPOP", - RedisCommandKind::LPos => "LPOS", - RedisCommandKind::LPush => "LPUSH", - RedisCommandKind::LPushX => "LPUSHX", - RedisCommandKind::LRange => "LRANGE", - RedisCommandKind::LMPop => "LMPOP", - RedisCommandKind::LRem => "LREM", - RedisCommandKind::LSet => "LSET", - RedisCommandKind::LTrim => "LTRIM", - RedisCommandKind::Lcs => "LCS", - RedisCommandKind::MemoryDoctor => "MEMORY DOCTOR", - RedisCommandKind::MemoryHelp => "MEMORY HELP", - RedisCommandKind::MemoryMallocStats => "MEMORY MALLOC-STATS", - RedisCommandKind::MemoryPurge => "MEMORY PURGE", - RedisCommandKind::MemoryStats => "MEMORY STATS", - RedisCommandKind::MemoryUsage => "MEMORY USAGE", - RedisCommandKind::Mget => "MGET", - RedisCommandKind::Migrate => "MIGRATE", - RedisCommandKind::Monitor => "MONITOR", - RedisCommandKind::Move => "MOVE", - RedisCommandKind::Mset => "MSET", - RedisCommandKind::Msetnx => "MSETNX", - RedisCommandKind::Multi => "MULTI", - RedisCommandKind::Object => "OBJECT", - RedisCommandKind::Persist => "PERSIST", - RedisCommandKind::Pexpire => "PEXPIRE", - RedisCommandKind::Pexpireat => "PEXPIREAT", - RedisCommandKind::Pfadd => "PFADD", - RedisCommandKind::Pfcount => "PFCOUNT", - RedisCommandKind::Pfmerge => "PFMERGE", - RedisCommandKind::Ping => "PING", - RedisCommandKind::Psetex => "PSETEX", - RedisCommandKind::Psubscribe => "PSUBSCRIBE", - RedisCommandKind::Pttl => "PTTL", - RedisCommandKind::Publish => "PUBLISH", - RedisCommandKind::Punsubscribe => "PUNSUBSCRIBE", - RedisCommandKind::Quit => "QUIT", - RedisCommandKind::Randomkey => "RANDOMKEY", - RedisCommandKind::Readonly => "READONLY", - RedisCommandKind::Readwrite => "READWRITE", - RedisCommandKind::Rename => "RENAME", - RedisCommandKind::Renamenx => "RENAMENX", - RedisCommandKind::Restore => "RESTORE", - RedisCommandKind::Role => "ROLE", - RedisCommandKind::Rpop => "RPOP", - RedisCommandKind::Rpoplpush => "RPOPLPUSH", - RedisCommandKind::Rpush => "RPUSH", - RedisCommandKind::Rpushx => "RPUSHX", - RedisCommandKind::Sadd => "SADD", - RedisCommandKind::Save => "SAVE", - RedisCommandKind::Scard => "SCARD", - RedisCommandKind::Sdiff => "SDIFF", - RedisCommandKind::Sdiffstore => "SDIFFSTORE", - RedisCommandKind::Select => "SELECT", - RedisCommandKind::Sentinel => "SENTINEL", - RedisCommandKind::Set => "SET", - RedisCommandKind::Setbit => "SETBIT", - RedisCommandKind::Setex => "SETEX", - RedisCommandKind::Setnx => "SETNX", - RedisCommandKind::Setrange => "SETRANGE", - RedisCommandKind::Shutdown => "SHUTDOWN", - RedisCommandKind::Sinter => "SINTER", - RedisCommandKind::Sinterstore => "SINTERSTORE", - RedisCommandKind::Sismember => "SISMEMBER", - RedisCommandKind::Replicaof => "REPLICAOF", - RedisCommandKind::Slowlog => "SLOWLOG", - RedisCommandKind::Smembers => "SMEMBERS", - RedisCommandKind::Smismember => "SMISMEMBER", - RedisCommandKind::Smove => "SMOVE", - RedisCommandKind::Sort => "SORT", - RedisCommandKind::SortRo => "SORT_RO", - RedisCommandKind::Spop => "SPOP", - RedisCommandKind::Srandmember => "SRANDMEMBER", - RedisCommandKind::Srem => "SREM", - RedisCommandKind::Strlen => "STRLEN", - RedisCommandKind::Subscribe => "SUBSCRIBE", - RedisCommandKind::Sunion => "SUNION", - RedisCommandKind::Sunionstore => "SUNIONSTORE", - RedisCommandKind::Swapdb => "SWAPDB", - RedisCommandKind::Sync => "SYNC", - RedisCommandKind::Time => "TIME", - RedisCommandKind::Touch => "TOUCH", - RedisCommandKind::Ttl => "TTL", - RedisCommandKind::Type => "TYPE", - RedisCommandKind::Unsubscribe => "UNSUBSCRIBE", - RedisCommandKind::Unlink => "UNLINK", - RedisCommandKind::Unwatch => "UNWATCH", - RedisCommandKind::Wait => "WAIT", - RedisCommandKind::Watch => "WATCH", - RedisCommandKind::XinfoConsumers => "XINFO CONSUMERS", - RedisCommandKind::XinfoGroups => "XINFO GROUPS", - RedisCommandKind::XinfoStream => "XINFO STREAM", - RedisCommandKind::Xadd => "XADD", - RedisCommandKind::Xtrim => "XTRIM", - RedisCommandKind::Xdel => "XDEL", - RedisCommandKind::Xrange => "XRANGE", - RedisCommandKind::Xrevrange => "XREVRANGE", - RedisCommandKind::Xlen => "XLEN", - RedisCommandKind::Xread => "XREAD", - RedisCommandKind::Xgroupcreate => "XGROUP CREATE", - RedisCommandKind::XgroupCreateConsumer => "XGROUP CREATECONSUMER", - RedisCommandKind::XgroupDelConsumer => "XGROUP DELCONSUMER", - RedisCommandKind::XgroupDestroy => "XGROUP DESTROY", - RedisCommandKind::XgroupSetId => "XGROUP SETID", - RedisCommandKind::Xreadgroup => "XREADGROUP", - RedisCommandKind::Xack => "XACK", - RedisCommandKind::Xclaim => "XCLAIM", - RedisCommandKind::Xautoclaim => "XAUTOCLAIM", - RedisCommandKind::Xpending => "XPENDING", - RedisCommandKind::Zadd => "ZADD", - RedisCommandKind::Zcard => "ZCARD", - RedisCommandKind::Zcount => "ZCOUNT", - RedisCommandKind::Zdiff => "ZDIFF", - RedisCommandKind::Zdiffstore => "ZDIFFSTORE", - RedisCommandKind::Zincrby => "ZINCRBY", - RedisCommandKind::Zinter => "ZINTER", - RedisCommandKind::Zinterstore => "ZINTERSTORE", - RedisCommandKind::Zlexcount => "ZLEXCOUNT", - RedisCommandKind::Zrandmember => "ZRANDMEMBER", - RedisCommandKind::Zrange => "ZRANGE", - RedisCommandKind::Zrangestore => "ZRANGESTORE", - RedisCommandKind::Zrangebylex => "ZRANGEBYLEX", - RedisCommandKind::Zrangebyscore => "ZRANGEBYSCORE", - RedisCommandKind::Zrank => "ZRANK", - RedisCommandKind::Zrem => "ZREM", - RedisCommandKind::Zremrangebylex => "ZREMRANGEBYLEX", - RedisCommandKind::Zremrangebyrank => "ZREMRANGEBYRANK", - RedisCommandKind::Zremrangebyscore => "ZREMRANGEBYSCORE", - RedisCommandKind::Zrevrange => "ZREVRANGE", - RedisCommandKind::Zrevrangebylex => "ZREVRANGEBYLEX", - RedisCommandKind::Zrevrangebyscore => "ZREVRANGEBYSCORE", - RedisCommandKind::Zrevrank => "ZREVRANK", - RedisCommandKind::Zscore => "ZSCORE", - RedisCommandKind::Zmscore => "ZMSCORE", - RedisCommandKind::Zunion => "ZUNION", - RedisCommandKind::Zunionstore => "ZUNIONSTORE", - RedisCommandKind::Zpopmax => "ZPOPMAX", - RedisCommandKind::Zpopmin => "ZPOPMIN", - RedisCommandKind::Zmpop => "ZMPOP", - RedisCommandKind::Scan => "SCAN", - RedisCommandKind::Sscan => "SSCAN", - RedisCommandKind::Hscan => "HSCAN", - RedisCommandKind::Zscan => "ZSCAN", - RedisCommandKind::ScriptDebug => "SCRIPT DEBUG", - RedisCommandKind::ScriptExists => "SCRIPT EXISTS", - RedisCommandKind::ScriptFlush => "SCRIPT FLUSH", - RedisCommandKind::ScriptKill => "SCRIPT KILL", - RedisCommandKind::ScriptLoad => "SCRIPT LOAD", - RedisCommandKind::Spublish => "SPUBLISH", - RedisCommandKind::Ssubscribe => "SSUBSCRIBE", - RedisCommandKind::Sunsubscribe => "SUNSUBSCRIBE", - RedisCommandKind::_AuthAllCluster => "AUTH ALL CLUSTER", - RedisCommandKind::_HelloAllCluster(_) => "HELLO ALL CLUSTER", - RedisCommandKind::_FlushAllCluster => "FLUSHALL CLUSTER", - RedisCommandKind::_ScriptFlushCluster => "SCRIPT FLUSH CLUSTER", - RedisCommandKind::_ScriptLoadCluster => "SCRIPT LOAD CLUSTER", - RedisCommandKind::_ScriptKillCluster => "SCRIPT Kill CLUSTER", - RedisCommandKind::_FunctionLoadCluster => "FUNCTION LOAD CLUSTER", - RedisCommandKind::_FunctionFlushCluster => "FUNCTION FLUSH CLUSTER", - RedisCommandKind::_FunctionDeleteCluster => "FUNCTION DELETE CLUSTER", - RedisCommandKind::_FunctionRestoreCluster => "FUNCTION RESTORE CLUSTER", - RedisCommandKind::_ClientTrackingCluster => "CLIENT TRACKING CLUSTER", - RedisCommandKind::Fcall => "FCALL", - RedisCommandKind::FcallRO => "FCALL_RO", - RedisCommandKind::FunctionDelete => "FUNCTION DELETE", - RedisCommandKind::FunctionDump => "FUNCTION DUMP", - RedisCommandKind::FunctionFlush => "FUNCTION FLUSH", - RedisCommandKind::FunctionKill => "FUNCTION KILL", - RedisCommandKind::FunctionList => "FUNCTION LIST", - RedisCommandKind::FunctionLoad => "FUNCTION LOAD", - RedisCommandKind::FunctionRestore => "FUNCTION RESTORE", - RedisCommandKind::FunctionStats => "FUNCTION STATS", - RedisCommandKind::PubsubChannels => "PUBSUB CHANNELS", - RedisCommandKind::PubsubNumpat => "PUBSUB NUMPAT", - RedisCommandKind::PubsubNumsub => "PUBSUB NUMSUB", - RedisCommandKind::PubsubShardchannels => "PUBSUB SHARDCHANNELS", - RedisCommandKind::PubsubShardnumsub => "PUBSUB SHARDNUMSUB", - RedisCommandKind::JsonArrAppend => "JSON.ARRAPPEND", - RedisCommandKind::JsonArrIndex => "JSON.ARRINDEX", - RedisCommandKind::JsonArrInsert => "JSON.ARRINSERT", - RedisCommandKind::JsonArrLen => "JSON.ARRLEN", - RedisCommandKind::JsonArrPop => "JSON.ARRPOP", - RedisCommandKind::JsonArrTrim => "JSON.ARRTRIM", - RedisCommandKind::JsonClear => "JSON.CLEAR", - RedisCommandKind::JsonDebugMemory => "JSON.DEBUG MEMORY", - RedisCommandKind::JsonDel => "JSON.DEL", - RedisCommandKind::JsonGet => "JSON.GET", - RedisCommandKind::JsonMerge => "JSON.MERGE", - RedisCommandKind::JsonMGet => "JSON.MGET", - RedisCommandKind::JsonMSet => "JSON.MSET", - RedisCommandKind::JsonNumIncrBy => "JSON.NUMINCRBY", - RedisCommandKind::JsonObjKeys => "JSON.OBJKEYS", - RedisCommandKind::JsonObjLen => "JSON.OBJLEN", - RedisCommandKind::JsonResp => "JSON.RESP", - RedisCommandKind::JsonSet => "JSON.SET", - RedisCommandKind::JsonStrAppend => "JSON.STRAPPEND", - RedisCommandKind::JsonStrLen => "JSON.STRLEN", - RedisCommandKind::JsonToggle => "JSON.TOGGLE", - RedisCommandKind::JsonType => "JSON.TYPE", - RedisCommandKind::TsAdd => "TS.ADD", - RedisCommandKind::TsAlter => "TS.ALTER", - RedisCommandKind::TsCreate => "TS.CREATE", - RedisCommandKind::TsCreateRule => "TS.CREATERULE", - RedisCommandKind::TsDecrBy => "TS.DECRBY", - RedisCommandKind::TsDel => "TS.DEL", - RedisCommandKind::TsDeleteRule => "TS.DELETERULE", - RedisCommandKind::TsGet => "TS.GET", - RedisCommandKind::TsIncrBy => "TS.INCRBY", - RedisCommandKind::TsInfo => "TS.INFO", - RedisCommandKind::TsMAdd => "TS.MADD", - RedisCommandKind::TsMGet => "TS.MGET", - RedisCommandKind::TsMRange => "TS.MRANGE", - RedisCommandKind::TsMRevRange => "TS.MREVRANGE", - RedisCommandKind::TsQueryIndex => "TS.QUERYINDEX", - RedisCommandKind::TsRange => "TS.RANGE", - RedisCommandKind::TsRevRange => "TS.REVRANGE", - RedisCommandKind::FtList => "FT._LIST", - RedisCommandKind::FtAggregate => "FT.AGGREGATE", - RedisCommandKind::FtSearch => "FT.SEARCH", - RedisCommandKind::FtCreate => "FT.CREATE", - RedisCommandKind::FtAlter => "FT.ALTER", - RedisCommandKind::FtAliasAdd => "FT.ALIASADD", - RedisCommandKind::FtAliasDel => "FT.ALIASDEL", - RedisCommandKind::FtAliasUpdate => "FT.ALIASUPDATE", - RedisCommandKind::FtConfigGet => "FT.CONFIG GET", - RedisCommandKind::FtConfigSet => "FT.CONFIG SET", - RedisCommandKind::FtCursorDel => "FT.CURSOR DEL", - RedisCommandKind::FtCursorRead => "FT.CURSOR READ", - RedisCommandKind::FtDictAdd => "FT.DICTADD", - RedisCommandKind::FtDictDel => "FT.DICTDEL", - RedisCommandKind::FtDictDump => "FT.DICTDUMP", - RedisCommandKind::FtDropIndex => "FT.DROPINDEX", - RedisCommandKind::FtExplain => "FT.EXPLAIN", - RedisCommandKind::FtInfo => "FT.INFO", - RedisCommandKind::FtSpellCheck => "FT.SPELLCHECK", - RedisCommandKind::FtSugAdd => "FT.SUGADD", - RedisCommandKind::FtSugDel => "FT.SUGDEL", - RedisCommandKind::FtSugGet => "FT.SUGGET", - RedisCommandKind::FtSugLen => "FT.SUGLEN", - RedisCommandKind::FtSynDump => "FT.SYNDUMP", - RedisCommandKind::FtSynUpdate => "FT.SYNUPDATE", - RedisCommandKind::FtTagVals => "FT.TAGVALS", - RedisCommandKind::_Custom(ref kind) => &kind.cmd, - } - } - - /// Read the protocol string for a command, panicking for internal commands that don't map directly to redis - /// command. - pub(crate) fn cmd_str(&self) -> Str { - let s = match *self { - RedisCommandKind::AclLoad - | RedisCommandKind::AclSave - | RedisCommandKind::AclList - | RedisCommandKind::AclUsers - | RedisCommandKind::AclGetUser - | RedisCommandKind::AclSetUser - | RedisCommandKind::AclDelUser - | RedisCommandKind::AclCat - | RedisCommandKind::AclGenPass - | RedisCommandKind::AclWhoAmI - | RedisCommandKind::AclLog - | RedisCommandKind::AclHelp => "ACL", - RedisCommandKind::Append => "APPEND", - RedisCommandKind::Auth => "AUTH", - RedisCommandKind::Asking => "ASKING", - RedisCommandKind::BgreWriteAof => "BGREWRITEAOF", - RedisCommandKind::BgSave => "BGSAVE", - RedisCommandKind::BitCount => "BITCOUNT", - RedisCommandKind::BitField => "BITFIELD", - RedisCommandKind::BitOp => "BITOP", - RedisCommandKind::BitPos => "BITPOS", - RedisCommandKind::BlPop => "BLPOP", - RedisCommandKind::BlMove => "BLMOVE", - RedisCommandKind::BrPop => "BRPOP", - RedisCommandKind::BrPopLPush => "BRPOPLPUSH", - RedisCommandKind::BzPopMin => "BZPOPMIN", - RedisCommandKind::BzPopMax => "BZPOPMAX", - RedisCommandKind::BzmPop => "BZMPOP", - RedisCommandKind::BlmPop => "BLMPOP", - RedisCommandKind::ClientID - | RedisCommandKind::ClientInfo - | RedisCommandKind::ClientKill - | RedisCommandKind::ClientList - | RedisCommandKind::ClientGetName - | RedisCommandKind::ClientPause - | RedisCommandKind::ClientUnpause - | RedisCommandKind::ClientUnblock - | RedisCommandKind::ClientReply - | RedisCommandKind::ClientSetname - | RedisCommandKind::ClientCaching - | RedisCommandKind::ClientTrackingInfo - | RedisCommandKind::ClientTracking - | RedisCommandKind::ClientGetRedir => "CLIENT", - RedisCommandKind::ClusterAddSlots - | RedisCommandKind::ClusterCountFailureReports - | RedisCommandKind::ClusterCountKeysInSlot - | RedisCommandKind::ClusterDelSlots - | RedisCommandKind::ClusterFailOver - | RedisCommandKind::ClusterForget - | RedisCommandKind::ClusterGetKeysInSlot - | RedisCommandKind::ClusterInfo - | RedisCommandKind::ClusterKeySlot - | RedisCommandKind::ClusterMeet - | RedisCommandKind::ClusterNodes - | RedisCommandKind::ClusterReplicate - | RedisCommandKind::ClusterReset - | RedisCommandKind::ClusterSaveConfig - | RedisCommandKind::ClusterSetConfigEpoch - | RedisCommandKind::ClusterSetSlot - | RedisCommandKind::ClusterReplicas - | RedisCommandKind::ClusterSlots - | RedisCommandKind::ClusterBumpEpoch - | RedisCommandKind::ClusterFlushSlots - | RedisCommandKind::ClusterMyID => "CLUSTER", - RedisCommandKind::ConfigGet - | RedisCommandKind::ConfigRewrite - | RedisCommandKind::ConfigSet - | RedisCommandKind::ConfigResetStat => "CONFIG", - RedisCommandKind::Copy => "COPY", - RedisCommandKind::DBSize => "DBSIZE", - RedisCommandKind::Decr => "DECR", - RedisCommandKind::DecrBy => "DECRBY", - RedisCommandKind::Del => "DEL", - RedisCommandKind::Discard => "DISCARD", - RedisCommandKind::Dump => "DUMP", - RedisCommandKind::Echo => "ECHO", - RedisCommandKind::Eval => "EVAL", - RedisCommandKind::EvalSha => "EVALSHA", - RedisCommandKind::Exec => "EXEC", - RedisCommandKind::Exists => "EXISTS", - RedisCommandKind::Expire => "EXPIRE", - RedisCommandKind::ExpireAt => "EXPIREAT", - RedisCommandKind::Failover => "FAILOVER", - RedisCommandKind::FlushAll => "FLUSHALL", - RedisCommandKind::_FlushAllCluster => "FLUSHALL", - RedisCommandKind::FlushDB => "FLUSHDB", - RedisCommandKind::GeoAdd => "GEOADD", - RedisCommandKind::GeoHash => "GEOHASH", - RedisCommandKind::GeoPos => "GEOPOS", - RedisCommandKind::GeoDist => "GEODIST", - RedisCommandKind::GeoRadius => "GEORADIUS", - RedisCommandKind::GeoRadiusByMember => "GEORADIUSBYMEMBER", - RedisCommandKind::GeoSearch => "GEOSEARCH", - RedisCommandKind::GeoSearchStore => "GEOSEARCHSTORE", - RedisCommandKind::Get => "GET", - RedisCommandKind::GetDel => "GETDEL", - RedisCommandKind::GetBit => "GETBIT", - RedisCommandKind::GetRange => "GETRANGE", - RedisCommandKind::GetSet => "GETSET", - RedisCommandKind::HDel => "HDEL", - RedisCommandKind::_Hello(_) => "HELLO", - RedisCommandKind::HExists => "HEXISTS", - RedisCommandKind::HGet => "HGET", - RedisCommandKind::HGetAll => "HGETALL", - RedisCommandKind::HIncrBy => "HINCRBY", - RedisCommandKind::HIncrByFloat => "HINCRBYFLOAT", - RedisCommandKind::HKeys => "HKEYS", - RedisCommandKind::HLen => "HLEN", - RedisCommandKind::HMGet => "HMGET", - RedisCommandKind::HMSet => "HMSET", - RedisCommandKind::HSet => "HSET", - RedisCommandKind::HSetNx => "HSETNX", - RedisCommandKind::HStrLen => "HSTRLEN", - RedisCommandKind::HRandField => "HRANDFIELD", - RedisCommandKind::HVals => "HVALS", - RedisCommandKind::Incr => "INCR", - RedisCommandKind::IncrBy => "INCRBY", - RedisCommandKind::IncrByFloat => "INCRBYFLOAT", - RedisCommandKind::Info => "INFO", - RedisCommandKind::Keys => "KEYS", - RedisCommandKind::LastSave => "LASTSAVE", - RedisCommandKind::LIndex => "LINDEX", - RedisCommandKind::LInsert => "LINSERT", - RedisCommandKind::LLen => "LLEN", - RedisCommandKind::LMove => "LMOVE", - RedisCommandKind::LPop => "LPOP", - RedisCommandKind::LPos => "LPOS", - RedisCommandKind::LPush => "LPUSH", - RedisCommandKind::LPushX => "LPUSHX", - RedisCommandKind::LRange => "LRANGE", - RedisCommandKind::LMPop => "LMPOP", - RedisCommandKind::LRem => "LREM", - RedisCommandKind::LSet => "LSET", - RedisCommandKind::LTrim => "LTRIM", - RedisCommandKind::Lcs => "LCS", - RedisCommandKind::MemoryDoctor => "MEMORY", - RedisCommandKind::MemoryHelp => "MEMORY", - RedisCommandKind::MemoryMallocStats => "MEMORY", - RedisCommandKind::MemoryPurge => "MEMORY", - RedisCommandKind::MemoryStats => "MEMORY", - RedisCommandKind::MemoryUsage => "MEMORY", - RedisCommandKind::Mget => "MGET", - RedisCommandKind::Migrate => "MIGRATE", - RedisCommandKind::Monitor => "MONITOR", - RedisCommandKind::Move => "MOVE", - RedisCommandKind::Mset => "MSET", - RedisCommandKind::Msetnx => "MSETNX", - RedisCommandKind::Multi => "MULTI", - RedisCommandKind::Object => "OBJECT", - RedisCommandKind::Persist => "PERSIST", - RedisCommandKind::Pexpire => "PEXPIRE", - RedisCommandKind::Pexpireat => "PEXPIREAT", - RedisCommandKind::Pfadd => "PFADD", - RedisCommandKind::Pfcount => "PFCOUNT", - RedisCommandKind::Pfmerge => "PFMERGE", - RedisCommandKind::Ping => "PING", - RedisCommandKind::Psetex => "PSETEX", - RedisCommandKind::Psubscribe => "PSUBSCRIBE", - RedisCommandKind::Pttl => "PTTL", - RedisCommandKind::Publish => "PUBLISH", - RedisCommandKind::Punsubscribe => "PUNSUBSCRIBE", - RedisCommandKind::Quit => "QUIT", - RedisCommandKind::Randomkey => "RANDOMKEY", - RedisCommandKind::Readonly => "READONLY", - RedisCommandKind::Readwrite => "READWRITE", - RedisCommandKind::Rename => "RENAME", - RedisCommandKind::Renamenx => "RENAMENX", - RedisCommandKind::Restore => "RESTORE", - RedisCommandKind::Role => "ROLE", - RedisCommandKind::Rpop => "RPOP", - RedisCommandKind::Rpoplpush => "RPOPLPUSH", - RedisCommandKind::Rpush => "RPUSH", - RedisCommandKind::Rpushx => "RPUSHX", - RedisCommandKind::Sadd => "SADD", - RedisCommandKind::Save => "SAVE", - RedisCommandKind::Scard => "SCARD", - RedisCommandKind::Sdiff => "SDIFF", - RedisCommandKind::Sdiffstore => "SDIFFSTORE", - RedisCommandKind::Select => "SELECT", - RedisCommandKind::Sentinel => "SENTINEL", - RedisCommandKind::Set => "SET", - RedisCommandKind::Setbit => "SETBIT", - RedisCommandKind::Setex => "SETEX", - RedisCommandKind::Setnx => "SETNX", - RedisCommandKind::Setrange => "SETRANGE", - RedisCommandKind::Shutdown => "SHUTDOWN", - RedisCommandKind::Sinter => "SINTER", - RedisCommandKind::Sinterstore => "SINTERSTORE", - RedisCommandKind::Sismember => "SISMEMBER", - RedisCommandKind::Replicaof => "REPLICAOF", - RedisCommandKind::Slowlog => "SLOWLOG", - RedisCommandKind::Smembers => "SMEMBERS", - RedisCommandKind::Smismember => "SMISMEMBER", - RedisCommandKind::Smove => "SMOVE", - RedisCommandKind::Sort => "SORT", - RedisCommandKind::SortRo => "SORT_RO", - RedisCommandKind::Spop => "SPOP", - RedisCommandKind::Srandmember => "SRANDMEMBER", - RedisCommandKind::Srem => "SREM", - RedisCommandKind::Strlen => "STRLEN", - RedisCommandKind::Subscribe => "SUBSCRIBE", - RedisCommandKind::Sunion => "SUNION", - RedisCommandKind::Sunionstore => "SUNIONSTORE", - RedisCommandKind::Swapdb => "SWAPDB", - RedisCommandKind::Sync => "SYNC", - RedisCommandKind::Time => "TIME", - RedisCommandKind::Touch => "TOUCH", - RedisCommandKind::Ttl => "TTL", - RedisCommandKind::Type => "TYPE", - RedisCommandKind::Unsubscribe => "UNSUBSCRIBE", - RedisCommandKind::Unlink => "UNLINK", - RedisCommandKind::Unwatch => "UNWATCH", - RedisCommandKind::Wait => "WAIT", - RedisCommandKind::Watch => "WATCH", - RedisCommandKind::XinfoConsumers | RedisCommandKind::XinfoGroups | RedisCommandKind::XinfoStream => "XINFO", - RedisCommandKind::Xadd => "XADD", - RedisCommandKind::Xtrim => "XTRIM", - RedisCommandKind::Xdel => "XDEL", - RedisCommandKind::Xrange => "XRANGE", - RedisCommandKind::Xrevrange => "XREVRANGE", - RedisCommandKind::Xlen => "XLEN", - RedisCommandKind::Xread => "XREAD", - RedisCommandKind::Xgroupcreate - | RedisCommandKind::XgroupCreateConsumer - | RedisCommandKind::XgroupDelConsumer - | RedisCommandKind::XgroupDestroy - | RedisCommandKind::XgroupSetId => "XGROUP", - RedisCommandKind::Xreadgroup => "XREADGROUP", - RedisCommandKind::Xack => "XACK", - RedisCommandKind::Xclaim => "XCLAIM", - RedisCommandKind::Xautoclaim => "XAUTOCLAIM", - RedisCommandKind::Xpending => "XPENDING", - RedisCommandKind::Zadd => "ZADD", - RedisCommandKind::Zcard => "ZCARD", - RedisCommandKind::Zcount => "ZCOUNT", - RedisCommandKind::Zdiff => "ZDIFF", - RedisCommandKind::Zdiffstore => "ZDIFFSTORE", - RedisCommandKind::Zincrby => "ZINCRBY", - RedisCommandKind::Zinter => "ZINTER", - RedisCommandKind::Zinterstore => "ZINTERSTORE", - RedisCommandKind::Zlexcount => "ZLEXCOUNT", - RedisCommandKind::Zrandmember => "ZRANDMEMBER", - RedisCommandKind::Zrange => "ZRANGE", - RedisCommandKind::Zrangestore => "ZRANGESTORE", - RedisCommandKind::Zrangebylex => "ZRANGEBYLEX", - RedisCommandKind::Zrangebyscore => "ZRANGEBYSCORE", - RedisCommandKind::Zrank => "ZRANK", - RedisCommandKind::Zrem => "ZREM", - RedisCommandKind::Zremrangebylex => "ZREMRANGEBYLEX", - RedisCommandKind::Zremrangebyrank => "ZREMRANGEBYRANK", - RedisCommandKind::Zremrangebyscore => "ZREMRANGEBYSCORE", - RedisCommandKind::Zrevrange => "ZREVRANGE", - RedisCommandKind::Zrevrangebylex => "ZREVRANGEBYLEX", - RedisCommandKind::Zrevrangebyscore => "ZREVRANGEBYSCORE", - RedisCommandKind::Zrevrank => "ZREVRANK", - RedisCommandKind::Zscore => "ZSCORE", - RedisCommandKind::Zmscore => "ZMSCORE", - RedisCommandKind::Zunion => "ZUNION", - RedisCommandKind::Zunionstore => "ZUNIONSTORE", - RedisCommandKind::Zpopmax => "ZPOPMAX", - RedisCommandKind::Zpopmin => "ZPOPMIN", - RedisCommandKind::Zmpop => "ZMPOP", - RedisCommandKind::ScriptDebug - | RedisCommandKind::ScriptExists - | RedisCommandKind::ScriptFlush - | RedisCommandKind::ScriptKill - | RedisCommandKind::ScriptLoad - | RedisCommandKind::_ScriptFlushCluster - | RedisCommandKind::_ScriptKillCluster - | RedisCommandKind::_ScriptLoadCluster => "SCRIPT", - RedisCommandKind::Spublish => "SPUBLISH", - RedisCommandKind::Ssubscribe => "SSUBSCRIBE", - RedisCommandKind::Sunsubscribe => "SUNSUBSCRIBE", - RedisCommandKind::Scan => "SCAN", - RedisCommandKind::Sscan => "SSCAN", - RedisCommandKind::Hscan => "HSCAN", - RedisCommandKind::Zscan => "ZSCAN", - RedisCommandKind::Fcall => "FCALL", - RedisCommandKind::FcallRO => "FCALL_RO", - RedisCommandKind::FunctionDelete - | RedisCommandKind::FunctionDump - | RedisCommandKind::FunctionFlush - | RedisCommandKind::FunctionKill - | RedisCommandKind::FunctionList - | RedisCommandKind::FunctionLoad - | RedisCommandKind::FunctionRestore - | RedisCommandKind::FunctionStats - | RedisCommandKind::_FunctionFlushCluster - | RedisCommandKind::_FunctionRestoreCluster - | RedisCommandKind::_FunctionDeleteCluster - | RedisCommandKind::_FunctionLoadCluster => "FUNCTION", - RedisCommandKind::PubsubChannels - | RedisCommandKind::PubsubNumpat - | RedisCommandKind::PubsubNumsub - | RedisCommandKind::PubsubShardchannels - | RedisCommandKind::PubsubShardnumsub => "PUBSUB", - RedisCommandKind::_AuthAllCluster => "AUTH", - RedisCommandKind::_HelloAllCluster(_) => "HELLO", - RedisCommandKind::_ClientTrackingCluster => "CLIENT", - RedisCommandKind::JsonArrAppend => "JSON.ARRAPPEND", - RedisCommandKind::JsonArrIndex => "JSON.ARRINDEX", - RedisCommandKind::JsonArrInsert => "JSON.ARRINSERT", - RedisCommandKind::JsonArrLen => "JSON.ARRLEN", - RedisCommandKind::JsonArrPop => "JSON.ARRPOP", - RedisCommandKind::JsonArrTrim => "JSON.ARRTRIM", - RedisCommandKind::JsonClear => "JSON.CLEAR", - RedisCommandKind::JsonDebugMemory => "JSON.DEBUG", - RedisCommandKind::JsonDel => "JSON.DEL", - RedisCommandKind::JsonGet => "JSON.GET", - RedisCommandKind::JsonMerge => "JSON.MERGE", - RedisCommandKind::JsonMGet => "JSON.MGET", - RedisCommandKind::JsonMSet => "JSON.MSET", - RedisCommandKind::JsonNumIncrBy => "JSON.NUMINCRBY", - RedisCommandKind::JsonObjKeys => "JSON.OBJKEYS", - RedisCommandKind::JsonObjLen => "JSON.OBJLEN", - RedisCommandKind::JsonResp => "JSON.RESP", - RedisCommandKind::JsonSet => "JSON.SET", - RedisCommandKind::JsonStrAppend => "JSON.STRAPPEND", - RedisCommandKind::JsonStrLen => "JSON.STRLEN", - RedisCommandKind::JsonToggle => "JSON.TOGGLE", - RedisCommandKind::JsonType => "JSON.TYPE", - RedisCommandKind::TsAdd => "TS.ADD", - RedisCommandKind::TsAlter => "TS.ALTER", - RedisCommandKind::TsCreate => "TS.CREATE", - RedisCommandKind::TsCreateRule => "TS.CREATERULE", - RedisCommandKind::TsDecrBy => "TS.DECRBY", - RedisCommandKind::TsDel => "TS.DEL", - RedisCommandKind::TsDeleteRule => "TS.DELETERULE", - RedisCommandKind::TsGet => "TS.GET", - RedisCommandKind::TsIncrBy => "TS.INCRBY", - RedisCommandKind::TsInfo => "TS.INFO", - RedisCommandKind::TsMAdd => "TS.MADD", - RedisCommandKind::TsMGet => "TS.MGET", - RedisCommandKind::TsMRange => "TS.MRANGE", - RedisCommandKind::TsMRevRange => "TS.MREVRANGE", - RedisCommandKind::TsQueryIndex => "TS.QUERYINDEX", - RedisCommandKind::TsRange => "TS.RANGE", - RedisCommandKind::TsRevRange => "TS.REVRANGE", - RedisCommandKind::FtList => "FT._LIST", - RedisCommandKind::FtAggregate => "FT.AGGREGATE", - RedisCommandKind::FtSearch => "FT.SEARCH", - RedisCommandKind::FtCreate => "FT.CREATE", - RedisCommandKind::FtAlter => "FT.ALTER", - RedisCommandKind::FtAliasAdd => "FT.ALIASADD", - RedisCommandKind::FtAliasDel => "FT.ALIASDEL", - RedisCommandKind::FtAliasUpdate => "FT.ALIASUPDATE", - RedisCommandKind::FtConfigGet => "FT.CONFIG", - RedisCommandKind::FtConfigSet => "FT.CONFIG", - RedisCommandKind::FtCursorDel => "FT.CURSOR", - RedisCommandKind::FtCursorRead => "FT.CURSOR", - RedisCommandKind::FtDictAdd => "FT.DICTADD", - RedisCommandKind::FtDictDel => "FT.DICTDEL", - RedisCommandKind::FtDictDump => "FT.DICTDUMP", - RedisCommandKind::FtDropIndex => "FT.DROPINDEX", - RedisCommandKind::FtExplain => "FT.EXPLAIN", - RedisCommandKind::FtInfo => "FT.INFO", - RedisCommandKind::FtSpellCheck => "FT.SPELLCHECK", - RedisCommandKind::FtSugAdd => "FT.SUGADD", - RedisCommandKind::FtSugDel => "FT.SUGDEL", - RedisCommandKind::FtSugGet => "FT.SUGGET", - RedisCommandKind::FtSugLen => "FT.SUGLEN", - RedisCommandKind::FtSynDump => "FT.SYNDUMP", - RedisCommandKind::FtSynUpdate => "FT.SYNUPDATE", - RedisCommandKind::FtTagVals => "FT.TAGVALS", - RedisCommandKind::_Custom(ref kind) => return kind.cmd.clone(), - }; - - client_utils::static_str(s) - } - - /// Read the optional subcommand string for a command. - pub fn subcommand_str(&self) -> Option { - let s = match *self { - RedisCommandKind::ScriptDebug => "DEBUG", - RedisCommandKind::ScriptLoad => "LOAD", - RedisCommandKind::ScriptKill => "KILL", - RedisCommandKind::ScriptFlush => "FLUSH", - RedisCommandKind::ScriptExists => "EXISTS", - RedisCommandKind::_ScriptFlushCluster => "FLUSH", - RedisCommandKind::_ScriptLoadCluster => "LOAD", - RedisCommandKind::_ScriptKillCluster => "KILL", - RedisCommandKind::AclLoad => "LOAD", - RedisCommandKind::AclSave => "SAVE", - RedisCommandKind::AclList => "LIST", - RedisCommandKind::AclUsers => "USERS", - RedisCommandKind::AclGetUser => "GETUSER", - RedisCommandKind::AclSetUser => "SETUSER", - RedisCommandKind::AclDelUser => "DELUSER", - RedisCommandKind::AclCat => "CAT", - RedisCommandKind::AclGenPass => "GENPASS", - RedisCommandKind::AclWhoAmI => "WHOAMI", - RedisCommandKind::AclLog => "LOG", - RedisCommandKind::AclHelp => "HELP", - RedisCommandKind::ClusterAddSlots => "ADDSLOTS", - RedisCommandKind::ClusterCountFailureReports => "COUNT-FAILURE-REPORTS", - RedisCommandKind::ClusterCountKeysInSlot => "COUNTKEYSINSLOT", - RedisCommandKind::ClusterDelSlots => "DELSLOTS", - RedisCommandKind::ClusterFailOver => "FAILOVER", - RedisCommandKind::ClusterForget => "FORGET", - RedisCommandKind::ClusterGetKeysInSlot => "GETKEYSINSLOT", - RedisCommandKind::ClusterInfo => "INFO", - RedisCommandKind::ClusterKeySlot => "KEYSLOT", - RedisCommandKind::ClusterMeet => "MEET", - RedisCommandKind::ClusterNodes => "NODES", - RedisCommandKind::ClusterReplicate => "REPLICATE", - RedisCommandKind::ClusterReset => "RESET", - RedisCommandKind::ClusterSaveConfig => "SAVECONFIG", - RedisCommandKind::ClusterSetConfigEpoch => "SET-CONFIG-EPOCH", - RedisCommandKind::ClusterSetSlot => "SETSLOT", - RedisCommandKind::ClusterReplicas => "REPLICAS", - RedisCommandKind::ClusterSlots => "SLOTS", - RedisCommandKind::ClusterBumpEpoch => "BUMPEPOCH", - RedisCommandKind::ClusterFlushSlots => "FLUSHSLOTS", - RedisCommandKind::ClusterMyID => "MYID", - RedisCommandKind::ClientID => "ID", - RedisCommandKind::ClientInfo => "INFO", - RedisCommandKind::ClientKill => "KILL", - RedisCommandKind::ClientList => "LIST", - RedisCommandKind::ClientGetName => "GETNAME", - RedisCommandKind::ClientPause => "PAUSE", - RedisCommandKind::ClientUnpause => "UNPAUSE", - RedisCommandKind::ClientUnblock => "UNBLOCK", - RedisCommandKind::ClientReply => "REPLY", - RedisCommandKind::ClientSetname => "SETNAME", - RedisCommandKind::ConfigGet => "GET", - RedisCommandKind::ConfigRewrite => "REWRITE", - RedisCommandKind::ClientGetRedir => "GETREDIR", - RedisCommandKind::ClientTracking => "TRACKING", - RedisCommandKind::ClientTrackingInfo => "TRACKINGINFO", - RedisCommandKind::ClientCaching => "CACHING", - RedisCommandKind::ConfigSet => "SET", - RedisCommandKind::ConfigResetStat => "RESETSTAT", - RedisCommandKind::MemoryDoctor => "DOCTOR", - RedisCommandKind::MemoryHelp => "HELP", - RedisCommandKind::MemoryUsage => "USAGE", - RedisCommandKind::MemoryMallocStats => "MALLOC-STATS", - RedisCommandKind::MemoryStats => "STATS", - RedisCommandKind::MemoryPurge => "PURGE", - RedisCommandKind::XinfoConsumers => "CONSUMERS", - RedisCommandKind::XinfoGroups => "GROUPS", - RedisCommandKind::XinfoStream => "STREAM", - RedisCommandKind::Xgroupcreate => "CREATE", - RedisCommandKind::XgroupCreateConsumer => "CREATECONSUMER", - RedisCommandKind::XgroupDelConsumer => "DELCONSUMER", - RedisCommandKind::XgroupDestroy => "DESTROY", - RedisCommandKind::XgroupSetId => "SETID", - RedisCommandKind::FunctionDelete => "DELETE", - RedisCommandKind::FunctionDump => "DUMP", - RedisCommandKind::FunctionFlush => "FLUSH", - RedisCommandKind::FunctionKill => "KILL", - RedisCommandKind::FunctionList => "LIST", - RedisCommandKind::FunctionLoad => "LOAD", - RedisCommandKind::FunctionRestore => "RESTORE", - RedisCommandKind::FunctionStats => "STATS", - RedisCommandKind::PubsubChannels => "CHANNELS", - RedisCommandKind::PubsubNumpat => "NUMPAT", - RedisCommandKind::PubsubNumsub => "NUMSUB", - RedisCommandKind::PubsubShardchannels => "SHARDCHANNELS", - RedisCommandKind::PubsubShardnumsub => "SHARDNUMSUB", - RedisCommandKind::_FunctionLoadCluster => "LOAD", - RedisCommandKind::_FunctionFlushCluster => "FLUSH", - RedisCommandKind::_FunctionDeleteCluster => "DELETE", - RedisCommandKind::_FunctionRestoreCluster => "RESTORE", - RedisCommandKind::_ClientTrackingCluster => "TRACKING", - RedisCommandKind::JsonDebugMemory => "MEMORY", - RedisCommandKind::FtConfigGet => "GET", - RedisCommandKind::FtConfigSet => "SET", - RedisCommandKind::FtCursorDel => "DEL", - RedisCommandKind::FtCursorRead => "READ", - _ => return None, - }; - - Some(utils::static_str(s)) - } - - pub fn use_random_cluster_node(&self) -> bool { - matches!( - *self, - RedisCommandKind::Publish - | RedisCommandKind::Ping - | RedisCommandKind::Info - | RedisCommandKind::Scan - | RedisCommandKind::FlushAll - | RedisCommandKind::FlushDB - ) - } - - pub fn is_blocking(&self) -> bool { - match *self { - RedisCommandKind::BlPop - | RedisCommandKind::BrPop - | RedisCommandKind::BrPopLPush - | RedisCommandKind::BlMove - | RedisCommandKind::BzPopMin - | RedisCommandKind::BzPopMax - | RedisCommandKind::BlmPop - | RedisCommandKind::BzmPop - | RedisCommandKind::Fcall - | RedisCommandKind::FcallRO - | RedisCommandKind::Wait => true, - // default is false, but can be changed by the BLOCKING args. the RedisCommand::can_pipeline function checks the - // args too. - RedisCommandKind::Xread | RedisCommandKind::Xreadgroup => false, - RedisCommandKind::_Custom(ref kind) => kind.blocking, - _ => false, - } - } - - pub fn force_all_cluster_nodes(&self) -> bool { - matches!( - *self, - RedisCommandKind::_FlushAllCluster - | RedisCommandKind::_AuthAllCluster - | RedisCommandKind::_ScriptFlushCluster - | RedisCommandKind::_ScriptKillCluster - | RedisCommandKind::_HelloAllCluster(_) - | RedisCommandKind::_ClientTrackingCluster - | RedisCommandKind::_ScriptLoadCluster - | RedisCommandKind::_FunctionFlushCluster - | RedisCommandKind::_FunctionDeleteCluster - | RedisCommandKind::_FunctionRestoreCluster - | RedisCommandKind::_FunctionLoadCluster - ) - } - - pub fn should_flush(&self) -> bool { - matches!( - *self, - RedisCommandKind::Quit - | RedisCommandKind::Shutdown - | RedisCommandKind::Ping - | RedisCommandKind::Auth - | RedisCommandKind::_Hello(_) - | RedisCommandKind::Exec - | RedisCommandKind::Discard - | RedisCommandKind::Eval - | RedisCommandKind::EvalSha - | RedisCommandKind::Fcall - | RedisCommandKind::FcallRO - | RedisCommandKind::_Custom(_) - ) - } - - pub fn can_pipeline(&self) -> bool { - if self.is_blocking() || self.closes_connection() { - false - } else { - match self { - // make it easier to handle multiple potentially out-of-band responses - RedisCommandKind::Subscribe - | RedisCommandKind::Unsubscribe - | RedisCommandKind::Psubscribe - | RedisCommandKind::Punsubscribe - | RedisCommandKind::Ssubscribe - | RedisCommandKind::Sunsubscribe - // https://redis.io/commands/eval#evalsha-in-the-context-of-pipelining - | RedisCommandKind::Eval - | RedisCommandKind::EvalSha - | RedisCommandKind::Auth - | RedisCommandKind::Fcall - | RedisCommandKind::FcallRO - // makes it easier to avoid decoding in-flight responses with the wrong codec logic - | RedisCommandKind::_Hello(_) => false, - _ => true, - } - } - } - - pub fn is_eval(&self) -> bool { - matches!( - *self, - RedisCommandKind::EvalSha | RedisCommandKind::Eval | RedisCommandKind::Fcall | RedisCommandKind::FcallRO - ) - } -} - -pub struct RedisCommand { - /// The command and optional subcommand name. - pub kind: RedisCommandKind, - /// The policy to apply when handling the response. - pub response: ResponseKind, - /// The policy to use when hashing the arguments for cluster routing. - pub hasher: ClusterHash, - /// The provided arguments. - /// - /// Some commands store arguments differently. Callers should use `self.args()` to account for this. - pub arguments: Vec, - /// A oneshot sender used to communicate with the router. - pub router_tx: RefCount>>, - /// The number of times the command has been written to a socket. - pub write_attempts: u32, - /// The number of write attempts remaining. - pub attempts_remaining: u32, - /// The number of cluster redirections remaining. - pub redirections_remaining: u32, - /// Whether the command can be pipelined. - /// - /// Also used for commands like XREAD that block based on an argument. - pub can_pipeline: bool, - /// Whether to skip backpressure checks. - pub skip_backpressure: bool, - /// Whether to fail fast without retries if the connection ever closes unexpectedly. - pub fail_fast: bool, - /// The internal ID of a transaction. - pub transaction_id: Option, - /// The timeout duration provided by the `with_options` interface. - pub timeout_dur: Option, - /// Whether the command has timed out from the perspective of the caller. - pub timed_out: RefCount, - /// A timestamp of when the command was last written to the socket. - pub network_start: Option, - /// Whether to route the command to a replica, if possible. - pub use_replica: bool, - /// Only send the command to the provided server. - pub cluster_node: Option, - /// A timestamp of when the command was first created from the public interface. - #[cfg(feature = "metrics")] - pub created: Instant, - /// Tracing state that has to carry over across writer/reader tasks to track certain fields (response size, etc). - #[cfg(feature = "partial-tracing")] - pub traces: CommandTraces, - /// A counter to differentiate unique commands. - #[cfg(feature = "debug-ids")] - pub counter: usize, - /// Whether to send a `CLIENT CACHING yes|no` before the command. - #[cfg(feature = "i-tracking")] - pub caching: Option, -} - -impl fmt::Debug for RedisCommand { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut formatter = f.debug_struct("RedisCommand"); - formatter - .field("command", &self.kind.to_str_debug()) - .field("attempts_remaining", &self.attempts_remaining) - .field("redirections_remaining", &self.redirections_remaining) - .field("can_pipeline", &self.can_pipeline) - .field("write_attempts", &self.write_attempts) - .field("timeout_dur", &self.timeout_dur) - .field("no_backpressure", &self.skip_backpressure) - .field("cluster_node", &self.cluster_node) - .field("cluster_hash", &self.hasher) - .field("use_replica", &self.use_replica) - .field("fail_fast", &self.fail_fast); - - #[cfg(feature = "network-logs")] - formatter.field("arguments", &self.args()); - - formatter.finish() - } -} - -impl fmt::Display for RedisCommand { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.kind.to_str_debug()) - } -} - -impl From for RedisCommand { - fn from(kind: RedisCommandKind) -> Self { - (kind, Vec::new()).into() - } -} - -impl From<(RedisCommandKind, Vec)> for RedisCommand { - fn from((kind, arguments): (RedisCommandKind, Vec)) -> Self { - RedisCommand { - kind, - arguments, - timed_out: RefCount::new(AtomicBool::new(false)), - timeout_dur: None, - response: ResponseKind::Respond(None), - hasher: ClusterHash::default(), - router_tx: RefCount::new(Mutex::new(None)), - attempts_remaining: 0, - redirections_remaining: 0, - can_pipeline: true, - skip_backpressure: false, - transaction_id: None, - use_replica: false, - cluster_node: None, - network_start: None, - write_attempts: 0, - fail_fast: false, - #[cfg(feature = "metrics")] - created: Instant::now(), - #[cfg(feature = "partial-tracing")] - traces: CommandTraces::default(), - #[cfg(feature = "debug-ids")] - counter: command_counter(), - #[cfg(feature = "i-tracking")] - caching: None, - } - } -} - -impl From<(RedisCommandKind, Vec, ResponseSender)> for RedisCommand { - fn from((kind, arguments, tx): (RedisCommandKind, Vec, ResponseSender)) -> Self { - RedisCommand { - kind, - arguments, - response: ResponseKind::Respond(Some(tx)), - timed_out: RefCount::new(AtomicBool::new(false)), - timeout_dur: None, - hasher: ClusterHash::default(), - router_tx: RefCount::new(Mutex::new(None)), - attempts_remaining: 0, - redirections_remaining: 0, - can_pipeline: true, - skip_backpressure: false, - transaction_id: None, - use_replica: false, - cluster_node: None, - network_start: None, - write_attempts: 0, - fail_fast: false, - #[cfg(feature = "metrics")] - created: Instant::now(), - #[cfg(feature = "partial-tracing")] - traces: CommandTraces::default(), - #[cfg(feature = "debug-ids")] - counter: command_counter(), - #[cfg(feature = "i-tracking")] - caching: None, - } - } -} - -impl From<(RedisCommandKind, Vec, ResponseKind)> for RedisCommand { - fn from((kind, arguments, response): (RedisCommandKind, Vec, ResponseKind)) -> Self { - RedisCommand { - kind, - arguments, - response, - timed_out: RefCount::new(AtomicBool::new(false)), - timeout_dur: None, - hasher: ClusterHash::default(), - router_tx: RefCount::new(Mutex::new(None)), - attempts_remaining: 0, - redirections_remaining: 0, - can_pipeline: true, - skip_backpressure: false, - transaction_id: None, - use_replica: false, - cluster_node: None, - network_start: None, - write_attempts: 0, - fail_fast: false, - #[cfg(feature = "metrics")] - created: Instant::now(), - #[cfg(feature = "partial-tracing")] - traces: CommandTraces::default(), - #[cfg(feature = "debug-ids")] - counter: command_counter(), - #[cfg(feature = "i-tracking")] - caching: None, - } - } -} - -impl RedisCommand { - /// Create a new command without a response handling policy. - pub fn new(kind: RedisCommandKind, arguments: Vec) -> Self { - RedisCommand { - kind, - arguments, - timed_out: RefCount::new(AtomicBool::new(false)), - timeout_dur: None, - response: ResponseKind::Respond(None), - hasher: ClusterHash::default(), - router_tx: RefCount::new(Mutex::new(None)), - attempts_remaining: 0, - redirections_remaining: 0, - can_pipeline: true, - skip_backpressure: false, - transaction_id: None, - use_replica: false, - cluster_node: None, - network_start: None, - write_attempts: 0, - fail_fast: false, - #[cfg(feature = "metrics")] - created: Instant::now(), - #[cfg(feature = "partial-tracing")] - traces: CommandTraces::default(), - #[cfg(feature = "debug-ids")] - counter: command_counter(), - #[cfg(feature = "i-tracking")] - caching: None, - } - } - - /// Create a new empty `ASKING` command. - pub fn new_asking(hash_slot: u16) -> Self { - RedisCommand { - kind: RedisCommandKind::Asking, - hasher: ClusterHash::Custom(hash_slot), - arguments: Vec::new(), - timed_out: RefCount::new(AtomicBool::new(false)), - timeout_dur: None, - response: ResponseKind::Respond(None), - router_tx: RefCount::new(Mutex::new(None)), - attempts_remaining: 0, - redirections_remaining: 0, - can_pipeline: true, - skip_backpressure: false, - transaction_id: None, - use_replica: false, - cluster_node: None, - network_start: None, - write_attempts: 0, - fail_fast: false, - #[cfg(feature = "metrics")] - created: Instant::now(), - #[cfg(feature = "partial-tracing")] - traces: CommandTraces::default(), - #[cfg(feature = "debug-ids")] - counter: command_counter(), - #[cfg(feature = "i-tracking")] - caching: None, - } - } - - /// Whether to pipeline the command. - pub fn should_auto_pipeline(&self, inner: &RefCount, force: bool) -> bool { - let should_pipeline = force - || (inner.is_pipelined() - && self.can_pipeline - && self.kind.can_pipeline() - && !self.blocks_connection() - && !self.is_all_cluster_nodes() - // disable pipelining for transactions to handle ASK errors or support the `abort_on_error` logic - && self.transaction_id.is_none()); - - _trace!( - inner, - "Pipeline check {}: {}", - self.kind.to_str_debug(), - should_pipeline - ); - should_pipeline - } - - /// Whether the command should be sent to all cluster nodes concurrently. - pub fn is_all_cluster_nodes(&self) -> bool { - self.kind.force_all_cluster_nodes() - || match self.kind { - // since we don't know the hash slot we send this to all nodes - RedisCommandKind::Sunsubscribe => self.arguments.is_empty(), - _ => false, - } - } - - /// Whether errors writing the command should be returned to the caller. - pub fn should_finish_with_error(&self, inner: &RefCount) -> bool { - self.fail_fast || self.attempts_remaining == 0 || inner.policy.read().is_none() - } - - /// Increment and check the number of write attempts. - pub fn decr_check_attempted(&mut self) -> Result<(), RedisError> { - if self.attempts_remaining == 0 { - Err(RedisError::new( - RedisErrorKind::Unknown, - "Too many failed write attempts.", - )) - } else { - self.attempts_remaining -= 1; - Ok(()) - } - } - - pub fn decr_check_redirections(&mut self) -> Result<(), RedisError> { - if self.redirections_remaining == 0 { - Err(RedisError::new(RedisErrorKind::Unknown, "Too many redirections.")) - } else { - self.redirections_remaining -= 1; - Ok(()) - } - } - - /// Read the arguments associated with the command. - pub fn args(&self) -> &Vec { - match self.response { - ResponseKind::ValueScan(ref inner) => &inner.args, - ResponseKind::KeyScan(ref inner) => &inner.args, - _ => &self.arguments, - } - } - - /// Whether the command blocks the connection. - pub fn blocks_connection(&self) -> bool { - self.transaction_id.is_none() - && (self.kind.is_blocking() - || match self.kind { - RedisCommandKind::Xread | RedisCommandKind::Xreadgroup => !self.can_pipeline, - _ => false, - }) - } - - /// Whether the command may receive response frames. - /// - /// Currently, the pubsub subscription commands (other than `SSUBSCRIBE`) all fall into this category since their - /// responses arrive out-of-band. - // `SSUBSCRIBE` is not included here so that we can follow cluster redirections. this works as long as we never - // pipeline `SSUBSCRIBE`. - pub fn has_no_responses(&self) -> bool { - matches!( - self.kind, - RedisCommandKind::Subscribe - | RedisCommandKind::Unsubscribe - | RedisCommandKind::Psubscribe - | RedisCommandKind::Punsubscribe - | RedisCommandKind::Sunsubscribe - ) - } - - /// Take the arguments from this command. - pub fn take_args(&mut self) -> Vec { - match self.response { - ResponseKind::ValueScan(ref mut inner) => inner.args.drain(..).collect(), - ResponseKind::KeyScan(ref mut inner) => inner.args.drain(..).collect(), - _ => self.arguments.drain(..).collect(), - } - } - - /// Take the response handler, replacing it with `ResponseKind::Skip`. - pub fn take_response(&mut self) -> ResponseKind { - mem::replace(&mut self.response, ResponseKind::Skip) - } - - /// Create a channel on which to block the router, returning the receiver. - pub fn create_router_channel(&self) -> OneshotReceiver { - let (tx, rx) = oneshot_channel(); - let mut guard = self.router_tx.lock(); - *guard = Some(tx); - rx - } - - /// Send a message to unblock the router loop, if necessary. - pub fn respond_to_router(&self, inner: &RefCount, cmd: RouterResponse) { - #[allow(unused_mut)] - if let Some(mut tx) = self.router_tx.lock().take() { - if tx.send(cmd).is_err() { - _debug!(inner, "Failed to unblock router loop."); - } - } - } - - /// Take the router sender from the command. - pub fn take_router_tx(&self) -> Option { - self.router_tx.lock().take() - } - - /// Whether the command has a channel to the router. - pub fn has_router_channel(&self) -> bool { - self.router_tx.lock().is_some() - } - - /// Clone the command, supporting commands with shared response state. - /// - /// Note: this will **not** clone the router channel. - pub fn duplicate(&self, response: ResponseKind) -> Self { - RedisCommand { - timed_out: RefCount::new(AtomicBool::new(false)), - kind: self.kind.clone(), - arguments: self.arguments.clone(), - hasher: self.hasher.clone(), - transaction_id: self.transaction_id, - attempts_remaining: self.attempts_remaining, - redirections_remaining: self.redirections_remaining, - timeout_dur: self.timeout_dur, - can_pipeline: self.can_pipeline, - skip_backpressure: self.skip_backpressure, - router_tx: self.router_tx.clone(), - cluster_node: self.cluster_node.clone(), - fail_fast: self.fail_fast, - response, - use_replica: self.use_replica, - write_attempts: self.write_attempts, - network_start: self.network_start, - #[cfg(feature = "metrics")] - created: Instant::now(), - #[cfg(feature = "partial-tracing")] - traces: CommandTraces::default(), - #[cfg(feature = "debug-ids")] - counter: command_counter(), - #[cfg(feature = "i-tracking")] - caching: self.caching, - } - } - - /// Inherit connection and perf settings from the client. - pub fn inherit_options(&mut self, inner: &RefCount) { - if self.attempts_remaining == 0 { - self.attempts_remaining = inner.connection.max_command_attempts; - } - if self.redirections_remaining == 0 { - self.redirections_remaining = inner.connection.max_redirections; - } - if self.timeout_dur.is_none() { - let default_dur = inner.default_command_timeout(); - if !default_dur.is_zero() { - self.timeout_dur = Some(default_dur); - } - } - } - - /// Take the command tracing state for the `queued` span. - #[cfg(feature = "full-tracing")] - pub fn take_queued_span(&mut self) -> Option { - self.traces.queued.take() - } - - /// Take the command tracing state for the `queued` span. - #[cfg(not(feature = "full-tracing"))] - pub fn take_queued_span(&mut self) -> Option { - None - } - - /// Take the response sender from the command. - /// - /// Usually used for responding early without sending the command. - pub fn take_responder(&mut self) -> Option { - match self.response { - ResponseKind::Respond(ref mut tx) => tx.take(), - ResponseKind::Buffer { ref mut tx, .. } => tx.lock().take(), - _ => None, - } - } - - /// Whether the command has a channel for sending responses to the caller. - pub fn has_response_tx(&self) -> bool { - match self.response { - ResponseKind::Respond(ref r) => r.is_some(), - ResponseKind::Buffer { ref tx, .. } => tx.lock().is_some(), - _ => false, - } - } - - /// Respond to the caller, taking the response channel in the process. - pub fn respond_to_caller(&mut self, result: Result) { - #[allow(unused_mut)] - if let Some(mut tx) = self.take_responder() { - let _ = tx.send(result); - } - } - - /// Finish the command, responding to both the caller and router. - pub fn finish(mut self, inner: &RefCount, result: Result) { - self.respond_to_caller(result); - self.respond_to_router(inner, RouterResponse::Continue); - } - - /// Read the first key in the arguments according to the `FirstKey` cluster hash policy. - pub fn first_key(&self) -> Option<&[u8]> { - ClusterHash::FirstKey.find_key(self.args()) - } - - /// Hash the arguments according to the command's cluster hash policy. - pub fn cluster_hash(&self) -> Option { - self - .kind - .custom_hash_slot() - .or(self.scan_hash_slot()) - .or(self.hasher.hash(self.args())) - } - - /// Read the custom hash slot assigned to a scan operation. - pub fn scan_hash_slot(&self) -> Option { - match self.response { - ResponseKind::KeyScan(ref inner) => inner.hash_slot, - _ => None, - } - } - - /// Convert to a single frame with an array of bulk strings (or null). - pub fn to_frame(&self, is_resp3: bool) -> Result { - protocol_utils::command_to_frame(self, is_resp3) - } - - /// Convert to a single frame with an array of bulk strings (or null), using a blocking task. - #[cfg(all(feature = "blocking-encoding", not(feature = "glommio")))] - pub fn to_frame_blocking(&self, is_resp3: bool, blocking_threshold: usize) -> Result { - let cmd_size = protocol_utils::args_size(self.args()); - - if cmd_size >= blocking_threshold { - trace!("Using blocking task to convert command to frame with size {}", cmd_size); - tokio::task::block_in_place(|| protocol_utils::command_to_frame(self, is_resp3)) - } else { - protocol_utils::command_to_frame(self, is_resp3) - } - } - - #[cfg(feature = "mocks")] - pub fn to_mocked(&self) -> MockCommand { - MockCommand { - cmd: self.kind.cmd_str(), - subcommand: self.kind.subcommand_str(), - args: self.args().clone(), - } - } - - #[cfg(not(feature = "debug-ids"))] - pub fn debug_id(&self) -> usize { - 0 - } - - #[cfg(feature = "debug-ids")] - pub fn debug_id(&self) -> usize { - self.counter - } -} - -/// A message sent from the front-end client to the router. -pub enum RouterCommand { - /// Send a command to the server. - Command(RedisCommand), - /// Send a pipelined series of commands to the server. - Pipeline { commands: Vec }, - /// Send a transaction to the server. - // Notes: - // * The inner command buffer will not contain the trailing `EXEC` command. - // * Transactions are never pipelined in order to handle ASK responses. - // * IDs must be unique w/r/t other transactions buffered in memory. - // - // There is one special failure mode that must be considered: - // 1. The client sends `MULTI` and we receive an `OK` response. - // 2. The caller sends `GET foo{1}` and we receive a `QUEUED` response. - // 3. The caller sends `GET bar{1}` and we receive an `ASK` response. - // - // According to the cluster spec the client should retry the entire transaction against the node in the `ASK` - // response, but with an `ASKING` command before `MULTI`. However, the future returned to the caller from `GET - // foo{1}` will have already finished at this point. To account for this the client will never pipeline - // transactions against a cluster, and may clone commands before sending them in order to replay them later with - // a different cluster node mapping. - #[cfg(feature = "transactions")] - Transaction { - id: u64, - commands: Vec, - watched: Option, - abort_on_error: bool, - tx: ResponseSender, - }, - /// Retry a command after a `MOVED` error. - // This will trigger a call to `CLUSTER SLOTS` before the command is retried. - Moved { - slot: u16, - server: Server, - command: RedisCommand, - }, - /// Retry a command after an `ASK` error. - // This is typically used instead of `RouterResponse::Ask` when a command was pipelined. - Ask { - slot: u16, - server: Server, - command: RedisCommand, - }, - /// Initiate a reconnection to the provided server, or all servers. - // The client may not perform a reconnection if a healthy connection exists to `server`, unless `force` is `true`. - Reconnect { - server: Option, - force: bool, - tx: Option, - #[cfg(feature = "replicas")] - replica: bool, - }, - /// Sync the cached cluster state with the server via `CLUSTER SLOTS`. - SyncCluster { tx: OneshotSender> }, - /// Read the set of active connections managed by the client. - Connections { tx: OneshotSender> }, - /// Force sync the replica routing table with the server(s). - #[cfg(feature = "replicas")] - SyncReplicas { - tx: OneshotSender>, - reset: bool, - }, -} - -impl RouterCommand { - /// Whether the client should skip backpressure on the command buffer when sending this command. - pub fn should_skip_backpressure(&self) -> bool { - matches!( - *self, - RouterCommand::Moved { .. } - | RouterCommand::Ask { .. } - | RouterCommand::SyncCluster { .. } - | RouterCommand::Connections { .. } - ) - } - - /// Whether the command should check the health of the backing connections before being used. - pub fn should_check_fail_fast(&self) -> bool { - match self { - RouterCommand::Command(command) => command.fail_fast, - RouterCommand::Pipeline { commands, .. } => commands.first().map(|c| c.fail_fast).unwrap_or(false), - #[cfg(feature = "transactions")] - RouterCommand::Transaction { commands, .. } => commands.first().map(|c| c.fail_fast).unwrap_or(false), - _ => false, - } - } - - /// Finish the command early with the provided error. - #[allow(unused_mut)] - pub fn finish_with_error(self, error: RedisError) { - match self { - RouterCommand::Command(mut command) => { - command.respond_to_caller(Err(error)); - }, - RouterCommand::Pipeline { commands } => { - for mut command in commands.into_iter() { - command.respond_to_caller(Err(error.clone())); - } - }, - #[cfg(feature = "transactions")] - RouterCommand::Transaction { mut tx, .. } => { - if let Err(_) = tx.send(Err(error)) { - warn!("Error responding early to transaction."); - } - }, - RouterCommand::Reconnect { tx: Some(mut tx), .. } => { - if let Err(_) = tx.send(Err(error)) { - warn!("Error responding early to reconnect command."); - } - }, - _ => {}, - } - } - - /// Inherit settings from the configuration structs on `inner`. - pub fn inherit_options(&mut self, inner: &RefCount) { - match self { - RouterCommand::Command(ref mut cmd) => { - cmd.inherit_options(inner); - }, - RouterCommand::Pipeline { ref mut commands, .. } => { - for cmd in commands.iter_mut() { - cmd.inherit_options(inner); - } - }, - #[cfg(feature = "transactions")] - RouterCommand::Transaction { ref mut commands, .. } => { - for cmd in commands.iter_mut() { - cmd.inherit_options(inner); - } - }, - _ => {}, - }; - } - - /// Apply a timeout to the response channel receiver based on the command and `inner` context. - pub fn timeout_dur(&self) -> Option { - match self { - RouterCommand::Command(ref command) => command.timeout_dur, - RouterCommand::Pipeline { ref commands, .. } => commands.first().and_then(|c| c.timeout_dur), - #[cfg(feature = "transactions")] - RouterCommand::Transaction { ref commands, .. } => commands.first().and_then(|c| c.timeout_dur), - _ => None, - } - } -} - -impl fmt::Debug for RouterCommand { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut formatter = f.debug_struct("RouterCommand"); - - match self { - RouterCommand::Ask { server, slot, command } => { - formatter - .field("kind", &"Ask") - .field("server", &server) - .field("slot", &slot) - .field("command", &command.kind.to_str_debug()); - }, - RouterCommand::Moved { server, slot, command } => { - formatter - .field("kind", &"Moved") - .field("server", &server) - .field("slot", &slot) - .field("command", &command.kind.to_str_debug()); - }, - #[cfg(not(feature = "replicas"))] - RouterCommand::Reconnect { server, force, .. } => { - formatter - .field("kind", &"Reconnect") - .field("server", &server) - .field("force", &force); - }, - #[cfg(feature = "replicas")] - RouterCommand::Reconnect { - server, force, replica, .. - } => { - formatter - .field("kind", &"Reconnect") - .field("server", &server) - .field("replica", &replica) - .field("force", &force); - }, - RouterCommand::SyncCluster { .. } => { - formatter.field("kind", &"Sync Cluster"); - }, - #[cfg(feature = "transactions")] - RouterCommand::Transaction { .. } => { - formatter.field("kind", &"Transaction"); - }, - RouterCommand::Pipeline { .. } => { - formatter.field("kind", &"Pipeline"); - }, - RouterCommand::Connections { .. } => { - formatter.field("kind", &"Connections"); - }, - RouterCommand::Command(command) => { - formatter - .field("kind", &"Command") - .field("command", &command.kind.to_str_debug()); - }, - #[cfg(feature = "replicas")] - RouterCommand::SyncReplicas { reset, .. } => { - formatter.field("kind", &"Sync Replicas"); - formatter.field("reset", &reset); - }, - }; - - formatter.finish() - } -} - -impl From for RouterCommand { - fn from(cmd: RedisCommand) -> Self { - RouterCommand::Command(cmd) - } -} diff --git a/src/protocol/connection.rs b/src/protocol/connection.rs deleted file mode 100644 index c44fc8bd..00000000 --- a/src/protocol/connection.rs +++ /dev/null @@ -1,1213 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - modules::inner::RedisClientInner, - protocol::{ - codec::RedisCodec, - command::{RedisCommand, RedisCommandKind, RouterResponse}, - types::{ProtocolFrame, Server}, - utils as protocol_utils, - }, - runtime::{AtomicBool, AtomicUsize, JoinHandle, RefCount}, - types::InfoKind, - utils as client_utils, - utils, -}; -use bytes_utils::Str; -use crossbeam_queue::SegQueue; -use futures::{ - sink::SinkExt, - stream::{SplitSink, SplitStream, StreamExt}, - Sink, - Stream, -}; -use redis_protocol::resp3::types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame, RespVersion}; -use semver::Version; -use std::{ - fmt, - net::SocketAddr, - pin::Pin, - str, - task::{Context, Poll}, - time::Duration, -}; -use tokio_util::codec::Framed; - -#[cfg(not(feature = "glommio"))] -use socket2::SockRef; - -#[cfg(feature = "glommio")] -use glommio::net::TcpStream as BaseTcpStream; -#[cfg(feature = "glommio")] -pub type TcpStream = crate::glommio::io_compat::TokioIO; - -#[cfg(not(feature = "glommio"))] -use tokio::net::TcpStream; -#[cfg(not(feature = "glommio"))] -use tokio::net::TcpStream as BaseTcpStream; - -#[cfg(feature = "unix-sockets")] -use crate::prelude::ServerConfig; -#[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" -))] -use crate::protocol::tls::TlsConnector; -#[cfg(feature = "replicas")] -use crate::runtime::oneshot_channel; -#[cfg(feature = "replicas")] -use crate::{protocol::responders::ResponseKind, types::RedisValue}; -#[cfg(feature = "unix-sockets")] -use std::path::Path; -#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] -use std::{convert::TryInto, ops::Deref}; -#[cfg(feature = "unix-sockets")] -use tokio::net::UnixStream; -#[cfg(feature = "enable-native-tls")] -use tokio_native_tls::TlsStream as NativeTlsStream; -#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] -use tokio_rustls::client::TlsStream as RustlsStream; - -/// The contents of a simplestring OK response. -pub const OK: &str = "OK"; -/// The timeout duration used when dropping the split sink and waiting on the split stream to close. -pub const CONNECTION_CLOSE_TIMEOUT_MS: u64 = 5_000; - -pub type CommandBuffer = Vec; - -/// A shared buffer across tasks. -#[derive(Clone, Debug)] -pub struct SharedBuffer { - inner: RefCount>, - blocked: RefCount, -} - -impl SharedBuffer { - pub fn new() -> Self { - SharedBuffer { - inner: RefCount::new(SegQueue::new()), - blocked: RefCount::new(AtomicBool::new(false)), - } - } - - pub fn push(&self, cmd: RedisCommand) { - self.inner.push(cmd); - } - - pub fn pop(&self) -> Option { - self.inner.pop() - } - - pub fn len(&self) -> usize { - self.inner.len() - } - - pub fn set_blocked(&self) { - utils::set_bool_atomic(&self.blocked, true); - } - - pub fn set_unblocked(&self) { - utils::set_bool_atomic(&self.blocked, false); - } - - pub fn is_blocked(&self) -> bool { - utils::read_bool_atomic(&self.blocked) - } - - pub fn drain(&self) -> Vec { - utils::set_bool_atomic(&self.blocked, false); - let mut out = Vec::with_capacity(self.inner.len()); - while let Some(cmd) = self.inner.pop() { - out.push(cmd); - } - out - } -} - -pub type SplitRedisSink = SplitSink, ProtocolFrame>; -pub type SplitRedisStream = SplitStream>; - -/// Connect to each socket addr and return the first successful connection. -async fn tcp_connect_any( - inner: &RefCount, - server: &Server, - addrs: &Vec, -) -> Result<(TcpStream, SocketAddr), RedisError> { - let mut last_error: Option = None; - - for addr in addrs.iter() { - _debug!( - inner, - "Creating TCP connection to {} at {}:{}", - server.host, - addr.ip(), - addr.port() - ); - let socket = match BaseTcpStream::connect(addr).await { - Ok(socket) => socket, - Err(e) => { - _debug!(inner, "Error connecting to {}: {:?}", addr, e); - last_error = Some(e.into()); - continue; - }, - }; - if let Some(val) = inner.connection.tcp.nodelay { - socket.set_nodelay(val)?; - } - if let Some(_dur) = inner.connection.tcp.linger { - #[cfg(not(feature = "glommio"))] - socket.set_linger(Some(_dur))?; - #[cfg(feature = "glommio")] - _warn!(inner, "TCP Linger is not yet supported with Glommio features."); - } - if let Some(ttl) = inner.connection.tcp.ttl { - socket.set_ttl(ttl)?; - } - if let Some(ref _keepalive) = inner.connection.tcp.keepalive { - #[cfg(not(feature = "glommio"))] - SockRef::from(&socket).set_tcp_keepalive(_keepalive)?; - #[cfg(feature = "glommio")] - _warn!(inner, "TCP keepalive is not yet supported with Glommio features."); - } - - #[cfg(feature = "glommio")] - let socket = crate::glommio::io_compat::TokioIO(socket); - return Ok((socket, *addr)); - } - - _trace!(inner, "Failed to connect to any of {:?}.", addrs); - Err(last_error.unwrap_or(RedisError::new(RedisErrorKind::IO, "Failed to connect."))) -} - -pub enum ConnectionKind { - Tcp(Framed), - #[cfg(feature = "unix-sockets")] - Unix(Framed), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - Rustls(Framed, RedisCodec>), - #[cfg(feature = "enable-native-tls")] - NativeTls(Framed, RedisCodec>), -} - -impl ConnectionKind { - /// Split the connection. - pub fn split(self) -> (SplitSinkKind, SplitStreamKind) { - match self { - ConnectionKind::Tcp(conn) => { - let (sink, stream) = conn.split(); - (SplitSinkKind::Tcp(sink), SplitStreamKind::Tcp(stream)) - }, - #[cfg(feature = "unix-sockets")] - ConnectionKind::Unix(conn) => { - let (sink, stream) = conn.split(); - (SplitSinkKind::Unix(sink), SplitStreamKind::Unix(stream)) - }, - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - ConnectionKind::Rustls(conn) => { - let (sink, stream) = conn.split(); - (SplitSinkKind::Rustls(sink), SplitStreamKind::Rustls(stream)) - }, - #[cfg(feature = "enable-native-tls")] - ConnectionKind::NativeTls(conn) => { - let (sink, stream) = conn.split(); - (SplitSinkKind::NativeTls(sink), SplitStreamKind::NativeTls(stream)) - }, - } - } -} - -impl Stream for ConnectionKind { - type Item = Result; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.get_mut() { - ConnectionKind::Tcp(ref mut conn) => Pin::new(conn).poll_next(cx), - #[cfg(feature = "unix-sockets")] - ConnectionKind::Unix(ref mut conn) => Pin::new(conn).poll_next(cx), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - ConnectionKind::Rustls(ref mut conn) => Pin::new(conn).poll_next(cx), - #[cfg(feature = "enable-native-tls")] - ConnectionKind::NativeTls(ref mut conn) => Pin::new(conn).poll_next(cx), - } - } - - fn size_hint(&self) -> (usize, Option) { - match self { - ConnectionKind::Tcp(ref conn) => conn.size_hint(), - #[cfg(feature = "unix-sockets")] - ConnectionKind::Unix(ref conn) => conn.size_hint(), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - ConnectionKind::Rustls(ref conn) => conn.size_hint(), - #[cfg(feature = "enable-native-tls")] - ConnectionKind::NativeTls(ref conn) => conn.size_hint(), - } - } -} - -impl Sink for ConnectionKind { - type Error = RedisError; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.get_mut() { - ConnectionKind::Tcp(ref mut conn) => Pin::new(conn).poll_ready(cx), - #[cfg(feature = "unix-sockets")] - ConnectionKind::Unix(ref mut conn) => Pin::new(conn).poll_ready(cx), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - ConnectionKind::Rustls(ref mut conn) => Pin::new(conn).poll_ready(cx), - #[cfg(feature = "enable-native-tls")] - ConnectionKind::NativeTls(ref mut conn) => Pin::new(conn).poll_ready(cx), - } - } - - fn start_send(self: Pin<&mut Self>, item: ProtocolFrame) -> Result<(), Self::Error> { - match self.get_mut() { - ConnectionKind::Tcp(ref mut conn) => Pin::new(conn).start_send(item), - #[cfg(feature = "unix-sockets")] - ConnectionKind::Unix(ref mut conn) => Pin::new(conn).start_send(item), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - ConnectionKind::Rustls(ref mut conn) => Pin::new(conn).start_send(item), - #[cfg(feature = "enable-native-tls")] - ConnectionKind::NativeTls(ref mut conn) => Pin::new(conn).start_send(item), - } - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.get_mut() { - ConnectionKind::Tcp(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e), - #[cfg(feature = "unix-sockets")] - ConnectionKind::Unix(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - ConnectionKind::Rustls(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e), - #[cfg(feature = "enable-native-tls")] - ConnectionKind::NativeTls(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e), - } - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.get_mut() { - ConnectionKind::Tcp(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e), - #[cfg(feature = "unix-sockets")] - ConnectionKind::Unix(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - ConnectionKind::Rustls(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e), - #[cfg(feature = "enable-native-tls")] - ConnectionKind::NativeTls(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e), - } - } -} - -pub enum SplitStreamKind { - Tcp(SplitRedisStream), - #[cfg(feature = "unix-sockets")] - Unix(SplitRedisStream), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - Rustls(SplitRedisStream>), - #[cfg(feature = "enable-native-tls")] - NativeTls(SplitRedisStream>), -} - -impl Stream for SplitStreamKind { - type Item = Result; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.get_mut() { - SplitStreamKind::Tcp(ref mut conn) => Pin::new(conn).poll_next(cx), - #[cfg(feature = "unix-sockets")] - SplitStreamKind::Unix(ref mut conn) => Pin::new(conn).poll_next(cx), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - SplitStreamKind::Rustls(ref mut conn) => Pin::new(conn).poll_next(cx), - #[cfg(feature = "enable-native-tls")] - SplitStreamKind::NativeTls(ref mut conn) => Pin::new(conn).poll_next(cx), - } - } - - fn size_hint(&self) -> (usize, Option) { - match self { - SplitStreamKind::Tcp(ref conn) => conn.size_hint(), - #[cfg(feature = "unix-sockets")] - SplitStreamKind::Unix(ref conn) => conn.size_hint(), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - SplitStreamKind::Rustls(ref conn) => conn.size_hint(), - #[cfg(feature = "enable-native-tls")] - SplitStreamKind::NativeTls(ref conn) => conn.size_hint(), - } - } -} - -pub enum SplitSinkKind { - Tcp(SplitRedisSink), - #[cfg(feature = "unix-sockets")] - Unix(SplitRedisSink), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - Rustls(SplitRedisSink>), - #[cfg(feature = "enable-native-tls")] - NativeTls(SplitRedisSink>), -} - -impl Sink for SplitSinkKind { - type Error = RedisError; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.get_mut() { - SplitSinkKind::Tcp(ref mut conn) => Pin::new(conn).poll_ready(cx), - #[cfg(feature = "unix-sockets")] - SplitSinkKind::Unix(ref mut conn) => Pin::new(conn).poll_ready(cx), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - SplitSinkKind::Rustls(ref mut conn) => Pin::new(conn).poll_ready(cx), - #[cfg(feature = "enable-native-tls")] - SplitSinkKind::NativeTls(ref mut conn) => Pin::new(conn).poll_ready(cx), - } - } - - fn start_send(self: Pin<&mut Self>, item: ProtocolFrame) -> Result<(), Self::Error> { - match self.get_mut() { - SplitSinkKind::Tcp(ref mut conn) => Pin::new(conn).start_send(item), - #[cfg(feature = "unix-sockets")] - SplitSinkKind::Unix(ref mut conn) => Pin::new(conn).start_send(item), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - SplitSinkKind::Rustls(ref mut conn) => Pin::new(conn).start_send(item), - #[cfg(feature = "enable-native-tls")] - SplitSinkKind::NativeTls(ref mut conn) => Pin::new(conn).start_send(item), - } - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.get_mut() { - SplitSinkKind::Tcp(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e), - #[cfg(feature = "unix-sockets")] - SplitSinkKind::Unix(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - SplitSinkKind::Rustls(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e), - #[cfg(feature = "enable-native-tls")] - SplitSinkKind::NativeTls(ref mut conn) => Pin::new(conn).poll_flush(cx).map_err(|e| e), - } - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.get_mut() { - SplitSinkKind::Tcp(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e), - #[cfg(feature = "unix-sockets")] - SplitSinkKind::Unix(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - SplitSinkKind::Rustls(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e), - #[cfg(feature = "enable-native-tls")] - SplitSinkKind::NativeTls(ref mut conn) => Pin::new(conn).poll_close(cx).map_err(|e| e), - } - } -} - -/// Atomic counters stored with connection state. -// TODO with glommio these don't need to be atomics -#[derive(Clone, Debug)] -pub struct Counters { - pub cmd_buffer_len: RefCount, - pub in_flight: RefCount, - pub feed_count: RefCount, -} - -impl Counters { - pub fn new(cmd_buffer_len: &RefCount) -> Self { - Counters { - cmd_buffer_len: cmd_buffer_len.clone(), - in_flight: RefCount::new(AtomicUsize::new(0)), - feed_count: RefCount::new(AtomicUsize::new(0)), - } - } - - /// Flush the sink if the max feed count is reached or no commands are queued following the current command. - pub fn should_send(&self, inner: &RefCount) -> bool { - client_utils::read_atomic(&self.feed_count) as u64 > inner.max_feed_count() - || client_utils::read_atomic(&self.cmd_buffer_len) == 0 - } - - pub fn incr_feed_count(&self) -> usize { - client_utils::incr_atomic(&self.feed_count) - } - - pub fn incr_in_flight(&self) -> usize { - client_utils::incr_atomic(&self.in_flight) - } - - pub fn decr_in_flight(&self) -> usize { - client_utils::decr_atomic(&self.in_flight) - } - - pub fn reset_feed_count(&self) { - client_utils::set_atomic(&self.feed_count, 0); - } - - pub fn reset_in_flight(&self) { - client_utils::set_atomic(&self.in_flight, 0); - } -} - -pub struct RedisTransport { - /// An identifier for the connection, usually `|:`. - pub server: Server, - /// The parsed `SocketAddr` for the connection. - pub addr: Option, - /// The hostname used to initialize the connection. - pub default_host: Str, - /// The network connection. - pub transport: ConnectionKind, - /// The connection/client ID from the CLIENT ID command. - pub id: Option, - /// The server version. - pub version: Option, - /// Counters for the connection state. - pub counters: Counters, -} - -impl RedisTransport { - pub async fn new_tcp(inner: &RefCount, server: &Server) -> Result { - let counters = Counters::new(&inner.counters.cmd_buffer_len); - let (id, version) = (None, None); - let default_host = server.host.clone(); - let codec = RedisCodec::new(inner, server); - let addrs = inner - .get_resolver() - .await - .resolve(server.host.clone(), server.port) - .await?; - let (socket, addr) = tcp_connect_any(inner, server, &addrs).await?; - let transport = ConnectionKind::Tcp(Framed::new(socket, codec)); - - Ok(RedisTransport { - server: server.clone(), - addr: Some(addr), - default_host, - counters, - id, - version, - transport, - }) - } - - #[cfg(feature = "unix-sockets")] - pub async fn new_unix(inner: &RefCount, path: &Path) -> Result { - _debug!(inner, "Connecting via unix socket to {}", utils::path_to_string(path)); - let server = Server::new(utils::path_to_string(path), 0); - let counters = Counters::new(&inner.counters.cmd_buffer_len); - let (id, version) = (None, None); - let default_host = server.host.clone(); - let codec = RedisCodec::new(inner, &server); - let socket = UnixStream::connect(path).await?; - let transport = ConnectionKind::Unix(Framed::new(socket, codec)); - - Ok(RedisTransport { - addr: None, - server, - default_host, - counters, - id, - version, - transport, - }) - } - - #[cfg(feature = "enable-native-tls")] - #[allow(unreachable_patterns)] - pub async fn new_native_tls( - inner: &RefCount, - server: &Server, - ) -> Result { - let connector = match inner.config.tls { - Some(ref config) => match config.connector { - TlsConnector::Native(ref connector) => connector.clone(), - _ => return Err(RedisError::new(RedisErrorKind::Tls, "Invalid TLS configuration.")), - }, - None => return RedisTransport::new_tcp(inner, server).await, - }; - - let counters = Counters::new(&inner.counters.cmd_buffer_len); - let (id, version) = (None, None); - let tls_server_name = server.tls_server_name.as_ref().cloned().unwrap_or(server.host.clone()); - - let default_host = server.host.clone(); - let codec = RedisCodec::new(inner, server); - let addrs = inner - .get_resolver() - .await - .resolve(server.host.clone(), server.port) - .await?; - let (socket, addr) = tcp_connect_any(inner, server, &addrs).await?; - - _debug!(inner, "native-tls handshake with server name/host: {}", tls_server_name); - let socket = connector.clone().connect(&tls_server_name, socket).await?; - let transport = ConnectionKind::NativeTls(Framed::new(socket, codec)); - - Ok(RedisTransport { - server: server.clone(), - addr: Some(addr), - default_host, - counters, - id, - version, - transport, - }) - } - - #[cfg(not(feature = "enable-native-tls"))] - pub async fn new_native_tls( - inner: &RefCount, - server: &Server, - ) -> Result { - RedisTransport::new_tcp(inner, server).await - } - - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - #[allow(unreachable_patterns)] - pub async fn new_rustls(inner: &RefCount, server: &Server) -> Result { - use rustls::pki_types::ServerName; - - let connector = match inner.config.tls { - Some(ref config) => match config.connector { - TlsConnector::Rustls(ref connector) => connector.clone(), - _ => return Err(RedisError::new(RedisErrorKind::Tls, "Invalid TLS configuration.")), - }, - None => return RedisTransport::new_tcp(inner, server).await, - }; - - let counters = Counters::new(&inner.counters.cmd_buffer_len); - let (id, version) = (None, None); - let tls_server_name = server.tls_server_name.as_ref().cloned().unwrap_or(server.host.clone()); - - let default_host = server.host.clone(); - let codec = RedisCodec::new(inner, server); - let addrs = inner - .get_resolver() - .await - .resolve(server.host.clone(), server.port) - .await?; - let (socket, addr) = tcp_connect_any(inner, server, &addrs).await?; - let server_name: ServerName = tls_server_name.deref().try_into()?; - - _debug!(inner, "rustls handshake with server name/host: {:?}", tls_server_name); - let socket = connector.clone().connect(server_name.to_owned(), socket).await?; - let transport = ConnectionKind::Rustls(Framed::new(socket, codec)); - - Ok(RedisTransport { - server: server.clone(), - addr: Some(addr), - counters, - default_host, - id, - version, - transport, - }) - } - - #[cfg(not(any(feature = "enable-rustls", feature = "enable-rustls-ring")))] - pub async fn new_rustls(inner: &RefCount, server: &Server) -> Result { - RedisTransport::new_tcp(inner, server).await - } - - /// Send a command to the server. - pub async fn request_response(&mut self, cmd: RedisCommand, is_resp3: bool) -> Result { - let frame = cmd.to_frame(is_resp3)?; - self.transport.send(frame).await?; - - match self.transport.next().await { - Some(result) => result.map(|f| f.into_resp3()), - None => Ok(Resp3Frame::Null), - } - } - - /// Set the client name with `CLIENT SETNAME`. - pub async fn set_client_name(&mut self, inner: &RefCount) -> Result<(), RedisError> { - _debug!(inner, "Setting client name."); - let name = &inner.id; - let command = RedisCommand::new(RedisCommandKind::ClientSetname, vec![name.clone().into()]); - let response = self.request_response(command, inner.is_resp3()).await?; - - if protocol_utils::is_ok(&response) { - debug!("{}: Successfully set Redis client name.", name); - Ok(()) - } else { - error!("{} Failed to set client name with error {:?}", name, response); - Err(RedisError::new(RedisErrorKind::Protocol, "Failed to set client name.")) - } - } - - /// Read and cache the server version. - pub async fn cache_server_version(&mut self, inner: &RefCount) -> Result<(), RedisError> { - let command = RedisCommand::new(RedisCommandKind::Info, vec![InfoKind::Server.to_str().into()]); - let result = self.request_response(command, inner.is_resp3()).await?; - let result = match result { - Resp3Frame::SimpleString { data, .. } => String::from_utf8(data.to_vec())?, - Resp3Frame::BlobString { data, .. } | Resp3Frame::VerbatimString { data, .. } => { - String::from_utf8(data.to_vec())? - }, - Resp3Frame::SimpleError { data, .. } => { - _warn!(inner, "Failed to read server version: {:?}", data); - return Ok(()); - }, - Resp3Frame::BlobError { data, .. } => { - let parsed = String::from_utf8_lossy(&data); - _warn!(inner, "Failed to read server version: {:?}", parsed); - return Ok(()); - }, - _ => { - _warn!(inner, "Invalid INFO response: {:?}", result.kind()); - return Ok(()); - }, - }; - - self.version = result.lines().find_map(|line| { - let parts: Vec<&str> = line.split(':').collect(); - if parts.len() < 2 { - return None; - } - - if parts[0] == "redis_version" { - Version::parse(parts[1]).ok() - } else { - None - } - }); - - _debug!(inner, "Read server version {:?}", self.version); - Ok(()) - } - - /// Authenticate via AUTH, then set the client name. - pub async fn authenticate( - &mut self, - name: &str, - username: Option, - password: Option, - is_resp3: bool, - ) -> Result<(), RedisError> { - if let Some(password) = password { - let args = if let Some(username) = username { - vec![username.into(), password.into()] - } else { - vec![password.into()] - }; - let command = RedisCommand::new(RedisCommandKind::Auth, args); - - debug!("{}: Authenticating Redis client...", name); - let frame = self.request_response(command, is_resp3).await?; - - if !protocol_utils::is_ok(&frame) { - let error = protocol_utils::frame_into_string(frame)?; - return Err(protocol_utils::pretty_error(&error)); - } - } else { - trace!("{}: Skip authentication without credentials.", name); - } - - Ok(()) - } - - /// Authenticate via HELLO in RESP3 mode or AUTH in RESP2 mode, then set the client name. - pub async fn switch_protocols_and_authenticate( - &mut self, - inner: &RefCount, - ) -> Result<(), RedisError> { - // reset the protocol version to the one specified by the config when we create new connections - inner.reset_protocol_version(); - let username = inner.config.username.clone(); - let password = inner.config.password.clone(); - - if inner.is_resp3() { - _debug!(inner, "Switching to RESP3 protocol with HELLO..."); - let args = if let Some(password) = password { - if let Some(username) = username { - vec![username.into(), password.into()] - } else { - vec!["default".into(), password.into()] - } - } else { - vec![] - }; - - let cmd = RedisCommand::new(RedisCommandKind::_Hello(RespVersion::RESP3), args); - let response = self.request_response(cmd, true).await?; - let response = protocol_utils::frame_to_results(response)?; - inner.switch_protocol_versions(RespVersion::RESP3); - _trace!(inner, "Recv HELLO response {:?}", response); - - Ok(()) - } else { - self.authenticate(&inner.id, username, password, false).await - } - } - - /// Read and cache the connection ID. - pub async fn cache_connection_id(&mut self, inner: &RefCount) -> Result<(), RedisError> { - let command = (RedisCommandKind::ClientID, vec![]).into(); - let result = self.request_response(command, inner.is_resp3()).await; - _debug!(inner, "Read client ID: {:?}", result); - self.id = match result { - Ok(Resp3Frame::Number { data, .. }) => Some(data), - _ => None, - }; - - Ok(()) - } - - /// Send `PING` to the server. - pub async fn ping(&mut self, inner: &RefCount) -> Result<(), RedisError> { - let command = RedisCommandKind::Ping.into(); - let response = self.request_response(command, inner.is_resp3()).await?; - - if let Some(e) = protocol_utils::frame_to_error(&response) { - Err(e) - } else { - Ok(()) - } - } - - /// Send `QUIT` and close the connection. - pub async fn disconnect(&mut self, inner: &RefCount) -> Result<(), RedisError> { - if let Err(e) = self.transport.close().await { - _warn!(inner, "Error closing connection to {}: {:?}", self.server, e); - } - Ok(()) - } - - /// Select the database provided in the `RedisConfig`. - pub async fn select_database(&mut self, inner: &RefCount) -> Result<(), RedisError> { - if inner.config.server.is_clustered() { - return Ok(()); - } - - let db = match inner.config.database { - Some(db) => db, - None => return Ok(()), - }; - - _trace!(inner, "Selecting database {} after connecting.", db); - let command = RedisCommand::new(RedisCommandKind::Select, vec![db.into()]); - let response = self.request_response(command, inner.is_resp3()).await?; - - if let Some(error) = protocol_utils::frame_to_error(&response) { - Err(error) - } else { - Ok(()) - } - } - - /// Check the `cluster_state` via `CLUSTER INFO`. - /// - /// Returns an error if the state is not `ok`. - pub async fn check_cluster_state(&mut self, inner: &RefCount) -> Result<(), RedisError> { - if !inner.config.server.is_clustered() { - return Ok(()); - } - - _trace!(inner, "Checking cluster info for {}", self.server); - let command = RedisCommand::new(RedisCommandKind::ClusterInfo, vec![]); - let response = self.request_response(command, inner.is_resp3()).await?; - let response: String = protocol_utils::frame_to_results(response)?.convert()?; - - for line in response.lines() { - let parts: Vec<&str> = line.split(':').collect(); - if parts.len() == 2 && parts[0] == "cluster_state" && parts[1] == "ok" { - return Ok(()); - } - } - - Err(RedisError::new( - RedisErrorKind::Protocol, - "Invalid or missing cluster state.", - )) - } - - /// Authenticate, set the protocol version, set the client name, select the provided database, cache the - /// connection ID and server version, and check the cluster state (if applicable). - pub async fn setup( - &mut self, - inner: &RefCount, - timeout: Option, - ) -> Result<(), RedisError> { - let timeout = timeout.unwrap_or(inner.internal_command_timeout()); - - utils::timeout( - async { - if inner.config.password.is_some() || inner.config.version == RespVersion::RESP3 { - self.switch_protocols_and_authenticate(inner).await?; - } else { - self.ping(inner).await?; - } - self.select_database(inner).await?; - if inner.connection.auto_client_setname { - self.set_client_name(inner).await?; - } - self.cache_connection_id(inner).await?; - self.cache_server_version(inner).await?; - if !inner.connection.disable_cluster_health_check { - self.check_cluster_state(inner).await?; - } - - Ok::<_, RedisError>(()) - }, - timeout, - ) - .await - } - - /// Send `READONLY` to the server. - #[cfg(feature = "replicas")] - pub async fn readonly( - &mut self, - inner: &RefCount, - timeout: Option, - ) -> Result<(), RedisError> { - if !inner.config.server.is_clustered() { - return Ok(()); - } - let timeout = timeout.unwrap_or(inner.internal_command_timeout()); - - utils::timeout( - async { - _debug!(inner, "Sending READONLY to {}", self.server); - let command = RedisCommand::new(RedisCommandKind::Readonly, vec![]); - let response = self.request_response(command, inner.is_resp3()).await?; - let _ = protocol_utils::frame_to_results(response)?; - - Ok::<_, RedisError>(()) - }, - timeout, - ) - .await - } - - /// Send the `ROLE` command to the server. - #[cfg(feature = "replicas")] - pub async fn role( - &mut self, - inner: &RefCount, - timeout: Option, - ) -> Result { - let timeout = timeout.unwrap_or(inner.internal_command_timeout()); - let command = RedisCommand::new(RedisCommandKind::Role, vec![]); - - utils::timeout( - async { - self - .request_response(command, inner.is_resp3()) - .await - .and_then(protocol_utils::frame_to_results) - }, - timeout, - ) - .await - } - - /// Discover connected replicas via the ROLE command. - #[cfg(feature = "replicas")] - pub async fn discover_replicas(&mut self, inner: &RefCount) -> Result, RedisError> { - self - .role(inner, None) - .await - .and_then(protocol_utils::parse_master_role_replicas) - } - - /// Discover connected replicas via the ROLE command. - #[cfg(not(feature = "replicas"))] - pub async fn discover_replicas(&mut self, _: &RefCount) -> Result, RedisError> { - Ok(Vec::new()) - } - - /// Split the transport into reader/writer halves. - pub fn split(self) -> (RedisWriter, RedisReader) { - let buffer = SharedBuffer::new(); - let (server, addr, default_host) = (self.server, self.addr, self.default_host); - let (sink, stream) = self.transport.split(); - let (id, version, counters) = (self.id, self.version, self.counters); - - let writer = RedisWriter { - sink, - id, - version, - default_host, - counters: counters.clone(), - server: server.clone(), - addr, - buffer: buffer.clone(), - reader: None, - }; - let reader = RedisReader { - stream: Some(stream), - task: None, - server, - buffer, - counters, - }; - (writer, reader) - } -} - -pub struct RedisReader { - pub stream: Option, - pub server: Server, - pub buffer: SharedBuffer, - pub counters: Counters, - pub task: Option>>, -} - -impl RedisReader { - pub async fn wait(&mut self) -> Result<(), RedisError> { - if let Some(ref mut task) = self.task { - task.await? - } else { - Ok(()) - } - } - - pub fn is_connected(&self) -> bool { - self.task.is_some() || self.stream.is_some() - } - - pub fn is_running(&self) -> bool { - self.task.is_some() - } - - pub fn stop(&mut self, abort: bool) { - if abort && self.task.is_some() { - self.task.take().unwrap().abort(); - } else { - self.task = None; - } - self.stream = None; - } -} - -pub struct RedisWriter { - pub sink: SplitSinkKind, - pub server: Server, - pub default_host: Str, - pub addr: Option, - pub buffer: SharedBuffer, - pub version: Option, - pub id: Option, - pub counters: Counters, - pub reader: Option, -} - -impl fmt::Debug for RedisWriter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Connection") - .field("server", &self.server) - .field("id", &self.id) - .field("default_host", &self.default_host) - .field("version", &self.version) - .finish() - } -} - -impl RedisWriter { - /// Flush the sink and reset the feed counter. - pub async fn flush(&mut self) -> Result<(), RedisError> { - trace!("Flushing socket to {}", self.server); - self.sink.flush().await?; - trace!("Flushed socket to {}", self.server); - self.counters.reset_feed_count(); - Ok(()) - } - - #[cfg(feature = "replicas")] - pub async fn discover_replicas(&mut self, inner: &RefCount) -> Result, RedisError> { - let command = RedisCommand::new(RedisCommandKind::Role, vec![]); - let role = request_response(inner, self, command, None) - .await - .and_then(protocol_utils::frame_to_results)?; - - protocol_utils::parse_master_role_replicas(role) - } - - /// Check if the reader task is still running or awaiting frames. - pub fn is_working(&self) -> bool { - self - .reader - .as_ref() - .and_then(|reader| reader.task.as_ref()) - .map(|task| !task.is_finished()) - .unwrap_or(false) - } - - /// Send a command to the server without waiting on the response. - pub async fn write_frame( - &mut self, - frame: ProtocolFrame, - should_flush: bool, - no_incr: bool, - ) -> Result<(), RedisError> { - if should_flush { - trace!("Writing and flushing {}", self.server); - if let Err(e) = self.sink.send(frame).await { - // the more useful error appears on the reader half but we'll log this just in case - debug!("{}: Error sending frame to socket: {:?}", self.server, e); - return Err(e); - } - self.counters.reset_feed_count(); - } else { - trace!("Writing without flushing {}", self.server); - if let Err(e) = self.sink.feed(frame).await { - // the more useful error appears on the reader half but we'll log this just in case - debug!("{}: Error feeding frame to socket: {:?}", self.server, e); - return Err(e); - } - self.counters.incr_feed_count(); - }; - if !no_incr { - self.counters.incr_in_flight(); - } - - Ok(()) - } - - /// Put a command at the back of the command queue. - pub fn push_command(&self, inner: &RefCount, mut cmd: RedisCommand) { - if cmd.has_no_responses() { - _trace!( - inner, - "Skip adding `{}` command to response buffer (no expected responses).", - cmd.kind.to_str_debug() - ); - - cmd.respond_to_router(inner, RouterResponse::Continue); - cmd.respond_to_caller(Ok(Resp3Frame::Null)); - return; - } - - if cmd.blocks_connection() { - self.buffer.set_blocked(); - } - self.buffer.push(cmd); - } - - /// Force close the connection. - /// - /// Returns the in-flight commands that had not received a response. - pub fn force_close(self, abort_reader: bool) -> CommandBuffer { - if abort_reader && self.reader.is_some() { - self.reader.unwrap().stop(true); - } - self.buffer.drain() - } - - /// Gracefully close the connection and wait for the reader task to finish. - /// - /// Returns the in-flight commands that had not received a response. - pub async fn graceful_close(mut self) -> CommandBuffer { - let _ = utils::timeout( - async { - let _ = self.sink.close().await; - if let Some(mut reader) = self.reader { - let _ = reader.wait().await; - } - - Ok::<_, RedisError>(()) - }, - Duration::from_millis(CONNECTION_CLOSE_TIMEOUT_MS), - ) - .await; - - self.buffer.drain() - } -} - -/// Create a connection to the specified `host` and `port` with the provided timeout, in ms. -/// -/// The returned connection will not be initialized. -pub async fn create( - inner: &RefCount, - server: &Server, - timeout: Option, -) -> Result { - let timeout = timeout.unwrap_or(inner.connection_timeout()); - - _trace!( - inner, - "Checking connection type. Native-tls: {}, Rustls: {}", - inner.config.uses_native_tls(), - inner.config.uses_rustls(), - ); - if inner.config.uses_native_tls() { - utils::timeout(RedisTransport::new_native_tls(inner, server), timeout).await - } else if inner.config.uses_rustls() { - utils::timeout(RedisTransport::new_rustls(inner, server), timeout).await - } else { - match inner.config.server { - #[cfg(feature = "unix-sockets")] - ServerConfig::Unix { ref path } => utils::timeout(RedisTransport::new_unix(inner, path), timeout).await, - _ => utils::timeout(RedisTransport::new_tcp(inner, server), timeout).await, - } - } -} - -/// Split a connection, spawn a reader task, and link the reader and writer halves. -pub fn split( - inner: &RefCount, - transport: RedisTransport, - is_replica: bool, - func: F, -) -> Result<(Server, RedisWriter), RedisError> -where - F: FnOnce( - &RefCount, - SplitStreamKind, - &Server, - &SharedBuffer, - &Counters, - bool, - ) -> JoinHandle>, -{ - let server = transport.server.clone(); - let (mut writer, mut reader) = transport.split(); - let reader_stream = match reader.stream.take() { - Some(stream) => stream, - None => { - return Err(RedisError::new( - RedisErrorKind::Unknown, - "Missing clustered connection reader stream.", - )) - }, - }; - reader.task = Some(func( - inner, - reader_stream, - &writer.server, - &writer.buffer, - &writer.counters, - is_replica, - )); - writer.reader = Some(reader); - - Ok((server, writer)) -} - -/// Send a command to the server and wait for a response. -#[cfg(feature = "replicas")] -pub async fn request_response( - inner: &RefCount, - writer: &mut RedisWriter, - mut command: RedisCommand, - timeout: Option, -) -> Result { - let (tx, rx) = oneshot_channel(); - command.response = ResponseKind::Respond(Some(tx)); - let timeout_dur = timeout - .or(command.timeout_dur) - .unwrap_or_else(|| inner.default_command_timeout()); - - _trace!( - inner, - "Sending {} ({}) to {}", - command.kind.to_str_debug(), - command.debug_id(), - writer.server - ); - let frame = protocol_utils::encode_frame(inner, &command)?; - - if !writer.is_working() { - return Err(RedisError::new(RedisErrorKind::IO, "Connection closed.")); - } - - writer.push_command(inner, command); - writer.write_frame(frame, true, false).await?; - utils::timeout(async { rx.await? }, timeout_dur).await -} diff --git a/src/protocol/debug.rs b/src/protocol/debug.rs deleted file mode 100644 index ee718bd1..00000000 --- a/src/protocol/debug.rs +++ /dev/null @@ -1,111 +0,0 @@ -use redis_protocol::{resp2::types::BytesFrame as Resp2Frame, resp3::types::BytesFrame as Resp3Frame}; -use std::{ - collections::{HashMap, HashSet}, - hash::{Hash, Hasher}, - str, -}; - -#[derive(Debug)] -enum DebugFrame { - String(String), - Bytes(Vec), - Integer(i64), - Double(f64), - #[allow(dead_code)] - Array(Vec), - // TODO add support for maps in network logs - #[allow(dead_code)] - Map(HashMap), - #[allow(dead_code)] - Set(HashSet), -} - -impl Hash for DebugFrame { - fn hash(&self, state: &mut H) { - match self { - DebugFrame::String(ref s) => { - 's'.hash(state); - s.hash(state) - }, - DebugFrame::Bytes(ref b) => { - 'b'.hash(state); - b.hash(state) - }, - DebugFrame::Integer(ref i) => { - 'i'.hash(state); - i.hash(state) - }, - DebugFrame::Double(ref f) => { - 'd'.hash(state); - f.to_be_bytes().hash(state) - }, - _ => panic!("Cannot hash network log debug frame {:?}", self), - } - } -} - -fn bytes_or_string(b: &[u8]) -> DebugFrame { - match str::from_utf8(b) { - Ok(s) => DebugFrame::String(s.to_owned()), - Err(_) => DebugFrame::Bytes(b.to_vec()), - } -} - -impl<'a> From<&'a Resp2Frame> for DebugFrame { - fn from(f: &'a Resp2Frame) -> Self { - match f { - Resp2Frame::Error(s) => DebugFrame::String(s.to_string()), - Resp2Frame::SimpleString(s) => bytes_or_string(s), - Resp2Frame::Integer(i) => DebugFrame::Integer(*i), - Resp2Frame::BulkString(b) => bytes_or_string(b), - Resp2Frame::Null => DebugFrame::String("nil".into()), - Resp2Frame::Array(frames) => DebugFrame::Array(frames.iter().map(|f| f.into()).collect()), - } - } -} - -impl<'a> From<&'a Resp3Frame> for DebugFrame { - fn from(frame: &'a Resp3Frame) -> Self { - match frame { - Resp3Frame::Map { ref data, .. } => DebugFrame::Array(data.iter().fold(vec![], |mut memo, (key, value)| { - memo.push(key.into()); - memo.push(value.into()); - memo - })), - Resp3Frame::Set { ref data, .. } => DebugFrame::Array(data.iter().map(|d| d.into()).collect()), - Resp3Frame::Array { ref data, .. } => DebugFrame::Array(data.iter().map(|d| d.into()).collect()), - Resp3Frame::BlobError { ref data, .. } => bytes_or_string(data), - Resp3Frame::BlobString { ref data, .. } => bytes_or_string(data), - Resp3Frame::SimpleString { ref data, .. } => bytes_or_string(data), - Resp3Frame::SimpleError { ref data, .. } => DebugFrame::String(data.to_string()), - Resp3Frame::Double { ref data, .. } => DebugFrame::Double(*data), - Resp3Frame::BigNumber { ref data, .. } => bytes_or_string(data), - Resp3Frame::Number { ref data, .. } => DebugFrame::Integer(*data), - Resp3Frame::Boolean { ref data, .. } => DebugFrame::String(data.to_string()), - Resp3Frame::Null => DebugFrame::String("nil".into()), - Resp3Frame::Push { ref data, .. } => DebugFrame::Array(data.iter().map(|d| d.into()).collect()), - Resp3Frame::ChunkedString(ref data) => bytes_or_string(data), - Resp3Frame::VerbatimString { ref data, .. } => bytes_or_string(data), - Resp3Frame::Hello { - ref version, ref auth, .. - } => { - let mut values = vec![DebugFrame::Integer(version.to_byte() as i64)]; - if let Some((ref username, ref password)) = auth { - values.push(DebugFrame::String(username.to_string())); - values.push(DebugFrame::String(password.to_string())); - } - DebugFrame::Array(values) - }, - } - } -} - -pub fn log_resp2_frame(name: &str, frame: &Resp2Frame, encode: bool) { - let prefix = if encode { "Encoded" } else { "Decoded" }; - trace!("{}: {} {:?}", name, prefix, DebugFrame::from(frame)) -} - -pub fn log_resp3_frame(name: &str, frame: &Resp3Frame, encode: bool) { - let prefix = if encode { "Encoded" } else { "Decoded" }; - trace!("{}: {} {:?}", name, prefix, DebugFrame::from(frame)) -} diff --git a/src/protocol/hashers.rs b/src/protocol/hashers.rs deleted file mode 100644 index e3d13564..00000000 --- a/src/protocol/hashers.rs +++ /dev/null @@ -1,97 +0,0 @@ -use crate::types::RedisValue; -use redis_protocol::redis_keyslot; - -pub fn hash_value(value: &RedisValue) -> Option { - Some(match value { - RedisValue::String(s) => redis_keyslot(s.as_bytes()), - RedisValue::Bytes(b) => redis_keyslot(b), - RedisValue::Integer(i) => redis_keyslot(i.to_string().as_bytes()), - RedisValue::Double(f) => redis_keyslot(f.to_string().as_bytes()), - RedisValue::Null => redis_keyslot(b"nil"), - RedisValue::Boolean(b) => redis_keyslot(b.to_string().as_bytes()), - _ => return None, - }) -} - -pub fn read_redis_key(value: &RedisValue) -> Option<&[u8]> { - match value { - RedisValue::String(s) => Some(s.as_bytes()), - RedisValue::Bytes(b) => Some(b), - _ => None, - } -} - -fn hash_key(value: &RedisValue) -> Option { - read_redis_key(value).map(redis_keyslot) -} - -/// A cluster hashing policy. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClusterHash { - /// Hash the first string or bytes value in the arguments. (Default) - FirstKey, - /// Hash the first argument regardless of type. - FirstValue, - /// Use a random node in the cluster. - Random, - /// Hash the value with the provided offset in the arguments array. - Offset(usize), - /// Provide a custom hash slot value. - Custom(u16), -} - -impl Default for ClusterHash { - fn default() -> Self { - ClusterHash::FirstKey - } -} - -impl From> for ClusterHash { - fn from(hash_slot: Option) -> Self { - match hash_slot { - Some(slot) => ClusterHash::Custom(slot), - None => ClusterHash::FirstKey, - } - } -} - -impl From for ClusterHash { - fn from(slot: u16) -> Self { - ClusterHash::Custom(slot) - } -} - -impl From<&str> for ClusterHash { - fn from(d: &str) -> Self { - ClusterHash::Custom(redis_keyslot(d.as_bytes())) - } -} - -impl From<&[u8]> for ClusterHash { - fn from(d: &[u8]) -> Self { - ClusterHash::Custom(redis_keyslot(d)) - } -} - -impl ClusterHash { - /// Hash the provided arguments. - pub fn hash(&self, args: &[RedisValue]) -> Option { - match self { - ClusterHash::FirstValue => args.first().and_then(hash_value), - ClusterHash::FirstKey => args.iter().find_map(hash_key), - ClusterHash::Random => None, - ClusterHash::Offset(idx) => args.get(*idx).and_then(hash_value), - ClusterHash::Custom(val) => Some(*val), - } - } - - /// Find the key to hash with the provided arguments. - pub fn find_key<'a>(&self, args: &'a [RedisValue]) -> Option<&'a [u8]> { - match self { - ClusterHash::FirstValue => args.first().and_then(read_redis_key), - ClusterHash::FirstKey => args.iter().find_map(read_redis_key), - ClusterHash::Offset(idx) => args.get(*idx).and_then(read_redis_key), - ClusterHash::Random | ClusterHash::Custom(_) => None, - } - } -} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs deleted file mode 100644 index a3d55503..00000000 --- a/src/protocol/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -pub mod cluster; -pub mod codec; -pub mod command; -pub mod connection; -#[cfg(feature = "network-logs")] -pub mod debug; -pub mod hashers; -pub mod responders; -/// TLS configuration types. -#[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" -))] -pub mod tls; -pub mod types; -pub mod utils; diff --git a/src/protocol/public.rs b/src/protocol/public.rs deleted file mode 100644 index 856d326f..00000000 --- a/src/protocol/public.rs +++ /dev/null @@ -1,210 +0,0 @@ -use crate::error::{RedisError, RedisErrorKind}; -use bytes::BytesMut; -use redis_protocol::{ - resp2::{decode::decode_mut as resp2_decode, encode::encode_bytes as resp2_encode}, - resp3::{ - decode::streaming::decode_mut as resp3_decode, - encode::complete::encode_bytes as resp3_encode, - types::StreamedFrame, - }, -}; -use tokio_util::codec::{Decoder, Encoder}; - -pub use redis_protocol::{ - redis_keyslot, - resp2::types::{Frame as Resp2Frame, FrameKind as Resp2FrameKind}, - resp2_frame_to_resp3, - resp3::types::{Auth, Frame as Resp3Frame, FrameKind as Resp3FrameKind, RespVersion}, -}; - -/// Encode a redis command string (`SET foo bar NX`, etc) into a RESP3 blob string array. -pub fn resp3_encode_command(cmd: &str) -> Resp3Frame { - Resp3Frame::Array { - data: cmd - .split(' ') - .map(|s| Resp3Frame::BlobString { - data: s.as_bytes().to_vec().into(), - attributes: None, - }) - .collect(), - attributes: None, - } -} - -/// Encode a redis command string (`SET foo bar NX`, etc) into a RESP2 bulk string array. -pub fn resp2_encode_command(cmd: &str) -> Resp2Frame { - Resp2Frame::Array( - cmd - .split(' ') - .map(|s| Resp2Frame::BulkString(s.as_bytes().to_vec().into())) - .collect(), - ) -} - -/// A framed RESP2 codec. -/// -/// ```rust -/// use fred::{ -/// codec::{resp2_encode_command, Resp2, Resp2Frame}, -/// prelude::*, -/// }; -/// use futures::{SinkExt, StreamExt}; -/// use tokio::net::TcpStream; -/// use tokio_util::codec::Framed; -/// -/// async fn example() -> Result<(), RedisError> { -/// let socket = TcpStream::connect("127.0.0.1:6379").await?; -/// let mut framed = Framed::new(socket, Resp2::default()); -/// -/// let auth = resp2_encode_command("AUTH foo bar"); -/// let get_foo = resp2_encode_command("GET foo"); -/// -/// let _ = framed.send(auth).await?; -/// let response = framed.next().await.unwrap().unwrap(); -/// assert_eq!(response.as_str().unwrap(), "OK"); -/// -/// let _ = framed.send(get_foo).await?; -/// let response = framed.next().await.unwrap().unwrap(); -/// assert_eq!(response, Resp2Frame::Null); -/// -/// Ok(()) -/// } -/// ``` -#[derive(Default)] -pub struct Resp2; - -impl Encoder for Resp2 { - type Error = RedisError; - - fn encode(&mut self, item: Resp2Frame, dst: &mut BytesMut) -> Result<(), Self::Error> { - #[cfg(feature = "network-logs")] - trace!("RESP2 codec encode: {:?}", item); - - resp2_encode(dst, &item).map(|_| ()).map_err(RedisError::from) - } -} - -impl Decoder for Resp2 { - type Error = RedisError; - type Item = Resp2Frame; - - fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { - if src.is_empty() { - return Ok(None); - } - let parsed = match resp2_decode(src)? { - Some((frame, _, _)) => frame, - None => return Ok(None), - }; - #[cfg(feature = "network-logs")] - trace!("RESP2 codec decode: {:?}", parsed); - - Ok(Some(parsed)) - } -} - -/// A framed codec for complete and streaming/chunked RESP3 frames with optional attributes. -/// -/// ```rust -/// use fred::{ -/// codec::{resp3_encode_command, Auth, Resp3, Resp3Frame, RespVersion}, -/// prelude::*, -/// }; -/// use futures::{SinkExt, StreamExt}; -/// use tokio::net::TcpStream; -/// use tokio_util::codec::Framed; -/// -/// // send `HELLO 3 AUTH foo bar` then `GET foo` -/// async fn example() -> Result<(), RedisError> { -/// let socket = TcpStream::connect("127.0.0.1:6379").await?; -/// let mut framed = Framed::new(socket, Resp3::default()); -/// -/// let hello = Resp3Frame::Hello { -/// version: RespVersion::RESP3, -/// auth: Some(Auth { -/// username: "foo".into(), -/// password: "bar".into(), -/// }), -/// }; -/// // or use the shorthand, but this likely only works for simple use cases -/// let get_foo = resp3_encode_command("GET foo"); -/// -/// // `Framed` implements both `Sink` and `Stream` -/// let _ = framed.send(hello).await?; -/// let response = framed.next().await; -/// println!("HELLO response: {:?}", response); -/// -/// let _ = framed.send(get_foo).await?; -/// let response = framed.next().await; -/// println!("GET foo: {:?}", response); -/// -/// Ok(()) -/// } -/// ``` -#[derive(Default)] -pub struct Resp3 { - streaming: Option, -} - -impl Encoder for Resp3 { - type Error = RedisError; - - fn encode(&mut self, item: Resp3Frame, dst: &mut BytesMut) -> Result<(), Self::Error> { - #[cfg(feature = "network-logs")] - trace!("RESP3 codec encode: {:?}", item); - - resp3_encode(dst, &item).map(|_| ()).map_err(RedisError::from) - } -} - -impl Decoder for Resp3 { - type Error = RedisError; - type Item = Resp3Frame; - - // FIXME ideally this would refer to the corresponding fn in codec.rs, but that code is too tightly coupled to the - // private inner interface to expose here - fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { - if src.is_empty() { - return Ok(None); - } - let parsed = match resp3_decode(src)? { - Some((f, _, _)) => f, - None => return Ok(None), - }; - - if self.streaming.is_some() && parsed.is_streaming() { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Cannot start a stream while already inside a stream.", - )); - } - - let result = if let Some(ref mut state) = self.streaming { - // we started receiving streamed data earlier - state.add_frame(parsed.into_complete_frame()?); - - if state.is_finished() { - Some(state.into_frame()?) - } else { - None - } - } else { - // we're processing a complete frame or starting a new streamed frame - if parsed.is_streaming() { - self.streaming = Some(parsed.into_streaming_frame()?); - None - } else { - // we're not in the middle of a stream and we found a complete frame - Some(parsed.into_complete_frame()?) - } - }; - - if result.is_some() { - let _ = self.streaming.take(); - } - - #[cfg(feature = "network-logs")] - trace!("RESP3 codec decode: {:?}", result); - Ok(result) - } -} diff --git a/src/protocol/responders.rs b/src/protocol/responders.rs deleted file mode 100644 index 6cdd952e..00000000 --- a/src/protocol/responders.rs +++ /dev/null @@ -1,606 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - interfaces::Resp3Frame, - modules::inner::RedisClientInner, - protocol::{ - command::{RedisCommand, RedisCommandKind, ResponseSender, RouterResponse}, - types::{KeyScanInner, Server, ValueScanInner, ValueScanResult}, - utils as protocol_utils, - }, - runtime::{AtomicUsize, Mutex, RefCount}, - types::{HScanResult, RedisKey, RedisValue, SScanResult, ScanResult, ZScanResult}, - utils as client_utils, -}; -use bytes_utils::Str; -use redis_protocol::resp3::types::{FrameKind, Resp3Frame as _Resp3Frame}; -use std::{fmt, fmt::Formatter, iter::repeat, mem, ops::DerefMut}; - -#[cfg(feature = "metrics")] -use crate::modules::metrics::MovingStats; -#[cfg(feature = "metrics")] -use crate::runtime::RwLock; -#[cfg(feature = "metrics")] -use std::{cmp, time::Instant}; - -const LAST_CURSOR: &str = "0"; - -pub enum ResponseKind { - /// Throw away the response frame and last command in the command buffer. - /// - /// Note: The reader task will still unblock the router, if specified. - /// - /// Equivalent to `Respond(None)`. - Skip, - /// Respond to the caller of the last command with the response frame. - Respond(Option), - /// Buffer multiple response frames until the expected number of frames are received, then respond with an array to - /// the caller. - /// - /// Typically used in `*_cluster` commands or to handle concurrent responses in a `Pipeline` that may span multiple - /// cluster connections. - Buffer { - /// A shared buffer for response frames. - frames: RefCount>>, - /// The expected number of response frames. - expected: usize, - /// The number of response frames received. - received: RefCount, - /// A shared oneshot channel to the caller. - tx: RefCount>>, - /// A local field for tracking the expected index of the response in the `frames` array. - index: usize, - /// Whether errors should be returned early to the caller. - error_early: bool, - }, - /// Handle the response as a page of key/value pairs from a HSCAN, SSCAN, ZSCAN command. - ValueScan(ValueScanInner), - /// Handle the response as a page of keys from a SCAN command. - KeyScan(KeyScanInner), -} - -impl fmt::Debug for ResponseKind { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", match self { - ResponseKind::Skip => "Skip", - ResponseKind::Buffer { .. } => "Buffer", - ResponseKind::Respond(_) => "Respond", - ResponseKind::KeyScan(_) => "KeyScan", - ResponseKind::ValueScan(_) => "ValueScan", - }) - } -} - -impl ResponseKind { - /// Attempt to clone the response channel. - /// - /// If the channel cannot be shared or cloned (since it contains a oneshot channel) this will fall back to a `Skip` - /// policy. - pub fn duplicate(&self) -> Option { - Some(match self { - ResponseKind::Skip => ResponseKind::Skip, - ResponseKind::Respond(_) => ResponseKind::Respond(None), - ResponseKind::Buffer { - frames, - tx, - received, - index, - expected, - error_early, - } => ResponseKind::Buffer { - frames: frames.clone(), - tx: tx.clone(), - received: received.clone(), - index: *index, - expected: *expected, - error_early: *error_early, - }, - ResponseKind::KeyScan(_) | ResponseKind::ValueScan(_) => return None, - }) - } - - pub fn set_expected_index(&mut self, idx: usize) { - if let ResponseKind::Buffer { ref mut index, .. } = self { - *index = idx; - } - } - - pub fn set_error_early(&mut self, _error_early: bool) { - if let ResponseKind::Buffer { - ref mut error_early, .. - } = self - { - *error_early = _error_early; - } - } - - pub fn new_buffer(tx: ResponseSender) -> Self { - ResponseKind::Buffer { - frames: RefCount::new(Mutex::new(vec![])), - tx: RefCount::new(Mutex::new(Some(tx))), - received: RefCount::new(AtomicUsize::new(0)), - index: 0, - expected: 0, - error_early: true, - } - } - - pub fn new_buffer_with_size(expected: usize, tx: ResponseSender) -> Self { - let frames = repeat(Resp3Frame::Null).take(expected).collect(); - ResponseKind::Buffer { - frames: RefCount::new(Mutex::new(frames)), - tx: RefCount::new(Mutex::new(Some(tx))), - received: RefCount::new(AtomicUsize::new(0)), - index: 0, - error_early: true, - expected, - } - } - - /// Take the oneshot response sender. - pub fn take_response_tx(&mut self) -> Option { - match self { - ResponseKind::Respond(tx) => tx.take(), - ResponseKind::Buffer { tx, .. } => tx.lock().take(), - _ => None, - } - } - - /// Clone the shared response sender for `Buffer` or `Multiple` variants. - pub fn clone_shared_response_tx(&self) -> Option>>> { - match self { - ResponseKind::Buffer { tx, .. } => Some(tx.clone()), - _ => None, - } - } - - /// Respond with an error to the caller. - pub fn respond_with_error(&mut self, error: RedisError) { - if let Some(tx) = self.take_response_tx() { - let _ = tx.send(Err(error)); - } - } - - /// Read the number of expected response frames. - pub fn expected_response_frames(&self) -> usize { - match self { - ResponseKind::Skip | ResponseKind::Respond(_) => 1, - ResponseKind::Buffer { ref expected, .. } => *expected, - ResponseKind::ValueScan(_) | ResponseKind::KeyScan(_) => 1, - } - } -} - -#[cfg(feature = "metrics")] -fn sample_latency(latency_stats: &RwLock, sent: Instant) { - let dur = Instant::now().duration_since(sent); - let dur_ms = cmp::max(0, (dur.as_secs() * 1000) + dur.subsec_millis() as u64) as i64; - latency_stats.write().sample(dur_ms); -} - -/// Sample overall and network latency values for a command. -#[cfg(feature = "metrics")] -fn sample_command_latencies(inner: &RefCount, command: &mut RedisCommand) { - if let Some(sent) = command.network_start.take() { - sample_latency(&inner.network_latency_stats, sent); - } - sample_latency(&inner.latency_stats, command.created); -} - -#[cfg(not(feature = "metrics"))] -fn sample_command_latencies(_: &RefCount, _: &mut RedisCommand) {} - -/// Update the client's protocol version codec version after receiving a non-error response to HELLO. -fn update_protocol_version(inner: &RefCount, command: &RedisCommand, frame: &Resp3Frame) { - if !matches!(frame.kind(), FrameKind::SimpleError | FrameKind::BlobError) { - let version = match command.kind { - RedisCommandKind::_Hello(ref version) => version, - RedisCommandKind::_HelloAllCluster(ref version) => version, - _ => return, - }; - - _debug!(inner, "Changing RESP version to {:?}", version); - // HELLO cannot be pipelined so this is safe - inner.switch_protocol_versions(version.clone()); - } -} - -fn respond_locked( - inner: &RefCount, - tx: &RefCount>>, - result: Result, -) { - if let Some(tx) = tx.lock().take() { - if let Err(_) = tx.send(result) { - _debug!(inner, "Error responding to caller."); - } - } -} - -fn add_buffered_frame( - server: &Server, - buffer: &RefCount>>, - index: usize, - frame: Resp3Frame, -) -> Result<(), RedisError> { - let mut guard = buffer.lock(); - let buffer_ref = guard.deref_mut(); - - if index >= buffer_ref.len() { - debug!( - "({}) Unexpected buffer response array index: {}, len: {}", - server, - index, - buffer_ref.len() - ); - return Err(RedisError::new( - RedisErrorKind::Unknown, - "Invalid buffer response index.", - )); - } - - trace!( - "({}) Add buffered frame {:?} at index {} with length {}", - server, - frame.kind(), - index, - buffer_ref.len() - ); - buffer_ref[index] = frame; - Ok(()) -} - -/// Check for errors while merging the provided frames into one Array frame. -fn merge_multiple_frames(frames: &mut Vec, error_early: bool) -> Resp3Frame { - if error_early { - for frame in frames.iter() { - if matches!(frame.kind(), FrameKind::SimpleError | FrameKind::BlobError) { - return frame.clone(); - } - } - } - - Resp3Frame::Array { - data: mem::take(frames), - attributes: None, - } -} - -/// Parse the output of a command that scans keys. -fn parse_key_scan_frame(frame: Resp3Frame) -> Result<(Str, Vec), RedisError> { - if let Resp3Frame::Array { mut data, .. } = frame { - if data.len() == 2 { - let cursor = match protocol_utils::frame_to_str(&data[0]) { - Some(s) => s, - None => { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Expected first SCAN result element to be a bulk string.", - )) - }, - }; - - if let Some(Resp3Frame::Array { data, .. }) = data.pop() { - let mut keys = Vec::with_capacity(data.len()); - - for frame in data.into_iter() { - let key = match protocol_utils::frame_to_bytes(&frame) { - Some(s) => s, - None => { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Expected an array of strings from second SCAN result.", - )) - }, - }; - - keys.push(key.into()); - } - - Ok((cursor, keys)) - } else { - Err(RedisError::new( - RedisErrorKind::Protocol, - "Expected second SCAN result element to be an array.", - )) - } - } else { - Err(RedisError::new( - RedisErrorKind::Protocol, - "Expected two-element bulk string array from SCAN.", - )) - } - } else { - Err(RedisError::new( - RedisErrorKind::Protocol, - "Expected bulk string array from SCAN.", - )) - } -} - -/// Parse the output of a command that scans values. -fn parse_value_scan_frame(frame: Resp3Frame) -> Result<(Str, Vec), RedisError> { - if let Resp3Frame::Array { mut data, .. } = frame { - if data.len() == 2 { - let cursor = match protocol_utils::frame_to_str(&data[0]) { - Some(s) => s, - None => { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Expected first result element to be a bulk string.", - )) - }, - }; - - if let Some(Resp3Frame::Array { data, .. }) = data.pop() { - let mut values = Vec::with_capacity(data.len()); - - for frame in data.into_iter() { - values.push(protocol_utils::frame_to_results(frame)?); - } - - Ok((cursor, values)) - } else { - Err(RedisError::new( - RedisErrorKind::Protocol, - "Expected second result element to be an array.", - )) - } - } else { - Err(RedisError::new( - RedisErrorKind::Protocol, - "Expected two-element bulk string array.", - )) - } - } else { - Err(RedisError::new(RedisErrorKind::Protocol, "Expected bulk string array.")) - } -} - -/// Send the output to the caller of a command that scans values. -fn send_value_scan_result( - inner: &RefCount, - scanner: ValueScanInner, - command: &RedisCommand, - result: Vec, - can_continue: bool, -) -> Result<(), RedisError> { - match command.kind { - RedisCommandKind::Zscan => { - let tx = scanner.tx.clone(); - let results = ValueScanInner::transform_zscan_result(result)?; - - let state = ValueScanResult::ZScan(ZScanResult { - can_continue, - inner: inner.clone(), - scan_state: scanner, - results: Some(results), - }); - - if let Err(_) = tx.send(Ok(state)) { - _warn!(inner, "Failed to send ZSCAN result to caller"); - } - }, - RedisCommandKind::Sscan => { - let tx = scanner.tx.clone(); - - let state = ValueScanResult::SScan(SScanResult { - can_continue, - inner: inner.clone(), - scan_state: scanner, - results: Some(result), - }); - - if let Err(_) = tx.send(Ok(state)) { - _warn!(inner, "Failed to send SSCAN result to caller"); - } - }, - RedisCommandKind::Hscan => { - let tx = scanner.tx.clone(); - let results = ValueScanInner::transform_hscan_result(result)?; - - let state = ValueScanResult::HScan(HScanResult { - can_continue, - inner: inner.clone(), - scan_state: scanner, - results: Some(results), - }); - - if let Err(_) = tx.send(Ok(state)) { - _warn!(inner, "Failed to send HSCAN result to caller"); - } - }, - _ => { - return Err(RedisError::new( - RedisErrorKind::Unknown, - "Invalid redis command. Expected HSCAN, SSCAN, or ZSCAN.", - )) - }, - }; - - Ok(()) -} - -/// Respond to the caller with the default response policy. -pub fn respond_to_caller( - inner: &RefCount, - server: &Server, - mut command: RedisCommand, - tx: ResponseSender, - frame: Resp3Frame, -) -> Result<(), RedisError> { - sample_command_latencies(inner, &mut command); - _trace!( - inner, - "Respond to caller from {} for {} with {:?}", - server, - command.kind.to_str_debug(), - frame.kind() - ); - if command.kind.is_hello() { - update_protocol_version(inner, &command, &frame); - } - - let _ = tx.send(Ok(frame)); - command.respond_to_router(inner, RouterResponse::Continue); - Ok(()) -} - -/// Respond to the caller, assuming multiple response frames from the last command, storing intermediate responses in -/// the shared buffer. -pub fn respond_buffer( - inner: &RefCount, - server: &Server, - command: RedisCommand, - received: RefCount, - expected: usize, - error_early: bool, - frames: RefCount>>, - index: usize, - tx: RefCount>>, - frame: Resp3Frame, -) -> Result<(), RedisError> { - _trace!( - inner, - "Handling `buffer` response from {} for {}. kind {:?}, Index: {}, ID: {}", - server, - command.kind.to_str_debug(), - frame.kind(), - index, - command.debug_id() - ); - - // errors are buffered like normal frames and are not returned early - if let Err(e) = add_buffered_frame(server, &frames, index, frame) { - respond_locked(inner, &tx, Err(e)); - command.respond_to_router(inner, RouterResponse::Continue); - _error!( - inner, - "Exiting early after unexpected buffer response index from {} with command {}, ID {}", - server, - command.kind.to_str_debug(), - command.debug_id() - ); - return Err(RedisError::new( - RedisErrorKind::Unknown, - "Invalid buffer response index.", - )); - } - - // this must come after adding the buffered frame. there's a potential race condition if this task is interrupted - // due to contention on the frame lock and another parallel task moves past the `received==expected` check before - // this task can add the frame to the buffer. - let received = client_utils::incr_atomic(&received); - if received == expected { - _trace!( - inner, - "Responding to caller after last buffered response from {}, ID: {}", - server, - command.debug_id() - ); - - let frame = merge_multiple_frames(&mut frames.lock(), error_early); - if matches!(frame.kind(), FrameKind::SimpleError | FrameKind::BlobError) { - let err = match frame.as_str() { - Some(s) => protocol_utils::pretty_error(s), - None => RedisError::new( - RedisErrorKind::Unknown, - "Unknown or invalid error from buffered frames.", - ), - }; - - respond_locked(inner, &tx, Err(err)); - } else { - respond_locked(inner, &tx, Ok(frame)); - } - command.respond_to_router(inner, RouterResponse::Continue); - } else { - // more responses are expected - _trace!( - inner, - "Waiting on {} more responses to all nodes command, ID: {}", - expected - received, - command.debug_id() - ); - // this response type is shared across connections so we do not return the command to be re-queued - } - - Ok(()) -} - -/// Respond to the caller of a key scanning operation. -pub fn respond_key_scan( - inner: &RefCount, - server: &Server, - command: RedisCommand, - mut scanner: KeyScanInner, - frame: Resp3Frame, -) -> Result<(), RedisError> { - _trace!( - inner, - "Handling `KeyScan` response from {} for {}", - server, - command.kind.to_str_debug() - ); - let (next_cursor, keys) = match parse_key_scan_frame(frame) { - Ok(result) => result, - Err(e) => { - scanner.send_error(e); - command.respond_to_router(inner, RouterResponse::Continue); - return Ok(()); - }, - }; - let scan_stream = scanner.tx.clone(); - let can_continue = next_cursor != LAST_CURSOR; - scanner.update_cursor(next_cursor); - command.respond_to_router(inner, RouterResponse::Continue); - - let scan_result = ScanResult { - scan_state: scanner, - inner: inner.clone(), - results: Some(keys), - can_continue, - }; - if let Err(_) = scan_stream.send(Ok(scan_result)) { - _debug!(inner, "Error sending SCAN page."); - } - - Ok(()) -} - -/// Respond to the caller of a value scanning operation. -pub fn respond_value_scan( - inner: &RefCount, - server: &Server, - command: RedisCommand, - mut scanner: ValueScanInner, - frame: Resp3Frame, -) -> Result<(), RedisError> { - _trace!( - inner, - "Handling `ValueScan` response from {} for {}", - server, - command.kind.to_str_debug() - ); - - let (next_cursor, values) = match parse_value_scan_frame(frame) { - Ok(result) => result, - Err(e) => { - scanner.send_error(e); - command.respond_to_router(inner, RouterResponse::Continue); - return Ok(()); - }, - }; - let scan_stream = scanner.tx.clone(); - let can_continue = next_cursor != LAST_CURSOR; - scanner.update_cursor(next_cursor); - command.respond_to_router(inner, RouterResponse::Continue); - - _trace!(inner, "Sending value scan result with {} values", values.len()); - if let Err(e) = send_value_scan_result(inner, scanner, &command, values, can_continue) { - if let Err(_) = scan_stream.send(Err(e)) { - _warn!(inner, "Error sending scan result."); - } - } - - Ok(()) -} diff --git a/src/protocol/tls.rs b/src/protocol/tls.rs deleted file mode 100644 index 99c7b622..00000000 --- a/src/protocol/tls.rs +++ /dev/null @@ -1,238 +0,0 @@ -use crate::error::RedisError; -use std::{ - fmt, - fmt::{Debug, Formatter}, - net::IpAddr, - sync::Arc, -}; - -#[cfg(feature = "enable-native-tls")] -use crate::error::RedisErrorKind; -#[cfg(feature = "enable-native-tls")] -use std::convert::{TryFrom, TryInto}; -#[cfg(feature = "enable-native-tls")] -use tokio_native_tls::native_tls::{ - TlsConnector as NativeTlsConnector, - TlsConnectorBuilder as NativeTlsConnectorBuilder, -}; -#[cfg(feature = "enable-native-tls")] -use tokio_native_tls::TlsConnector as TokioNativeTlsConnector; -#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] -use tokio_rustls::rustls::{ClientConfig as RustlsClientConfig, RootCertStore}; -#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] -use tokio_rustls::TlsConnector as RustlsConnector; - -/// A trait used for mapping IP addresses to hostnames when processing the `CLUSTER SLOTS` response. -#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-native-tls", feature = "enable-rustls"))))] -pub trait HostMapping: Send + Sync + Debug { - /// Map the provided IP address to a hostname that should be used during the TLS handshake. - /// - /// The `default_host` argument represents the hostname of the node that returned the `CLUSTER SLOTS` response. - /// - /// If `None` is returned the client will use the IP address as the server name during the TLS handshake. - fn map(&self, ip: &IpAddr, default_host: &str) -> Option; -} - -/// An optional enum used to describe how the client should modify or map IP addresses and hostnames in a clustered -/// deployment. -/// -/// This is only necessary to use with a clustered deployment. Centralized or sentinel deployments should use `None`. -/// -/// More information can be found [here](https://github.com/mna/redisc/issues/13) and [here](https://github.com/lettuce-io/lettuce-core/issues/1454#issuecomment-707537384). -#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-native-tls", feature = "enable-rustls"))))] -#[derive(Clone, Debug)] -pub enum TlsHostMapping { - /// Do not modify or replace hostnames or IP addresses in the `CLUSTER SLOTS` response. - /// - /// Default - None, - /// Replace any IP addresses in the `CLUSTER SLOTS` response with the hostname of the node that returned - /// the `CLUSTER SLOTS` response. - /// - /// If the `CLUSTER SLOTS` response contains hostnames alongside IP addresses (via the `metadata` block) then - /// those hostnames will be used instead. However, this is a relatively new Redis feature and it's likely some - /// configurations will not expose this information. - DefaultHost, - /// Provide a custom mapping from IP address to hostname to be used in a manner similar to a reverse DNS lookup. - Custom(Arc), -} - -impl TlsHostMapping { - pub(crate) fn map(&self, value: &IpAddr, default_host: &str) -> Option { - match self { - TlsHostMapping::None => None, - TlsHostMapping::DefaultHost => Some(default_host.to_owned()), - TlsHostMapping::Custom(ref inner) => inner.map(value, default_host), - } - } -} - -impl PartialEq for TlsHostMapping { - fn eq(&self, other: &Self) -> bool { - match self { - TlsHostMapping::None => matches!(other, TlsHostMapping::None), - TlsHostMapping::DefaultHost => matches!(other, TlsHostMapping::DefaultHost), - TlsHostMapping::Custom(_) => matches!(other, TlsHostMapping::Custom(_)), - } - } -} - -impl Eq for TlsHostMapping {} - -/// TLS configuration for a client. -/// -/// Note: the `hostnames` field is only necessary to use with certain clustered deployments. -/// -/// ```rust no_run -/// # use fred::types::*; -/// let config = TlsConfig { -/// // or use `TlsConnector::default_rustls()` -/// connector: TlsConnector::default_native_tls().unwrap(), -/// hostnames: TlsHostMapping::None -/// }; -/// -/// // or use the shorthand -/// let config: TlsConfig = TlsConnector::default_native_tls()?.into(); -/// let config: TlsConfig = TlsConnector::default_rustls()?.into(); -/// ``` -#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-native-tls", feature = "enable-rustls"))))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct TlsConfig { - /// The TLS connector from either `native-tls` or `rustls`. - pub connector: TlsConnector, - /// The hostname modification or mapping policy to use when discovering and connecting to cluster nodes. - pub hostnames: TlsHostMapping, -} - -impl> From for TlsConfig { - fn from(connector: C) -> Self { - TlsConfig { - connector: connector.into(), - hostnames: TlsHostMapping::None, - } - } -} - -/// An enum for interacting with various TLS libraries and interfaces. -#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-native-tls", feature = "enable-rustls"))))] -#[derive(Clone)] -pub enum TlsConnector { - #[cfg(feature = "enable-native-tls")] - #[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))] - Native(TokioNativeTlsConnector), - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - #[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))] - Rustls(RustlsConnector), -} - -impl PartialEq for TlsConnector { - fn eq(&self, _: &Self) -> bool { - true - } -} - -impl Eq for TlsConnector {} - -impl Debug for TlsConnector { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("TlsConnector") - .field("kind", match self { - #[cfg(feature = "enable-native-tls")] - TlsConnector::Native(_) => &"Native", - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - TlsConnector::Rustls(_) => &"Rustls", - }) - .finish() - } -} - -#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-native-tls", feature = "enable-rustls"))))] -impl TlsConnector { - /// Create a default TLS connector from the `native-tls` module. - #[cfg(feature = "enable-native-tls")] - #[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))] - pub fn default_native_tls() -> Result { - NativeTlsConnector::builder().try_into() - } - - /// Create a default TLS connector with the `rustls` module with safe defaults and system certs via [rustls-native-certs](https://github.com/rustls/rustls-native-certs). - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - #[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))] - pub fn default_rustls() -> Result { - let system_certs = rustls_native_certs::load_native_certs()?; - let mut cert_store = RootCertStore::empty(); - for system_cert in system_certs.into_iter() { - cert_store.add(system_cert)?; - } - - Ok( - RustlsClientConfig::builder() - .with_root_certificates(cert_store) - .with_no_client_auth() - .into(), - ) - } -} - -#[cfg(feature = "enable-native-tls")] -#[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))] -impl TryFrom for TlsConnector { - type Error = RedisError; - - fn try_from(builder: NativeTlsConnectorBuilder) -> Result { - let connector = builder - .build() - .map(TokioNativeTlsConnector::from) - .map_err(|e| RedisError::new(RedisErrorKind::Tls, format!("{:?}", e)))?; - Ok(TlsConnector::Native(connector)) - } -} - -#[cfg(feature = "enable-native-tls")] -#[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))] -impl From for TlsConnector { - fn from(connector: NativeTlsConnector) -> Self { - TlsConnector::Native(TokioNativeTlsConnector::from(connector)) - } -} - -#[cfg(feature = "enable-native-tls")] -#[cfg_attr(docsrs, doc(cfg(feature = "enable-native-tls")))] -impl From for TlsConnector { - fn from(connector: TokioNativeTlsConnector) -> Self { - TlsConnector::Native(connector) - } -} - -#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] -#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))] -impl From for TlsConnector { - fn from(config: RustlsClientConfig) -> Self { - TlsConnector::Rustls(RustlsConnector::from(Arc::new(config))) - } -} - -#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] -#[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))))] -impl From for TlsConnector { - fn from(connector: RustlsConnector) -> Self { - TlsConnector::Rustls(connector) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - fn should_create_default_rustls() { - let _ = TlsConnector::default_rustls().unwrap(); - } - - #[test] - #[cfg(feature = "enable-native-tls")] - fn should_create_default_native_tls() { - let _ = TlsConnector::default_native_tls().unwrap(); - } -} diff --git a/src/protocol/types.rs b/src/protocol/types.rs deleted file mode 100644 index 194fd013..00000000 --- a/src/protocol/types.rs +++ /dev/null @@ -1,682 +0,0 @@ -use super::utils as protocol_utils; -use crate::{ - error::{RedisError, RedisErrorKind}, - modules::inner::RedisClientInner, - prelude::RedisResult, - protocol::{cluster, utils::server_to_parts}, - runtime::{RefCount, UnboundedSender}, - types::*, - utils, -}; -use async_trait::async_trait; -use bytes_utils::Str; -use rand::Rng; -use redis_protocol::{resp2::types::BytesFrame as Resp2Frame, resp3::types::BytesFrame as Resp3Frame}; -use std::{ - cmp::Ordering, - collections::{BTreeMap, BTreeSet, HashMap}, - convert::TryInto, - fmt::{Display, Formatter}, - hash::{Hash, Hasher}, - net::{SocketAddr, ToSocketAddrs}, -}; - -#[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" -))] -use std::{net::IpAddr, str::FromStr}; - -/// Any kind of RESP frame. -#[derive(Debug)] -pub enum ProtocolFrame { - Resp2(Resp2Frame), - Resp3(Resp3Frame), -} - -impl ProtocolFrame { - /// Convert the frame to RESP3. - pub fn into_resp3(self) -> Resp3Frame { - // the `RedisValue::convert` logic already accounts for different encodings of maps and sets, so - // we can just change everything to RESP3 above the protocol layer - match self { - ProtocolFrame::Resp2(frame) => frame.into_resp3(), - ProtocolFrame::Resp3(frame) => frame, - } - } - - /// Whether the frame is encoded as a RESP3 frame. - pub fn is_resp3(&self) -> bool { - matches!(*self, ProtocolFrame::Resp3(_)) - } -} - -impl From for ProtocolFrame { - fn from(frame: Resp2Frame) -> Self { - ProtocolFrame::Resp2(frame) - } -} - -impl From for ProtocolFrame { - fn from(frame: Resp3Frame) -> Self { - ProtocolFrame::Resp3(frame) - } -} - -/// State necessary to identify or connect to a server. -#[derive(Debug, Clone)] -pub struct Server { - /// The hostname or IP address for the server. - pub host: Str, - /// The port for the server. - pub port: u16, - /// The server name used during the TLS handshake. - #[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" - ))] - #[cfg_attr( - docsrs, - doc(cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" - ))) - )] - pub tls_server_name: Option, -} - -impl Server { - /// Create a new `Server` from parts with a TLS server name. - #[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" - ))] - #[cfg_attr( - docsrs, - doc(cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" - ))) - )] - pub fn new_with_tls>(host: S, port: u16, tls_server_name: Option) -> Self { - Server { - host: host.into(), - port, - tls_server_name: tls_server_name.map(|s| s.into()), - } - } - - /// Create a new `Server` from parts. - pub fn new>(host: S, port: u16) -> Self { - Server { - host: host.into(), - port, - #[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - } - } - - #[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" - ))] - pub(crate) fn set_tls_server_name(&mut self, policy: &TlsHostMapping, default_host: &str) { - if *policy == TlsHostMapping::None { - return; - } - - let ip = match IpAddr::from_str(&self.host) { - Ok(ip) => ip, - Err(_) => return, - }; - if let Some(tls_server_name) = policy.map(&ip, default_host) { - self.tls_server_name = Some(Str::from(tls_server_name)); - } - } - - /// Attempt to parse a `host:port` string. - pub(crate) fn from_str(s: &str) -> Option { - let parts: Vec<&str> = s.trim().split(':').collect(); - if parts.len() == 2 { - if let Ok(port) = parts[1].parse::() { - Some(Server { - host: parts[0].into(), - port, - #[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - }) - } else { - None - } - } else { - None - } - } - - /// Create a new server struct from a `host:port` string and the default host that sent the last command. - pub(crate) fn from_parts(server: &str, default_host: &str) -> Option { - server_to_parts(server).ok().map(|(host, port)| { - let host = if host.is_empty() { - Str::from(default_host) - } else { - Str::from(host) - }; - - Server { - host, - port, - #[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - } - }) - } -} - -#[cfg(feature = "unix-sockets")] -#[doc(hidden)] -impl From<&std::path::Path> for Server { - fn from(value: &std::path::Path) -> Self { - Server { - host: utils::path_to_string(value).into(), - port: 0, - #[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - } - } -} - -impl TryFrom for Server { - type Error = RedisError; - - fn try_from(value: String) -> Result { - Server::from_str(&value).ok_or(RedisError::new(RedisErrorKind::Config, "Invalid `host:port` server.")) - } -} - -impl TryFrom<&str> for Server { - type Error = RedisError; - - fn try_from(value: &str) -> Result { - Server::from_str(value).ok_or(RedisError::new(RedisErrorKind::Config, "Invalid `host:port` server.")) - } -} - -impl From<(String, u16)> for Server { - fn from((host, port): (String, u16)) -> Self { - Server { - host: host.into(), - port, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - } - } -} - -impl From<(&str, u16)> for Server { - fn from((host, port): (&str, u16)) -> Self { - Server { - host: host.into(), - port, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - } - } -} - -impl From<&Server> for Server { - fn from(value: &Server) -> Self { - value.clone() - } -} - -impl Display for Server { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}:{}", self.host, self.port) - } -} - -impl PartialEq for Server { - fn eq(&self, other: &Self) -> bool { - self.host == other.host && self.port == other.port - } -} - -impl Eq for Server {} - -impl Hash for Server { - fn hash(&self, state: &mut H) { - self.host.hash(state); - self.port.hash(state); - } -} - -impl PartialOrd for Server { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Server { - fn cmp(&self, other: &Self) -> Ordering { - let host_ord = self.host.cmp(&other.host); - if host_ord == Ordering::Equal { - self.port.cmp(&other.port) - } else { - host_ord - } - } -} - -/// The kind of pubsub message. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum MessageKind { - /// A message from a `subscribe` command. - Message, - /// A message from a pattern `psubscribe` command. - PMessage, - /// A message from a sharded `ssubscribe` command. - SMessage, -} - -impl MessageKind { - pub(crate) fn from_str(s: &str) -> Option { - Some(match s { - "message" => MessageKind::Message, - "pmessage" => MessageKind::PMessage, - "smessage" => MessageKind::SMessage, - _ => return None, - }) - } -} - -/// A [publish-subscribe](https://redis.io/docs/manual/pubsub/) message. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Message { - /// The channel on which the message was sent. - pub channel: Str, - /// The message contents. - pub value: RedisValue, - /// The type of message subscription. - pub kind: MessageKind, - /// The server that sent the message. - pub server: Server, -} - -pub struct KeyScanInner { - /// The hash slot for the command. - pub hash_slot: Option, - /// An optional server override. - pub server: Option, - /// The index of the cursor in `args`. - pub cursor_idx: usize, - /// The arguments sent in each scan command. - pub args: Vec, - /// The sender half of the results channel. - pub tx: UnboundedSender>, -} - -impl KeyScanInner { - /// Update the cursor in place in the arguments. - pub fn update_cursor(&mut self, cursor: Str) { - self.args[self.cursor_idx] = cursor.into(); - } - - /// Send an error on the response stream. - pub fn send_error(&self, error: RedisError) { - let _ = self.tx.send(Err(error)); - } -} - -pub enum ValueScanResult { - SScan(SScanResult), - HScan(HScanResult), - ZScan(ZScanResult), -} - -pub struct ValueScanInner { - /// The index of the cursor argument in `args`. - pub cursor_idx: usize, - /// The arguments sent in each scan command. - pub args: Vec, - /// The sender half of the results channel. - pub tx: UnboundedSender>, -} - -impl ValueScanInner { - /// Update the cursor in place in the arguments. - pub fn update_cursor(&mut self, cursor: Str) { - self.args[self.cursor_idx] = cursor.into(); - } - - /// Send an error on the response stream. - pub fn send_error(&self, error: RedisError) { - let _ = self.tx.send(Err(error)); - } - - pub fn transform_hscan_result(mut data: Vec) -> Result { - if data.is_empty() { - return Ok(RedisMap::new()); - } - if data.len() % 2 != 0 { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Invalid HSCAN result. Expected array with an even number of elements.", - )); - } - - let mut out = HashMap::with_capacity(data.len() / 2); - while data.len() >= 2 { - let value = data.pop().unwrap(); - let key: RedisKey = match data.pop().unwrap() { - RedisValue::String(s) => s.into(), - RedisValue::Bytes(b) => b.into(), - _ => { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Invalid HSCAN result. Expected string.", - )) - }, - }; - - out.insert(key, value); - } - - out.try_into() - } - - pub fn transform_zscan_result(mut data: Vec) -> Result, RedisError> { - if data.is_empty() { - return Ok(Vec::new()); - } - if data.len() % 2 != 0 { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Invalid ZSCAN result. Expected array with an even number of elements.", - )); - } - - let mut out = Vec::with_capacity(data.len() / 2); - - for chunk in data.chunks_exact_mut(2) { - let value = chunk[0].take(); - let score = match chunk[1].take() { - RedisValue::String(s) => utils::redis_string_to_f64(&s)?, - RedisValue::Integer(i) => i as f64, - RedisValue::Double(f) => f, - _ => { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Invalid HSCAN result. Expected a string or number score.", - )) - }, - }; - - out.push((value, score)); - } - - Ok(out) - } -} - -/// A slot range and associated cluster node information from the `CLUSTER SLOTS` command. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct SlotRange { - /// The start of the hash slot range. - pub start: u16, - /// The end of the hash slot range. - pub end: u16, - /// The primary server owner. - pub primary: Server, - /// The internal ID assigned by the server. - pub id: Str, - /// Replica node owners. - #[cfg(feature = "replicas")] - #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] - pub replicas: Vec, -} - -/// The cached view of the cluster used by the client to route commands to the correct cluster nodes. -#[derive(Debug, Clone)] -pub struct ClusterRouting { - data: Vec, -} - -impl ClusterRouting { - /// Create a new empty routing table. - pub fn new() -> Self { - ClusterRouting { data: Vec::new() } - } - - /// Create a new routing table from the result of the `CLUSTER SLOTS` command. - /// - /// The `default_host` value refers to the server that provided the response. - pub fn from_cluster_slots>(value: RedisValue, default_host: S) -> Result { - let default_host = default_host.into(); - let mut data = cluster::parse_cluster_slots(value, &default_host)?; - data.sort_by(|a, b| a.start.cmp(&b.start)); - - Ok(ClusterRouting { data }) - } - - /// Read a set of unique hash slots that each map to a different primary/main node in the cluster. - pub fn unique_hash_slots(&self) -> Vec { - let mut out = BTreeMap::new(); - - for slot in self.data.iter() { - out.insert(&slot.primary, slot.start); - } - - out.into_iter().map(|(_, v)| v).collect() - } - - /// Read the set of unique primary nodes in the cluster. - pub fn unique_primary_nodes(&self) -> Vec { - let mut out = BTreeSet::new(); - - for slot in self.data.iter() { - out.insert(slot.primary.clone()); - } - - out.into_iter().collect() - } - - /// Rebuild the cache in place with the output of a `CLUSTER SLOTS` command. - pub(crate) fn rebuild( - &mut self, - inner: &RefCount, - cluster_slots: RedisValue, - default_host: &Str, - ) -> Result<(), RedisError> { - self.data = cluster::parse_cluster_slots(cluster_slots, default_host)?; - self.data.sort_by(|a, b| a.start.cmp(&b.start)); - - cluster::modify_cluster_slot_hostnames(inner, &mut self.data, default_host); - Ok(()) - } - - /// Calculate the cluster hash slot for the provided key. - pub fn hash_key(key: &[u8]) -> u16 { - redis_protocol::redis_keyslot(key) - } - - /// Find the primary server that owns the provided hash slot. - pub fn get_server(&self, slot: u16) -> Option<&Server> { - if self.data.is_empty() { - return None; - } - - protocol_utils::binary_search(&self.data, slot).map(|idx| &self.data[idx].primary) - } - - /// Read the replicas associated with the provided primary node based on the cached CLUSTER SLOTS response. - #[cfg(feature = "replicas")] - #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] - pub fn replicas(&self, primary: &Server) -> Vec { - self - .data - .iter() - .fold(BTreeSet::new(), |mut replicas, slot| { - if slot.primary == *primary { - replicas.extend(slot.replicas.clone()); - } - - replicas - }) - .into_iter() - .collect() - } - - /// Read the number of hash slot ranges in the cluster. - pub fn len(&self) -> usize { - self.data.len() - } - - /// Read the hash slot ranges in the cluster. - pub fn slots(&self) -> &[SlotRange] { - &self.data - } - - /// Read a random primary node hash slot range from the cluster cache. - pub fn random_slot(&self) -> Option<&SlotRange> { - if !self.data.is_empty() { - let idx = rand::thread_rng().gen_range(0 .. self.data.len()); - Some(&self.data[idx]) - } else { - None - } - } - - /// Read a random primary node from the cluster cache. - pub fn random_node(&self) -> Option<&Server> { - self.random_slot().map(|slot| &slot.primary) - } - - /// Print the contents of the routing table as a human-readable map. - pub fn pretty(&self) -> BTreeMap, BTreeSet)> { - let mut out = BTreeMap::new(); - for slot_range in self.data.iter() { - let entry = out - .entry(slot_range.primary.clone()) - .or_insert((Vec::new(), BTreeSet::new())); - entry.0.push((slot_range.start, slot_range.end)); - #[cfg(feature = "replicas")] - entry.1.extend(slot_range.replicas.iter().cloned()); - } - - out - } -} - -/// Default DNS resolver that uses [to_socket_addrs](std::net::ToSocketAddrs::to_socket_addrs). -#[derive(Clone, Debug)] -pub struct DefaultResolver { - id: Str, -} - -impl DefaultResolver { - /// Create a new resolver using the system's default DNS resolution. - pub fn new(id: &Str) -> Self { - DefaultResolver { id: id.clone() } - } -} - -/// A trait that can be used to override DNS resolution logic. -/// -/// Note: currently this requires [async-trait](https://crates.io/crates/async-trait). -#[cfg(feature = "glommio")] -#[async_trait(?Send)] -#[cfg_attr(docsrs, doc(cfg(feature = "dns")))] -pub trait Resolve: 'static { - /// Resolve a hostname. - async fn resolve(&self, host: Str, port: u16) -> RedisResult>; -} - -#[cfg(feature = "glommio")] -#[async_trait(?Send)] -impl Resolve for DefaultResolver { - async fn resolve(&self, host: Str, port: u16) -> RedisResult> { - let client_id = self.id.clone(); - - // glommio users should probably use a non-blocking impl such as hickory-dns - crate::runtime::spawn(async move { - let addr = format!("{}:{}", host, port); - let ips: Vec = addr.to_socket_addrs()?.collect(); - - if ips.is_empty() { - Err(RedisError::new( - RedisErrorKind::IO, - format!("Failed to resolve {}:{}", host, port), - )) - } else { - trace!("{}: Found {} addresses for {}", client_id, ips.len(), addr); - Ok(ips) - } - }) - .await? - } -} - -/// A trait that can be used to override DNS resolution logic. -/// -/// Note: currently this requires [async-trait](https://crates.io/crates/async-trait). -#[cfg(not(feature = "glommio"))] -#[async_trait] -#[cfg_attr(docsrs, doc(cfg(feature = "dns")))] -pub trait Resolve: Send + Sync + 'static { - /// Resolve a hostname. - async fn resolve(&self, host: Str, port: u16) -> RedisResult>; -} - -#[cfg(not(feature = "glommio"))] -#[async_trait] -impl Resolve for DefaultResolver { - async fn resolve(&self, host: Str, port: u16) -> RedisResult> { - let client_id = self.id.clone(); - - tokio::task::spawn_blocking(move || { - let addr = format!("{}:{}", host, port); - let ips: Vec = addr.to_socket_addrs()?.collect(); - - if ips.is_empty() { - Err(RedisError::new( - RedisErrorKind::IO, - format!("Failed to resolve {}:{}", host, port), - )) - } else { - trace!("{}: Found {} addresses for {}", client_id, ips.len(), addr); - Ok(ips) - } - }) - .await? - } -} diff --git a/src/protocol/utils.rs b/src/protocol/utils.rs deleted file mode 100644 index 9538d48c..00000000 --- a/src/protocol/utils.rs +++ /dev/null @@ -1,1273 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - modules::inner::RedisClientInner, - protocol::{ - codec::RedisCodec, - command::{ClusterErrorKind, RedisCommand, RedisCommandKind}, - connection::OK, - types::{ProtocolFrame, *}, - }, - runtime::RefCount, - types::*, - utils, -}; -use bytes::Bytes; -use bytes_utils::Str; -use redis_protocol::{ - resp2::types::{BytesFrame as Resp2Frame, Resp2Frame as _Resp2Frame}, - resp3::types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame}, - types::{PUBSUB_PUSH_PREFIX, REDIS_CLUSTER_SLOTS}, -}; -use std::{borrow::Cow, collections::HashMap, convert::TryInto, ops::Deref, str}; - -#[cfg(any(feature = "i-lists", feature = "i-sorted-sets"))] -use redis_protocol::resp3::types::FrameKind; -#[cfg(feature = "i-hashes")] -use redis_protocol::resp3::types::FrameMap; - -static LEGACY_AUTH_ERROR_BODY: &str = "ERR Client sent AUTH, but no password is set"; -static ACL_AUTH_ERROR_PREFIX: &str = - "ERR AUTH called without any password configured for the default user"; - -pub fn parse_cluster_error(data: &str) -> Result<(ClusterErrorKind, u16, String), RedisError> { - let parts: Vec<&str> = data.split(' ').collect(); - if parts.len() == 3 { - let kind: ClusterErrorKind = parts[0].try_into()?; - let slot: u16 = parts[1].parse()?; - let server = parts[2].to_string(); - - Ok((kind, slot, server)) - } else { - Err(RedisError::new(RedisErrorKind::Protocol, "Expected cluster error.")) - } -} - -pub fn queued_frame() -> Resp3Frame { - Resp3Frame::SimpleString { - data: utils::static_bytes(QUEUED.as_bytes()), - attributes: None, - } -} - -pub fn is_ok(frame: &Resp3Frame) -> bool { - match frame { - Resp3Frame::SimpleString { ref data, .. } => data == OK, - _ => false, - } -} - -pub fn server_to_parts(server: &str) -> Result<(&str, u16), RedisError> { - let parts: Vec<&str> = server.split(':').collect(); - if parts.len() < 2 { - return Err(RedisError::new(RedisErrorKind::IO, "Invalid server.")); - } - Ok((parts[0], parts[1].parse::()?)) -} - -pub fn binary_search(slots: &[SlotRange], slot: u16) -> Option { - if slot > REDIS_CLUSTER_SLOTS { - return None; - } - - let (mut low, mut high) = (0, slots.len() - 1); - while low <= high { - let mid = (low + high) / 2; - - let curr = match slots.get(mid) { - Some(slot) => slot, - None => { - warn!("Failed to find slot range at index {} for hash slot {}", mid, slot); - return None; - }, - }; - - if slot < curr.start { - high = mid - 1; - } else if slot > curr.end { - low = mid + 1; - } else { - return Some(mid); - } - } - - None -} - -pub fn pretty_error(resp: &str) -> RedisError { - let kind = { - let mut parts = resp.split_whitespace(); - - match parts.next().unwrap_or("") { - "" => RedisErrorKind::Unknown, - "ERR" => { - if resp.contains("instance has cluster support disabled") { - // Cluster client connecting to non-cluster server. - // Returning Config to signal no reconnect will help. - RedisErrorKind::Config - } else { - RedisErrorKind::Unknown - } - }, - "WRONGTYPE" => RedisErrorKind::InvalidArgument, - "NOAUTH" | "WRONGPASS" => RedisErrorKind::Auth, - "MOVED" | "ASK" | "CLUSTERDOWN" => RedisErrorKind::Cluster, - "Invalid" => match parts.next().unwrap_or("") { - "argument(s)" | "Argument" => RedisErrorKind::InvalidArgument, - "command" | "Command" => RedisErrorKind::InvalidCommand, - _ => RedisErrorKind::Unknown, - }, - _ => RedisErrorKind::Unknown, - } - }; - - let details = if resp.is_empty() { - Cow::Borrowed("No response!") - } else { - Cow::Owned(resp.to_owned()) - }; - RedisError::new(kind, details) -} - -/// Parse the frame as a string, without support for error frames. -pub fn frame_into_string(frame: Resp3Frame) -> Result { - match frame { - Resp3Frame::SimpleString { data, .. } => Ok(String::from_utf8(data.to_vec())?), - Resp3Frame::BlobString { data, .. } => Ok(String::from_utf8(data.to_vec())?), - Resp3Frame::Double { data, .. } => Ok(data.to_string()), - Resp3Frame::Number { data, .. } => Ok(data.to_string()), - Resp3Frame::Boolean { data, .. } => Ok(data.to_string()), - Resp3Frame::VerbatimString { data, .. } => Ok(String::from_utf8(data.to_vec())?), - Resp3Frame::BigNumber { data, .. } => Ok(String::from_utf8(data.to_vec())?), - Resp3Frame::SimpleError { data, .. } => Err(pretty_error(&data)), - Resp3Frame::BlobError { data, .. } => Err(pretty_error(str::from_utf8(&data)?)), - _ => Err(RedisError::new(RedisErrorKind::Protocol, "Expected string.")), - } -} - -/// Parse the frame from a shard pubsub channel. -// TODO clean this up with the v5 redis_protocol interface -pub fn parse_shard_pubsub_frame(server: &Server, frame: &Resp3Frame) -> Option { - let value = match frame { - Resp3Frame::Array { ref data, .. } | Resp3Frame::Push { ref data, .. } => { - if data.len() >= 3 && data.len() <= 5 { - // check both resp2 and resp3 formats - let has_either_prefix = (data[0].as_str().map(|s| s == PUBSUB_PUSH_PREFIX).unwrap_or(false) - && data[1].as_str().map(|s| s == "smessage").unwrap_or(false)) - || (data[0].as_str().map(|s| s == "smessage").unwrap_or(false)); - - if has_either_prefix { - let channel = match frame_to_str(&data[data.len() - 2]) { - Some(channel) => channel, - None => return None, - }; - let message = match frame_to_results(data[data.len() - 1].clone()) { - Ok(message) => message, - Err(_) => return None, - }; - - Some((channel, message)) - } else { - None - } - } else { - None - } - }, - _ => None, - }; - - value.map(|(channel, value)| Message { - channel, - value, - kind: MessageKind::SMessage, - server: server.clone(), - }) -} - -/// Parse the kind of pubsub message (pattern, sharded, or regular). -pub fn parse_message_kind(frame: &Resp3Frame) -> Result { - let frames = match frame { - Resp3Frame::Array { ref data, .. } => data, - Resp3Frame::Push { ref data, .. } => data, - _ => return Err(RedisError::new(RedisErrorKind::Protocol, "Invalid pubsub frame type.")), - }; - - let parsed = if frames.len() == 3 { - // resp2 format, normal message - frames[0].as_str().and_then(MessageKind::from_str) - } else if frames.len() == 4 { - // resp3 normal message or resp2 pattern/shard message - frames[1] - .as_str() - .and_then(MessageKind::from_str) - .or(frames[0].as_str().and_then(MessageKind::from_str)) - } else if frames.len() == 5 { - // resp3 pattern or shard message - frames[1] - .as_str() - .and_then(MessageKind::from_str) - .or(frames[2].as_str().and_then(MessageKind::from_str)) - } else { - None - }; - - parsed.ok_or(RedisError::new( - RedisErrorKind::Protocol, - "Invalid pubsub message kind.", - )) -} - -/// Parse the channel and value fields from a pubsub frame. -pub fn parse_message_fields(frame: Resp3Frame) -> Result<(Str, RedisValue), RedisError> { - let mut frames = match frame { - Resp3Frame::Array { data, .. } => data, - Resp3Frame::Push { data, .. } => data, - _ => return Err(RedisError::new(RedisErrorKind::Protocol, "Invalid pubsub frame type.")), - }; - - let value = frames - .pop() - .ok_or(RedisError::new(RedisErrorKind::Protocol, "Invalid pubsub message."))?; - let channel = frames - .pop() - .ok_or(RedisError::new(RedisErrorKind::Protocol, "Invalid pubsub channel."))?; - let channel = - frame_to_str(&channel).ok_or(RedisError::new(RedisErrorKind::Protocol, "Failed to parse channel."))?; - let value = frame_to_results(value)?; - - Ok((channel, value)) -} - -/// Parse the frame as a pubsub message. -pub fn frame_to_pubsub(server: &Server, frame: Resp3Frame) -> Result { - if let Some(message) = parse_shard_pubsub_frame(server, &frame) { - return Ok(message); - } - - let kind = parse_message_kind(&frame)?; - let (channel, value) = parse_message_fields(frame)?; - - Ok(Message { - kind, - channel, - value, - server: server.clone(), - }) -} - -pub fn check_resp2_auth_error(codec: &RedisCodec, frame: Resp2Frame) -> Resp2Frame { - let is_auth_error = match frame { - Resp2Frame::Error(ref data) => *data == LEGACY_AUTH_ERROR_BODY || data.starts_with(ACL_AUTH_ERROR_PREFIX), - _ => false, - }; - - if is_auth_error { - warn!( - "{}: [{}] Dropping unused auth warning: {}", - codec.name, - codec.server, - frame.as_str().unwrap_or("") - ); - Resp2Frame::SimpleString(utils::static_bytes(OK.as_bytes())) - } else { - frame - } -} - -pub fn check_resp3_auth_error(codec: &RedisCodec, frame: Resp3Frame) -> Resp3Frame { - let is_auth_error = match frame { - Resp3Frame::SimpleError { ref data, .. } => { - *data == LEGACY_AUTH_ERROR_BODY || data.starts_with(ACL_AUTH_ERROR_PREFIX) - }, - _ => false, - }; - - if is_auth_error { - warn!( - "{}: [{}] Dropping unused auth warning: {}", - codec.name, - codec.server, - frame.as_str().unwrap_or("") - ); - Resp3Frame::SimpleString { - data: utils::static_bytes(OK.as_bytes()), - attributes: None, - } - } else { - frame - } -} - -/// Try to parse the data as a string, and failing that return a byte slice. -pub fn string_or_bytes(data: Bytes) -> RedisValue { - if let Ok(s) = Str::from_inner(data.clone()) { - RedisValue::String(s) - } else { - RedisValue::Bytes(data) - } -} - -pub fn frame_to_bytes(frame: &Resp3Frame) -> Option { - match frame { - Resp3Frame::BigNumber { data, .. } => Some(data.clone()), - Resp3Frame::VerbatimString { data, .. } => Some(data.clone()), - Resp3Frame::BlobString { data, .. } => Some(data.clone()), - Resp3Frame::SimpleString { data, .. } => Some(data.clone()), - Resp3Frame::BlobError { data, .. } => Some(data.clone()), - Resp3Frame::SimpleError { data, .. } => Some(data.inner().clone()), - _ => None, - } -} - -pub fn frame_to_str(frame: &Resp3Frame) -> Option { - match frame { - Resp3Frame::BigNumber { data, .. } => Str::from_inner(data.clone()).ok(), - Resp3Frame::VerbatimString { data, .. } => Str::from_inner(data.clone()).ok(), - Resp3Frame::BlobString { data, .. } => Str::from_inner(data.clone()).ok(), - Resp3Frame::SimpleString { data, .. } => Str::from_inner(data.clone()).ok(), - Resp3Frame::BlobError { data, .. } => Str::from_inner(data.clone()).ok(), - Resp3Frame::SimpleError { data, .. } => Some(data.clone()), - _ => None, - } -} - -#[cfg(feature = "i-hashes")] -fn parse_nested_map(data: FrameMap) -> Result { - let mut out = HashMap::with_capacity(data.len()); - - for (key, value) in data.into_iter() { - let key: RedisKey = frame_to_results(key)?.try_into()?; - let value = frame_to_results(value)?; - - out.insert(key, value); - } - - Ok(RedisMap { inner: out }) -} - -/// Convert `nil` responses to a generic `Timeout` error. -#[cfg(any(feature = "i-lists", feature = "i-sorted-sets"))] -pub fn check_null_timeout(frame: &Resp3Frame) -> Result<(), RedisError> { - if frame.kind() == FrameKind::Null { - Err(RedisError::new(RedisErrorKind::Timeout, "Request timed out.")) - } else { - Ok(()) - } -} - -/// Parse the protocol frame into a redis value, with support for arbitrarily nested arrays. -pub fn frame_to_results(frame: Resp3Frame) -> Result { - let value = match frame { - Resp3Frame::Null => RedisValue::Null, - Resp3Frame::SimpleString { data, .. } => { - let value = string_or_bytes(data); - - if value.as_str().map(|s| s == QUEUED).unwrap_or(false) { - RedisValue::Queued - } else { - value - } - }, - Resp3Frame::SimpleError { data, .. } => return Err(pretty_error(&data)), - Resp3Frame::BlobString { data, .. } => string_or_bytes(data), - Resp3Frame::BlobError { data, .. } => { - let parsed = String::from_utf8_lossy(&data); - return Err(pretty_error(parsed.as_ref())); - }, - Resp3Frame::VerbatimString { data, .. } => string_or_bytes(data), - Resp3Frame::Number { data, .. } => data.into(), - Resp3Frame::Double { data, .. } => data.into(), - Resp3Frame::BigNumber { data, .. } => string_or_bytes(data), - Resp3Frame::Boolean { data, .. } => data.into(), - Resp3Frame::Array { data, .. } | Resp3Frame::Push { data, .. } => RedisValue::Array( - data - .into_iter() - .map(frame_to_results) - .collect::, _>>()?, - ), - Resp3Frame::Set { data, .. } => RedisValue::Array( - data - .into_iter() - .map(frame_to_results) - .collect::, _>>()?, - ), - Resp3Frame::Map { data, .. } => { - let mut out = HashMap::with_capacity(data.len()); - for (key, value) in data.into_iter() { - let key: RedisKey = frame_to_results(key)?.try_into()?; - let value = frame_to_results(value)?; - - out.insert(key, value); - } - - RedisValue::Map(RedisMap { inner: out }) - }, - _ => { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Invalid response frame type.", - )) - }, - }; - - Ok(value) -} - -/// Flatten a single nested layer of arrays or sets into one array. -#[cfg(feature = "i-hashes")] -pub fn flatten_frame(frame: Resp3Frame) -> Resp3Frame { - match frame { - Resp3Frame::Array { data, .. } => { - let count = data.iter().fold(0, |c, f| { - c + match f { - Resp3Frame::Push { ref data, .. } => data.len(), - Resp3Frame::Array { ref data, .. } => data.len(), - Resp3Frame::Set { ref data, .. } => data.len(), - _ => 1, - } - }); - - let mut out = Vec::with_capacity(count); - for frame in data.into_iter() { - match frame { - Resp3Frame::Push { data, .. } => out.extend(data), - Resp3Frame::Array { data, .. } => out.extend(data), - Resp3Frame::Set { data, .. } => out.extend(data), - _ => out.push(frame), - }; - } - - Resp3Frame::Array { - data: out, - attributes: None, - } - }, - Resp3Frame::Set { data, .. } => { - let count = data.iter().fold(0, |c, f| { - c + match f { - Resp3Frame::Array { ref data, .. } => data.len(), - Resp3Frame::Set { ref data, .. } => data.len(), - _ => 1, - } - }); - - let mut out = Vec::with_capacity(count); - for frame in data.into_iter() { - match frame { - Resp3Frame::Array { data, .. } => out.extend(data), - Resp3Frame::Set { data, .. } => out.extend(data), - _ => out.push(frame), - }; - } - - Resp3Frame::Array { - data: out, - attributes: None, - } - }, - _ => frame, - } -} - -#[cfg(feature = "i-hashes")] -/// Convert a frame to a nested RedisMap. -pub fn frame_to_map(frame: Resp3Frame) -> Result { - match frame { - Resp3Frame::Array { mut data, .. } => { - if data.is_empty() { - return Ok(RedisMap::new()); - } - if data.len() % 2 != 0 { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Expected an even number of frames.", - )); - } - - let mut inner = HashMap::with_capacity(data.len() / 2); - while data.len() >= 2 { - let value = frame_to_results(data.pop().unwrap())?; - let key = frame_to_results(data.pop().unwrap())?.try_into()?; - - inner.insert(key, value); - } - - Ok(RedisMap { inner }) - }, - Resp3Frame::Map { data, .. } => parse_nested_map(data), - Resp3Frame::SimpleError { data, .. } => Err(pretty_error(&data)), - Resp3Frame::BlobError { data, .. } => { - let parsed = String::from_utf8_lossy(&data); - Err(pretty_error(&parsed)) - }, - _ => Err(RedisError::new( - RedisErrorKind::Protocol, - "Expected array or map frames.", - )), - } -} - -/// Convert a frame to a `RedisError`. -pub fn frame_to_error(frame: &Resp3Frame) -> Option { - match frame { - Resp3Frame::SimpleError { ref data, .. } => Some(pretty_error(data)), - Resp3Frame::BlobError { ref data, .. } => { - let parsed = String::from_utf8_lossy(data); - Some(pretty_error(parsed.as_ref())) - }, - _ => None, - } -} - -pub fn value_to_outgoing_resp2_frame(value: &RedisValue) -> Result { - let frame = match value { - RedisValue::Double(ref f) => Resp2Frame::BulkString(f.to_string().into()), - RedisValue::Boolean(ref b) => Resp2Frame::BulkString(b.to_string().into()), - RedisValue::Integer(ref i) => Resp2Frame::BulkString(i.to_string().into()), - RedisValue::String(ref s) => Resp2Frame::BulkString(s.inner().clone()), - RedisValue::Bytes(ref b) => Resp2Frame::BulkString(b.clone()), - RedisValue::Queued => Resp2Frame::BulkString(Bytes::from_static(QUEUED.as_bytes())), - RedisValue::Null => Resp2Frame::Null, - _ => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - format!("Invalid argument type: {}", value.kind()), - )) - }, - }; - - Ok(frame) -} - -pub fn value_to_outgoing_resp3_frame(value: &RedisValue) -> Result { - let frame = match value { - RedisValue::Double(ref f) => Resp3Frame::BlobString { - data: f.to_string().into(), - attributes: None, - }, - RedisValue::Boolean(ref b) => Resp3Frame::BlobString { - data: b.to_string().into(), - attributes: None, - }, - RedisValue::Integer(ref i) => Resp3Frame::BlobString { - data: i.to_string().into(), - attributes: None, - }, - RedisValue::String(ref s) => Resp3Frame::BlobString { - data: s.inner().clone(), - attributes: None, - }, - RedisValue::Bytes(ref b) => Resp3Frame::BlobString { - data: b.clone(), - attributes: None, - }, - RedisValue::Queued => Resp3Frame::BlobString { - data: Bytes::from_static(QUEUED.as_bytes()), - attributes: None, - }, - RedisValue::Null => Resp3Frame::Null, - _ => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - format!("Invalid argument type: {}", value.kind()), - )) - }, - }; - - Ok(frame) -} - -#[cfg(feature = "mocks")] -pub fn mocked_value_to_frame(value: RedisValue) -> Resp3Frame { - match value { - RedisValue::Array(values) => Resp3Frame::Array { - data: values.into_iter().map(mocked_value_to_frame).collect(), - attributes: None, - }, - RedisValue::Map(values) => Resp3Frame::Map { - data: values - .inner() - .into_iter() - .map(|(key, value)| (mocked_value_to_frame(key.into()), mocked_value_to_frame(value))) - .collect(), - attributes: None, - }, - RedisValue::Null => Resp3Frame::Null, - RedisValue::Queued => Resp3Frame::SimpleString { - data: Bytes::from_static(QUEUED.as_bytes()), - attributes: None, - }, - RedisValue::Bytes(value) => Resp3Frame::BlobString { - data: value, - attributes: None, - }, - RedisValue::Boolean(value) => Resp3Frame::Boolean { - data: value, - attributes: None, - }, - RedisValue::Integer(value) => Resp3Frame::Number { - data: value, - attributes: None, - }, - RedisValue::Double(value) => Resp3Frame::Double { - data: value, - attributes: None, - }, - RedisValue::String(value) => Resp3Frame::BlobString { - data: value.into_inner(), - attributes: None, - }, - } -} - -pub fn expect_ok(value: &RedisValue) -> Result<(), RedisError> { - match *value { - RedisValue::String(ref resp) => { - if resp.deref() == OK || resp.deref() == QUEUED { - Ok(()) - } else { - Err(RedisError::new( - RedisErrorKind::Unknown, - format!("Expected OK, found {}", resp), - )) - } - }, - _ => Err(RedisError::new( - RedisErrorKind::Unknown, - format!("Expected OK, found {:?}.", value), - )), - } -} - -/// Parse the replicas from the ROLE response returned from a master/primary node. -#[cfg(feature = "replicas")] -pub fn parse_master_role_replicas(data: RedisValue) -> Result, RedisError> { - let mut role: Vec = data.convert()?; - - if role.len() == 3 { - if role[0].as_str().map(|s| s == "master").unwrap_or(false) { - let replicas: Vec = role[2].take().convert()?; - - Ok( - replicas - .into_iter() - .filter_map(|value| { - value - .convert::<(String, u16, String)>() - .ok() - .map(|(host, port, _)| Server::new(host, port)) - }) - .collect(), - ) - } else { - Ok(Vec::new()) - } - } else { - // we're talking to a replica or sentinel node - Ok(Vec::new()) - } -} - -#[cfg(feature = "i-geo")] -pub fn assert_array_len(data: &[T], len: usize) -> Result<(), RedisError> { - if data.len() == len { - Ok(()) - } else { - Err(RedisError::new( - RedisErrorKind::Parse, - format!("Expected {} values.", len), - )) - } -} - -/// Flatten a nested array of values into one array. -pub fn flatten_redis_value(value: RedisValue) -> RedisValue { - if let RedisValue::Array(values) = value { - let mut out = Vec::with_capacity(values.len()); - for value in values.into_iter() { - let flattened = flatten_redis_value(value); - if let RedisValue::Array(flattened) = flattened { - out.extend(flattened); - } else { - out.push(flattened); - } - } - - RedisValue::Array(out) - } else { - value - } -} - -/// Convert a redis value to an array of (value, score) tuples. -pub fn value_to_zset_result(value: RedisValue) -> Result, RedisError> { - let value = flatten_redis_value(value); - - if let RedisValue::Array(mut values) = value { - if values.is_empty() { - return Ok(Vec::new()); - } - if values.len() % 2 != 0 { - return Err(RedisError::new( - RedisErrorKind::Unknown, - "Expected an even number of redis values.", - )); - } - - let mut out = Vec::with_capacity(values.len() / 2); - while values.len() >= 2 { - let score = match values.pop().unwrap().as_f64() { - Some(f) => f, - None => { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Could not convert value to floating point number.", - )) - }, - }; - let value = values.pop().unwrap(); - - out.push((value, score)); - } - - Ok(out) - } else { - Err(RedisError::new( - RedisErrorKind::Unknown, - "Expected array of redis values.", - )) - } -} - -#[cfg(any(feature = "blocking-encoding", feature = "partial-tracing", feature = "full-tracing"))] -fn i64_size(i: i64) -> usize { - if i < 0 { - 1 + redis_protocol::digits_in_number(-i as usize) - } else { - redis_protocol::digits_in_number(i as usize) - } -} - -#[cfg(any(feature = "blocking-encoding", feature = "partial-tracing", feature = "full-tracing"))] -pub fn arg_size(value: &RedisValue) -> usize { - match value { - // use the RESP2 size - RedisValue::Boolean(_) => 5, - // TODO try digits_in_number(f.trunc()) + 1 + digits_in_number(f.fract()) - // but don't forget the negative sign byte - RedisValue::Double(_) => 10, - RedisValue::Null => 3, - RedisValue::Integer(ref i) => i64_size(*i), - RedisValue::String(ref s) => s.inner().len(), - RedisValue::Bytes(ref b) => b.len(), - RedisValue::Array(ref arr) => args_size(arr), - RedisValue::Map(ref map) => map - .inner - .iter() - .fold(0, |c, (k, v)| c + k.as_bytes().len() + arg_size(v)), - RedisValue::Queued => 0, - } -} - -#[cfg(any(feature = "blocking-encoding", feature = "partial-tracing", feature = "full-tracing"))] -pub fn args_size(args: &[RedisValue]) -> usize { - args.iter().fold(0, |c, arg| c + arg_size(arg)) -} - -fn serialize_hello(command: &RedisCommand, version: &RespVersion) -> Result { - let args = command.args(); - - let (auth, setname) = if args.len() == 3 { - // has auth and setname - let username = match args[0].as_bytes_str() { - Some(username) => username, - None => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Invalid username. Expected string.", - )); - }, - }; - let password = match args[1].as_bytes_str() { - Some(password) => password, - None => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Invalid password. Expected string.", - )); - }, - }; - let name = match args[2].as_bytes_str() { - Some(val) => val, - None => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Invalid setname value. Expected string.", - )); - }, - }; - - (Some((username, password)), Some(name)) - } else if args.len() == 2 { - // has auth but no setname - let username = match args[0].as_bytes_str() { - Some(username) => username, - None => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Invalid username. Expected string.", - )); - }, - }; - let password = match args[1].as_bytes_str() { - Some(password) => password, - None => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Invalid password. Expected string.", - )); - }, - }; - - (Some((username, password)), None) - } else if args.len() == 1 { - // has setname but no auth - let name = match args[0].as_bytes_str() { - Some(val) => val, - None => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Invalid setname value. Expected string.", - )); - }, - }; - - (None, Some(name)) - } else { - (None, None) - }; - - Ok(Resp3Frame::Hello { - version: version.clone(), - auth, - setname, - }) -} - -pub fn command_to_resp3_frame(command: &RedisCommand) -> Result { - let args = command.args(); - - match command.kind { - RedisCommandKind::_Custom(ref kind) => { - let parts: Vec<&str> = kind.cmd.trim().split(' ').collect(); - let mut bulk_strings = Vec::with_capacity(parts.len() + args.len()); - - for part in parts.into_iter() { - bulk_strings.push(Resp3Frame::BlobString { - data: part.as_bytes().to_vec().into(), - attributes: None, - }); - } - for value in args.iter() { - bulk_strings.push(value_to_outgoing_resp3_frame(value)?); - } - - Ok(Resp3Frame::Array { - data: bulk_strings, - attributes: None, - }) - }, - RedisCommandKind::_HelloAllCluster(ref version) | RedisCommandKind::_Hello(ref version) => { - serialize_hello(command, version) - }, - _ => { - let mut bulk_strings = Vec::with_capacity(args.len() + 2); - - bulk_strings.push(Resp3Frame::BlobString { - data: command.kind.cmd_str().into_inner(), - attributes: None, - }); - - if let Some(subcommand) = command.kind.subcommand_str() { - bulk_strings.push(Resp3Frame::BlobString { - data: subcommand.into_inner(), - attributes: None, - }); - } - for value in args.iter() { - bulk_strings.push(value_to_outgoing_resp3_frame(value)?); - } - - Ok(Resp3Frame::Array { - data: bulk_strings, - attributes: None, - }) - }, - } -} - -pub fn command_to_resp2_frame(command: &RedisCommand) -> Result { - let args = command.args(); - - match command.kind { - RedisCommandKind::_Custom(ref kind) => { - let parts: Vec<&str> = kind.cmd.trim().split(' ').collect(); - let mut bulk_strings = Vec::with_capacity(parts.len() + args.len()); - - for part in parts.into_iter() { - bulk_strings.push(Resp2Frame::BulkString(part.as_bytes().to_vec().into())); - } - for value in args.iter() { - bulk_strings.push(value_to_outgoing_resp2_frame(value)?); - } - - Ok(Resp2Frame::Array(bulk_strings)) - }, - _ => { - let mut bulk_strings = Vec::with_capacity(args.len() + 2); - - bulk_strings.push(Resp2Frame::BulkString(command.kind.cmd_str().into_inner())); - if let Some(subcommand) = command.kind.subcommand_str() { - bulk_strings.push(Resp2Frame::BulkString(subcommand.into_inner())); - } - for value in args.iter() { - bulk_strings.push(value_to_outgoing_resp2_frame(value)?); - } - - Ok(Resp2Frame::Array(bulk_strings)) - }, - } -} - -/// Serialize the command as a protocol frame. -pub fn command_to_frame(command: &RedisCommand, is_resp3: bool) -> Result { - if is_resp3 || command.kind.is_hello() { - command_to_resp3_frame(command).map(|c| c.into()) - } else { - command_to_resp2_frame(command).map(|c| c.into()) - } -} - -pub fn encode_frame(inner: &RefCount, command: &RedisCommand) -> Result { - #[cfg(all(feature = "blocking-encoding", not(feature = "glommio")))] - return command.to_frame_blocking( - inner.is_resp3(), - inner.with_perf_config(|c| c.blocking_encode_threshold), - ); - - #[cfg(any( - not(feature = "blocking-encoding"), - all(feature = "blocking-encoding", feature = "glommio") - ))] - return command.to_frame(inner.is_resp3()); -} - -#[cfg(test)] -mod tests { - #![allow(dead_code)] - #![allow(unused_imports)] - use super::*; - use std::{collections::HashMap, time::Duration}; - - fn str_to_f(s: &str) -> Resp3Frame { - Resp3Frame::SimpleString { - data: s.to_owned().into(), - attributes: None, - } - } - - fn str_to_bs(s: &str) -> Resp3Frame { - Resp3Frame::BlobString { - data: s.to_owned().into(), - attributes: None, - } - } - - fn int_to_f(i: i64) -> Resp3Frame { - Resp3Frame::Number { - data: i, - attributes: None, - } - } - - #[test] - #[cfg(feature = "i-memory")] - fn should_parse_memory_stats() { - // better from()/into() interfaces for frames coming in the next redis-protocol version... - let input = frame_to_results(Resp3Frame::Array { - data: vec![ - str_to_f("peak.allocated"), - int_to_f(934192), - str_to_f("total.allocated"), - int_to_f(872040), - str_to_f("startup.allocated"), - int_to_f(809912), - str_to_f("replication.backlog"), - int_to_f(0), - str_to_f("clients.slaves"), - int_to_f(0), - str_to_f("clients.normal"), - int_to_f(20496), - str_to_f("aof.buffer"), - int_to_f(0), - str_to_f("lua.caches"), - int_to_f(0), - str_to_f("db.0"), - Resp3Frame::Array { - data: vec![ - str_to_f("overhead.hashtable.main"), - int_to_f(72), - str_to_f("overhead.hashtable.expires"), - int_to_f(0), - ], - attributes: None, - }, - str_to_f("overhead.total"), - int_to_f(830480), - str_to_f("keys.count"), - int_to_f(1), - str_to_f("keys.bytes-per-key"), - int_to_f(62128), - str_to_f("dataset.bytes"), - int_to_f(41560), - str_to_f("dataset.percentage"), - str_to_f("66.894157409667969"), - str_to_f("peak.percentage"), - str_to_f("93.346977233886719"), - str_to_f("allocator.allocated"), - int_to_f(1022640), - str_to_f("allocator.active"), - int_to_f(1241088), - str_to_f("allocator.resident"), - int_to_f(5332992), - str_to_f("allocator-fragmentation.ratio"), - str_to_f("1.2136118412017822"), - str_to_f("allocator-fragmentation.bytes"), - int_to_f(218448), - str_to_f("allocator-rss.ratio"), - str_to_f("4.2970294952392578"), - str_to_f("allocator-rss.bytes"), - int_to_f(4091904), - str_to_f("rss-overhead.ratio"), - str_to_f("2.0268816947937012"), - str_to_f("rss-overhead.bytes"), - int_to_f(5476352), - str_to_f("fragmentation"), - str_to_f("13.007383346557617"), - str_to_f("fragmentation.bytes"), - int_to_f(9978328), - ], - attributes: None, - }) - .unwrap(); - let memory_stats: MemoryStats = input.convert().unwrap(); - - let expected_db_0 = DatabaseMemoryStats { - overhead_hashtable_expires: 0, - overhead_hashtable_main: 72, - overhead_hashtable_slot_to_keys: 0, - }; - let mut expected_db = HashMap::new(); - expected_db.insert(0, expected_db_0); - let expected = MemoryStats { - peak_allocated: 934192, - total_allocated: 872040, - startup_allocated: 809912, - replication_backlog: 0, - clients_slaves: 0, - clients_normal: 20496, - aof_buffer: 0, - lua_caches: 0, - db: expected_db, - overhead_total: 830480, - keys_count: 1, - keys_bytes_per_key: 62128, - dataset_bytes: 41560, - dataset_percentage: 66.894_157_409_667_97, - peak_percentage: 93.346_977_233_886_72, - allocator_allocated: 1022640, - allocator_active: 1241088, - allocator_resident: 5332992, - allocator_fragmentation_ratio: 1.2136118412017822, - allocator_fragmentation_bytes: 218448, - allocator_rss_ratio: 4.297_029_495_239_258, - allocator_rss_bytes: 4091904, - rss_overhead_ratio: 2.026_881_694_793_701, - rss_overhead_bytes: 5476352, - fragmentation: 13.007383346557617, - fragmentation_bytes: 9978328, - }; - - assert_eq!(memory_stats, expected); - } - - #[test] - #[cfg(feature = "i-slowlog")] - fn should_parse_slowlog_entries_redis_3() { - // redis 127.0.0.1:6379> slowlog get 2 - // 1) 1) (integer) 14 - // 2) (integer) 1309448221 - // 3) (integer) 15 - // 4) 1) "ping" - // 2) 1) (integer) 13 - // 2) (integer) 1309448128 - // 3) (integer) 30 - // 4) 1) "slowlog" - // 2) "get" - // 3) "100" - - let input = frame_to_results(Resp3Frame::Array { - data: vec![ - Resp3Frame::Array { - data: vec![int_to_f(14), int_to_f(1309448221), int_to_f(15), Resp3Frame::Array { - data: vec![str_to_bs("ping")], - attributes: None, - }], - attributes: None, - }, - Resp3Frame::Array { - data: vec![int_to_f(13), int_to_f(1309448128), int_to_f(30), Resp3Frame::Array { - data: vec![str_to_bs("slowlog"), str_to_bs("get"), str_to_bs("100")], - attributes: None, - }], - attributes: None, - }, - ], - attributes: None, - }) - .unwrap(); - let actual: Vec = input.convert().unwrap(); - - let expected = vec![ - SlowlogEntry { - id: 14, - timestamp: 1309448221, - duration: Duration::from_micros(15), - args: vec!["ping".into()], - ip: None, - name: None, - }, - SlowlogEntry { - id: 13, - timestamp: 1309448128, - duration: Duration::from_micros(30), - args: vec!["slowlog".into(), "get".into(), "100".into()], - ip: None, - name: None, - }, - ]; - - assert_eq!(actual, expected); - } - - #[test] - #[cfg(feature = "i-slowlog")] - fn should_parse_slowlog_entries_redis_4() { - // redis 127.0.0.1:6379> slowlog get 2 - // 1) 1) (integer) 14 - // 2) (integer) 1309448221 - // 3) (integer) 15 - // 4) 1) "ping" - // 5) "127.0.0.1:58217" - // 6) "worker-123" - // 2) 1) (integer) 13 - // 2) (integer) 1309448128 - // 3) (integer) 30 - // 4) 1) "slowlog" - // 2) "get" - // 3) "100" - // 5) "127.0.0.1:58217" - // 6) "worker-123" - - let input = frame_to_results(Resp3Frame::Array { - data: vec![ - Resp3Frame::Array { - data: vec![ - int_to_f(14), - int_to_f(1309448221), - int_to_f(15), - Resp3Frame::Array { - data: vec![str_to_bs("ping")], - attributes: None, - }, - str_to_bs("127.0.0.1:58217"), - str_to_bs("worker-123"), - ], - attributes: None, - }, - Resp3Frame::Array { - data: vec![ - int_to_f(13), - int_to_f(1309448128), - int_to_f(30), - Resp3Frame::Array { - data: vec![str_to_bs("slowlog"), str_to_bs("get"), str_to_bs("100")], - attributes: None, - }, - str_to_bs("127.0.0.1:58217"), - str_to_bs("worker-123"), - ], - attributes: None, - }, - ], - attributes: None, - }) - .unwrap(); - let actual: Vec = input.convert().unwrap(); - - let expected = vec![ - SlowlogEntry { - id: 14, - timestamp: 1309448221, - duration: Duration::from_micros(15), - args: vec!["ping".into()], - ip: Some("127.0.0.1:58217".into()), - name: Some("worker-123".into()), - }, - SlowlogEntry { - id: 13, - timestamp: 1309448128, - duration: Duration::from_micros(30), - args: vec!["slowlog".into(), "get".into(), "100".into()], - ip: Some("127.0.0.1:58217".into()), - name: Some("worker-123".into()), - }, - ]; - - assert_eq!(actual, expected); - } - - #[test] - #[cfg(feature = "i-cluster")] - fn should_parse_cluster_info() { - let input: RedisValue = "cluster_state:fail -cluster_slots_assigned:16384 -cluster_slots_ok:16384 -cluster_slots_pfail:3 -cluster_slots_fail:2 -cluster_known_nodes:6 -cluster_size:3 -cluster_current_epoch:6 -cluster_my_epoch:2 -cluster_stats_messages_sent:1483972 -cluster_stats_messages_received:1483968" - .into(); - - let expected = ClusterInfo { - cluster_state: ClusterState::Fail, - cluster_slots_assigned: 16384, - cluster_slots_ok: 16384, - cluster_slots_fail: 2, - cluster_slots_pfail: 3, - cluster_known_nodes: 6, - cluster_size: 3, - cluster_current_epoch: 6, - cluster_my_epoch: 2, - cluster_stats_messages_sent: 1483972, - cluster_stats_messages_received: 1483968, - }; - let actual: ClusterInfo = input.convert().unwrap(); - - assert_eq!(actual, expected); - } -} diff --git a/src/router/centralized.rs b/src/router/centralized.rs deleted file mode 100644 index 89768f04..00000000 --- a/src/router/centralized.rs +++ /dev/null @@ -1,212 +0,0 @@ -use crate::{ - error::RedisErrorKind, - modules::inner::RedisClientInner, - prelude::RedisError, - protocol::{ - command::{RedisCommand, RouterResponse}, - connection, - connection::{Counters, RedisWriter, SharedBuffer, SplitStreamKind}, - responders::{self, ResponseKind}, - types::Server, - utils as protocol_utils, - }, - router::{responses, utils, Connections, Written}, - runtime::{spawn, JoinHandle, RefCount}, - types::ServerConfig, -}; -use redis_protocol::resp3::types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame}; -use std::collections::VecDeque; - -pub async fn write( - inner: &RefCount, - writer: &mut Option, - command: RedisCommand, - force_flush: bool, -) -> Written { - if let Some(writer) = writer.as_mut() { - utils::write_command(inner, writer, command, force_flush).await - } else { - _debug!(inner, "Failed to read connection for {}", command.kind.to_str_debug()); - Written::Disconnected(( - None, - Some(command), - RedisError::new(RedisErrorKind::IO, "Missing connection."), - )) - } -} - -/// Spawn a task to read response frames from the reader half of the socket. -#[allow(unused_assignments)] -pub fn spawn_reader_task( - inner: &RefCount, - mut reader: SplitStreamKind, - server: &Server, - buffer: &SharedBuffer, - counters: &Counters, - is_replica: bool, -) -> JoinHandle> { - let (inner, server) = (inner.clone(), server.clone()); - let (buffer, counters) = (buffer.clone(), counters.clone()); - - spawn(async move { - let mut last_error = None; - - loop { - let frame = match utils::next_frame(&inner, &mut reader, &server, &buffer).await { - Ok(Some(frame)) => frame.into_resp3(), - Ok(None) => { - last_error = None; - break; - }, - Err(error) => { - last_error = Some(error); - break; - }, - }; - - if let Some(error) = responses::check_special_errors(&inner, &frame) { - last_error = Some(error); - break; - } - if let Some(frame) = responses::check_pubsub_message(&inner, &server, frame) { - if let Err(e) = process_response_frame(&inner, &server, &buffer, &counters, frame).await { - _debug!(inner, "Error processing response frame from {}: {:?}", server, e); - last_error = Some(e); - break; - } - } - } - - // at this point the order of the shared buffer no longer matters since we can't know which commands actually made - // it to the server, just that the connection closed. the shared buffer will be drained when the writer notices - // that this task finished, but here we need to first filter out any commands that have exceeded their max write - // attempts. - utils::check_blocked_router(&inner, &buffer, &last_error); - utils::check_final_write_attempt(&inner, &buffer, &last_error); - if is_replica { - responses::broadcast_replica_error(&inner, &server, last_error); - } else { - responses::broadcast_reader_error(&inner, &server, last_error); - } - - _debug!(inner, "Ending reader task from {}", server); - Ok(()) - }) -} - -/// Process the response frame in the context of the last command. -/// -/// Errors returned here will be logged, but will not close the socket or initiate a reconnect. -pub async fn process_response_frame( - inner: &RefCount, - server: &Server, - buffer: &SharedBuffer, - counters: &Counters, - frame: Resp3Frame, -) -> Result<(), RedisError> { - _trace!(inner, "Parsing response frame from {}", server); - let mut command = match buffer.pop() { - Some(command) => command, - None => { - _debug!( - inner, - "Missing last command from {}. Dropping {:?}.", - server, - frame.kind() - ); - return Ok(()); - }, - }; - _trace!( - inner, - "Checking response to {} ({})", - command.kind.to_str_debug(), - command.debug_id() - ); - counters.decr_in_flight(); - if command.blocks_connection() { - buffer.set_unblocked(); - } - responses::check_and_set_unblocked_flag(inner, &command).await; - - if command.transaction_id.is_some() { - if let Some(error) = protocol_utils::frame_to_error(&frame) { - #[allow(unused_mut)] - if let Some(mut tx) = command.take_router_tx() { - let _ = tx.send(RouterResponse::TransactionError((error, command))); - } - return Ok(()); - } else if command.kind.ends_transaction() { - command.respond_to_router(inner, RouterResponse::TransactionResult(frame)); - return Ok(()); - } else { - command.respond_to_router(inner, RouterResponse::Continue); - return Ok(()); - } - } - - // TODO clean this up - _trace!(inner, "Handling centralized response kind: {:?}", command.response); - match command.take_response() { - ResponseKind::Skip | ResponseKind::Respond(None) => { - command.respond_to_router(inner, RouterResponse::Continue); - Ok(()) - }, - ResponseKind::Respond(Some(tx)) => responders::respond_to_caller(inner, server, command, tx, frame), - ResponseKind::Buffer { - received, - expected, - frames, - tx, - index, - error_early, - } => responders::respond_buffer( - inner, - server, - command, - received, - expected, - error_early, - frames, - index, - tx, - frame, - ), - ResponseKind::KeyScan(scanner) => responders::respond_key_scan(inner, server, command, scanner, frame), - ResponseKind::ValueScan(scanner) => responders::respond_value_scan(inner, server, command, scanner, frame), - } -} - -/// Initialize fresh connections to the server, dropping any old connections and saving in-flight commands on -/// `buffer`. -pub async fn initialize_connection( - inner: &RefCount, - connections: &mut Connections, - buffer: &mut VecDeque, -) -> Result<(), RedisError> { - _debug!(inner, "Initializing centralized connection."); - let commands = connections.disconnect_all(inner).await; - buffer.extend(commands); - - match connections { - Connections::Centralized { writer, .. } => { - let server = match inner.config.server { - ServerConfig::Centralized { ref server } => server.clone(), - #[cfg(feature = "unix-sockets")] - ServerConfig::Unix { ref path } => path.as_path().into(), - _ => return Err(RedisError::new(RedisErrorKind::Config, "Expected centralized config.")), - }; - let mut transport = connection::create(inner, &server, None).await?; - transport.setup(inner, None).await?; - let (server, _writer) = connection::split(inner, transport, false, spawn_reader_task)?; - inner.notifications.broadcast_reconnect(server); - - *writer = Some(_writer); - Ok(()) - }, - _ => Err(RedisError::new( - RedisErrorKind::Config, - "Expected centralized connection.", - )), - } -} diff --git a/src/router/clustered.rs b/src/router/clustered.rs deleted file mode 100644 index 7011e4e2..00000000 --- a/src/router/clustered.rs +++ /dev/null @@ -1,747 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - interfaces, - modules::inner::RedisClientInner, - protocol::{ - command::{ClusterErrorKind, RedisCommand, RedisCommandKind, RouterCommand, RouterResponse}, - connection::{self, Counters, RedisTransport, RedisWriter, SharedBuffer, SplitStreamKind}, - responders, - responders::ResponseKind, - types::{ClusterRouting, Server, SlotRange}, - utils as protocol_utils, - }, - router::{responses, types::ClusterChange, utils, Connections, Written}, - runtime::{spawn, JoinHandle, Mutex, RefCount}, - types::{ClusterDiscoveryPolicy, ClusterStateChange}, - utils as client_utils, -}; -use futures::future::try_join_all; -use redis_protocol::resp3::types::{BytesFrame as Resp3Frame, FrameKind, Resp3Frame as _Resp3Frame}; -use std::{ - collections::{BTreeSet, HashMap, VecDeque}, - iter::repeat, -}; - -/// Find the cluster node that should receive the command. -pub fn route_command<'a>( - inner: &RefCount, - state: &'a ClusterRouting, - command: &RedisCommand, -) -> Option<&'a Server> { - if let Some(ref server) = command.cluster_node { - // this `_server` has a lifetime tied to `command`, so we switch `server` to refer to the record in `state` while - // we check whether that node exists in the cluster. we return None here if the command specifies a server that - // does not exist in the cluster. - _trace!(inner, "Routing with custom cluster node: {}", server); - state.slots().iter().find_map(|slot| { - if slot.primary == *server { - Some(&slot.primary) - } else { - None - } - }) - } else { - command - .cluster_hash() - .and_then(|slot| state.get_server(slot)) - .or_else(|| { - // for some commands we know they can go to any node, but for others it may depend on the arguments provided. - if command.args().is_empty() || command.kind.use_random_cluster_node() { - let node = state.random_node(); - _trace!( - inner, - "Using random cluster node `{:?}` for {}", - node, - command.kind.to_str_debug() - ); - node - } else { - None - } - }) - } -} - -/// Write a command to the cluster according to the [cluster hashing](https://redis.io/docs/reference/cluster-spec/) interface. -pub async fn write( - inner: &RefCount, - writers: &mut HashMap, - state: &ClusterRouting, - command: RedisCommand, - force_flush: bool, -) -> Written { - let has_custom_server = command.cluster_node.is_some(); - let server = match route_command(inner, state, &command) { - Some(server) => server, - None => { - return if has_custom_server { - _debug!( - inner, - "Respond to caller with error from missing cluster node override ({:?})", - command.cluster_node - ); - command.finish( - inner, - Err(RedisError::new( - RedisErrorKind::Cluster, - "Missing cluster node override.", - )), - ); - - Written::Ignore - } else { - // these errors usually mean the cluster is partially down or misconfigured - _warn!( - inner, - "Possible cluster misconfiguration. Missing hash slot owner for {:?}.", - command.cluster_hash() - ); - Written::NotFound(command) - }; - }, - }; - - if let Some(writer) = writers.get_mut(server) { - _debug!(inner, "Writing command `{}` to {}", command.kind.to_str_debug(), server); - utils::write_command(inner, writer, command, force_flush).await - } else { - // a reconnect message should already be queued from the reader task - _debug!( - inner, - "Failed to read connection {} for {}", - server, - command.kind.to_str_debug() - ); - - Written::Disconnected(( - Some(server.clone()), - Some(command), - RedisError::new(RedisErrorKind::IO, "Missing connection."), - )) - } -} - -/// Send a command to all cluster nodes. -/// -/// Note: if any of the commands fail to send the entire command is interrupted. -// There's probably a much cleaner way to express this. Most of the complexity here comes from the need to -// pre-allocate and assign response locations in the buffer ahead of time. This is done to avoid any race conditions. -pub async fn send_all_cluster_command( - inner: &RefCount, - writers: &mut HashMap, - mut command: RedisCommand, -) -> Result<(), RedisError> { - let num_nodes = writers.len(); - if let ResponseKind::Buffer { - ref mut frames, - ref mut expected, - .. - } = command.response - { - *expected = num_nodes; - - _trace!( - inner, - "Allocating {} null responses in buffer for {}.", - num_nodes, - command.kind.to_str_debug(), - ); - let mut guard = frames.lock(); - // pre-allocate responses - *guard = repeat(Resp3Frame::Null).take(num_nodes).collect(); - } - let mut responder = match command.response.duplicate() { - Some(resp) => resp, - None => { - return Err(RedisError::new( - RedisErrorKind::Config, - "Invalid command response type.", - )) - }, - }; - - for (idx, (server, writer)) in writers.iter_mut().enumerate() { - _debug!( - inner, - "Sending all cluster command to {} with index {}, ID: {}", - server, - idx, - command.debug_id() - ); - let mut cmd_responder = responder.duplicate().unwrap_or(ResponseKind::Skip); - cmd_responder.set_expected_index(idx); - let mut cmd = command.duplicate(cmd_responder); - cmd.skip_backpressure = true; - - if let Written::Disconnected((server, _, err)) = utils::write_command(inner, writer, cmd, true).await { - _debug!( - inner, - "Exit all nodes command early ({}/{}: {:?}) from error: {:?}", - idx + 1, - num_nodes, - server, - err - ); - responder.respond_with_error(err); - break; - } - } - - Ok(()) -} - -pub fn parse_cluster_changes( - cluster_state: &ClusterRouting, - writers: &HashMap, -) -> ClusterChange { - let mut old_servers = BTreeSet::new(); - let mut new_servers = BTreeSet::new(); - for server in cluster_state.unique_primary_nodes().into_iter() { - new_servers.insert(server); - } - for server in writers.keys() { - old_servers.insert(server.clone()); - } - let add = new_servers.difference(&old_servers).cloned().collect(); - let remove = old_servers.difference(&new_servers).cloned().collect(); - - ClusterChange { add, remove } -} - -pub fn broadcast_cluster_change(inner: &RefCount, changes: &ClusterChange) { - let mut added: Vec = changes - .add - .iter() - .map(|server| ClusterStateChange::Add(server.clone())) - .collect(); - let removed: Vec = changes - .remove - .iter() - .map(|server| ClusterStateChange::Remove(server.clone())) - .collect(); - - let changes = if added.is_empty() && removed.is_empty() { - vec![ClusterStateChange::Rebalance] - } else { - added.extend(removed); - added - }; - - inner.notifications.broadcast_cluster_change(changes); -} - -/// Spawn a task to read response frames from the reader half of the socket. -#[allow(unused_assignments)] -pub fn spawn_reader_task( - inner: &RefCount, - mut reader: SplitStreamKind, - server: &Server, - buffer: &SharedBuffer, - counters: &Counters, - is_replica: bool, -) -> JoinHandle> { - let (inner, server) = (inner.clone(), server.clone()); - let (buffer, counters) = (buffer.clone(), counters.clone()); - - // TODO support spawn_into() with glommio - spawn(async move { - let mut last_error = None; - - loop { - let frame = match utils::next_frame(&inner, &mut reader, &server, &buffer).await { - Ok(Some(frame)) => frame.into_resp3(), - Ok(None) => { - last_error = None; - break; - }, - Err(e) => { - last_error = Some(e); - break; - }, - }; - - if let Some(error) = responses::check_special_errors(&inner, &frame) { - last_error = Some(error); - break; - } - if let Some(frame) = responses::check_pubsub_message(&inner, &server, frame) { - if let Err(e) = process_response_frame(&inner, &server, &buffer, &counters, frame).await { - _debug!( - inner, - "Error processing clustered response frame from {}: {:?}", - server, - e - ); - last_error = Some(e); - break; - } - } - } - - // see the centralized variant of this function for more information. - utils::check_blocked_router(&inner, &buffer, &last_error); - utils::check_final_write_attempt(&inner, &buffer, &last_error); - if is_replica { - responses::broadcast_replica_error(&inner, &server, last_error); - } else { - responses::broadcast_reader_error(&inner, &server, last_error); - } - - _debug!(inner, "Ending reader task from {}", server); - Ok(()) - }) -} - -/// Send a MOVED or ASK command to the router, using the router channel if possible and falling back on the -/// command queue if appropriate. -// Cluster errors within a transaction can only be handled via the blocking router channel. -fn process_cluster_error( - inner: &RefCount, - server: &Server, - mut command: RedisCommand, - frame: Resp3Frame, -) { - // commands are not redirected to replica nodes - command.use_replica = false; - - let (kind, slot, server_str) = match frame.as_str() { - Some(data) => match protocol_utils::parse_cluster_error(data) { - Ok(result) => result, - Err(e) => { - command.respond_to_router(inner, RouterResponse::Continue); - command.respond_to_caller(Err(e)); - return; - }, - }, - None => { - command.respond_to_router(inner, RouterResponse::Continue); - command.respond_to_caller(Err(RedisError::new(RedisErrorKind::Protocol, "Invalid cluster error."))); - return; - }, - }; - let server = match Server::from_parts(&server_str, &server.host) { - Some(server) => server, - None => { - _warn!(inner, "Invalid server field in cluster error: {}", server_str); - command.respond_to_router(inner, RouterResponse::Continue); - command.respond_to_caller(Err(RedisError::new( - RedisErrorKind::Cluster, - "Invalid cluster redirection error.", - ))); - return; - }, - }; - - #[allow(unused_mut)] - if let Some(mut tx) = command.take_router_tx() { - let response = match kind { - ClusterErrorKind::Ask => RouterResponse::Ask((slot, server, command)), - ClusterErrorKind::Moved => RouterResponse::Moved((slot, server, command)), - }; - - _debug!(inner, "Sending cluster error to router channel."); - if let Err(response) = tx.send(response) { - #[cfg(feature = "glommio")] - let response = response.into_inner(); - - // if it could not be sent on the router tx then send it on the command channel - let command = match response { - RouterResponse::Ask((slot, server, command)) => { - if command.transaction_id.is_some() { - _debug!( - inner, - "Failed sending ASK cluster error to router in transaction: {}", - command.kind.to_str_debug() - ); - // do not send the command to the command queue - return; - } else { - RouterCommand::Ask { slot, server, command } - } - }, - RouterResponse::Moved((slot, server, command)) => { - if command.transaction_id.is_some() { - _debug!( - inner, - "Failed sending MOVED cluster error to router in transaction: {}", - command.kind.to_str_debug() - ); - // do not send the command to the command queue - return; - } else { - RouterCommand::Moved { slot, server, command } - } - }, - _ => { - _error!(inner, "Invalid cluster error router response type."); - return; - }, - }; - - _debug!(inner, "Sending cluster error to command queue."); - if let Err(e) = interfaces::send_to_router(inner, command) { - _warn!(inner, "Cannot send MOVED to router channel: {:?}", e); - } - } - } else { - let command = match kind { - ClusterErrorKind::Ask => RouterCommand::Ask { slot, server, command }, - ClusterErrorKind::Moved => RouterCommand::Moved { slot, server, command }, - }; - - _debug!(inner, "Sending cluster error to command queue."); - if let Err(e) = interfaces::send_to_router(inner, command) { - _warn!(inner, "Cannot send ASKED to router channel: {:?}", e); - } - } -} - -/// Process the response frame in the context of the last command. -/// -/// Errors returned here will be logged, but will not close the socket or initiate a reconnect. -pub async fn process_response_frame( - inner: &RefCount, - server: &Server, - buffer: &SharedBuffer, - counters: &Counters, - frame: Resp3Frame, -) -> Result<(), RedisError> { - _trace!(inner, "Parsing response frame from {}", server); - let mut command = match buffer.pop() { - Some(command) => command, - None => { - _debug!( - inner, - "Missing last command from {}. Dropping {:?}.", - server, - frame.kind() - ); - return Ok(()); - }, - }; - _trace!( - inner, - "Checking response to {} ({})", - command.kind.to_str_debug(), - command.debug_id() - ); - counters.decr_in_flight(); - if command.blocks_connection() { - buffer.set_unblocked(); - } - responses::check_and_set_unblocked_flag(inner, &command).await; - - if frame.is_redirection() { - _debug!( - inner, - "Recv MOVED or ASK error for `{}` from {}: {:?}", - command.kind.to_str_debug(), - server, - frame.as_str() - ); - process_cluster_error(inner, server, command, frame); - return Ok(()); - } - - if command.transaction_id.is_some() { - if let Some(error) = protocol_utils::frame_to_error(&frame) { - #[allow(unused_mut)] - if let Some(mut tx) = command.take_router_tx() { - let _ = tx.send(RouterResponse::TransactionError((error, command))); - } - return Ok(()); - } else if command.kind.ends_transaction() { - command.respond_to_router(inner, RouterResponse::TransactionResult(frame)); - return Ok(()); - } else { - command.respond_to_router(inner, RouterResponse::Continue); - return Ok(()); - } - } - - _trace!(inner, "Handling clustered response kind: {:?}", command.response); - match command.take_response() { - ResponseKind::Skip | ResponseKind::Respond(None) => { - command.respond_to_router(inner, RouterResponse::Continue); - Ok(()) - }, - ResponseKind::Respond(Some(tx)) => responders::respond_to_caller(inner, server, command, tx, frame), - ResponseKind::Buffer { - received, - expected, - frames, - tx, - index, - error_early, - } => responders::respond_buffer( - inner, - server, - command, - received, - expected, - error_early, - frames, - index, - tx, - frame, - ), - ResponseKind::KeyScan(scanner) => responders::respond_key_scan(inner, server, command, scanner, frame), - ResponseKind::ValueScan(scanner) => responders::respond_value_scan(inner, server, command, scanner, frame), - } -} - -/// Try connecting to any node in the provided `RedisConfig` or `old_servers`. -pub async fn connect_any( - inner: &RefCount, - old_cache: Option<&[SlotRange]>, -) -> Result { - let mut all_servers: BTreeSet = if let Some(old_cache) = old_cache { - old_cache.iter().map(|slot_range| slot_range.primary.clone()).collect() - } else { - BTreeSet::new() - }; - all_servers.extend(inner.config.server.hosts()); - _debug!(inner, "Attempting clustered connections to any of {:?}", all_servers); - - let num_servers = all_servers.len(); - let mut last_error = None; - for (idx, server) in all_servers.into_iter().enumerate() { - let mut connection = match connection::create(inner, &server, None).await { - Ok(connection) => connection, - Err(e) => { - last_error = Some(e); - continue; - }, - }; - - if let Err(e) = connection.setup(inner, None).await { - last_error = Some(e); - continue; - } - _debug!( - inner, - "Connected to {} ({}/{})", - connection.server, - idx + 1, - num_servers - ); - return Ok(connection); - } - - Err(last_error.unwrap_or(RedisError::new( - RedisErrorKind::Cluster, - "Failed connecting to any cluster node.", - ))) -} - -/// Run the `CLUSTER SLOTS` command on the backchannel, creating a new connection if needed. -/// -/// This function will attempt to use the existing backchannel connection, if found. Failing that it will -/// try to connect to any of the cluster nodes as identified in the `RedisConfig` or previous cached state. -/// -/// If this returns an error then all known cluster nodes are unreachable. -pub async fn cluster_slots_backchannel( - inner: &RefCount, - cache: Option<&ClusterRouting>, - force_disconnect: bool, -) -> Result { - if force_disconnect { - inner.backchannel.write().await.check_and_disconnect(inner, None).await; - } - - let (response, host) = { - let command: RedisCommand = RedisCommandKind::ClusterSlots.into(); - - let backchannel_result = { - // try to use the existing backchannel connection first - let mut backchannel = inner.backchannel.write().await; - if let Some(ref mut transport) = backchannel.transport { - let default_host = transport.default_host.clone(); - - _trace!(inner, "Sending backchannel CLUSTER SLOTS to {}", transport.server); - client_utils::timeout( - transport.request_response(command, inner.is_resp3()), - inner.internal_command_timeout(), - ) - .await - .ok() - .map(|frame| (frame, default_host)) - } else { - None - } - }; - if backchannel_result.is_none() { - inner.backchannel.write().await.check_and_disconnect(inner, None).await; - } - - // failing the backchannel, try to connect to any of the user-provided hosts or the last known cluster nodes - let old_cache = if let Some(policy) = inner.cluster_discovery_policy() { - match policy { - ClusterDiscoveryPolicy::ConfigEndpoint => None, - ClusterDiscoveryPolicy::UseCache => cache.map(|cache| cache.slots()), - } - } else { - cache.map(|cache| cache.slots()) - }; - - let command: RedisCommand = RedisCommandKind::ClusterSlots.into(); - let (frame, host) = if let Some((frame, host)) = backchannel_result { - let kind = frame.kind(); - - if matches!(kind, FrameKind::SimpleError | FrameKind::BlobError) { - // try connecting to any of the nodes, then try again - let mut transport = connect_any(inner, old_cache).await?; - let frame = client_utils::timeout( - transport.request_response(command, inner.is_resp3()), - inner.internal_command_timeout(), - ) - .await?; - let host = transport.default_host.clone(); - inner.update_backchannel(transport).await; - - (frame, host) - } else { - // use the response from the backchannel command - (frame, host) - } - } else { - // try connecting to any of the nodes, then try again - let mut transport = connect_any(inner, old_cache).await?; - let frame = client_utils::timeout( - transport.request_response(command, inner.is_resp3()), - inner.internal_command_timeout(), - ) - .await?; - let host = transport.default_host.clone(); - inner.update_backchannel(transport).await; - - (frame, host) - }; - - (protocol_utils::frame_to_results(frame)?, host) - }; - _trace!(inner, "Recv CLUSTER SLOTS response: {:?}", response); - if response.is_null() { - inner.backchannel.write().await.check_and_disconnect(inner, None).await; - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Invalid or missing CLUSTER SLOTS response.", - )); - } - - let mut new_cache = ClusterRouting::new(); - _debug!(inner, "Rebuilding cluster state from host: {}", host); - new_cache.rebuild(inner, response, &host)?; - Ok(new_cache) -} - -/// Check each connection and remove it from the writer map if it's not [working](RedisWriter::is_working). -pub async fn drop_broken_connections(writers: &mut HashMap) -> VecDeque { - let mut new_writers = HashMap::with_capacity(writers.len()); - let mut buffer = VecDeque::new(); - - for (server, writer) in writers.drain() { - if writer.is_working() { - new_writers.insert(server, writer); - } else { - buffer.extend(writer.graceful_close().await); - } - } - - *writers = new_writers; - buffer -} - -/// Run `CLUSTER SLOTS`, update the cached routing table, and modify the connection map. -pub async fn sync( - inner: &RefCount, - connections: &mut Connections, - buffer: &mut VecDeque, -) -> Result<(), RedisError> { - _debug!(inner, "Synchronizing cluster state."); - - if let Connections::Clustered { cache, writers } = connections { - // force disconnect after a connection unexpectedly closes or goes unresponsive - let force_disconnect = writers.is_empty() - || writers - .values() - .find_map(|t| if t.is_working() { None } else { Some(true) }) - .unwrap_or(false); - - let state = cluster_slots_backchannel(inner, Some(&*cache), force_disconnect).await?; - _debug!(inner, "Cluster routing state: {:?}", state.pretty()); - // update the cached routing table - inner - .server_state - .write() - .kind - .update_cluster_state(Some(state.clone())); - *cache = state.clone(); - - buffer.extend(drop_broken_connections(writers).await); - // detect changes to the cluster topology - let changes = parse_cluster_changes(&state, writers); - _debug!(inner, "Changing cluster connections: {:?}", changes); - broadcast_cluster_change(inner, &changes); - - // drop connections that are no longer used - for removed_server in changes.remove.into_iter() { - _debug!(inner, "Disconnecting from cluster node {}", removed_server); - let writer = match writers.remove(&removed_server) { - Some(writer) => writer, - None => continue, - }; - - let commands = writer.graceful_close().await; - buffer.extend(commands); - } - - let mut connections_ft = Vec::with_capacity(changes.add.len()); - let new_writers = RefCount::new(Mutex::new(HashMap::with_capacity(changes.add.len()))); - // connect to each of the new nodes - for server in changes.add.into_iter() { - let _inner = inner.clone(); - let _new_writers = new_writers.clone(); - connections_ft.push(async move { - _debug!(inner, "Connecting to cluster node {}", server); - let mut transport = connection::create(&_inner, &server, None).await?; - transport.setup(&_inner, None).await?; - - let (server, writer) = connection::split(&_inner, transport, false, spawn_reader_task)?; - inner.notifications.broadcast_reconnect(server.clone()); - _new_writers.lock().insert(server, writer); - Ok::<_, RedisError>(()) - }); - } - - let _ = try_join_all(connections_ft).await?; - for (server, writer) in new_writers.lock().drain() { - writers.insert(server, writer); - } - - _debug!(inner, "Finish synchronizing cluster connections."); - } else { - return Err(RedisError::new( - RedisErrorKind::Config, - "Expected clustered connections.", - )); - } - - if let Some(version) = connections.server_version() { - inner.server_state.write().kind.set_server_version(version); - } - Ok(()) -} - -/// Initialize fresh connections to the server, dropping any old connections and saving in-flight commands on -/// `buffer`. -pub async fn initialize_connections( - inner: &RefCount, - connections: &mut Connections, - buffer: &mut VecDeque, -) -> Result<(), RedisError> { - let commands = connections.disconnect_all(inner).await; - _trace!(inner, "Adding {} commands to retry buffer.", commands.len()); - buffer.extend(commands); - sync(inner, connections, buffer).await -} diff --git a/src/router/commands.rs b/src/router/commands.rs deleted file mode 100644 index 23369552..00000000 --- a/src/router/commands.rs +++ /dev/null @@ -1,727 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - modules::inner::{CommandReceiver, RedisClientInner}, - protocol::command::{ - RedisCommand, - RedisCommandKind, - ResponseSender, - RouterCommand, - RouterReceiver, - RouterResponse, - }, - router::{utils, Backpressure, Router, Written}, - runtime::{OneshotSender, RefCount}, - types::{Blocking, ClientState, ClientUnblockFlag, ClusterHash, Server}, - utils as client_utils, -}; -use redis_protocol::resp3::types::BytesFrame as Resp3Frame; - -#[cfg(feature = "transactions")] -use crate::router::transactions; -#[cfg(feature = "full-tracing")] -use tracing_futures::Instrument; - -/// Wait for the response from the reader task, handling cluster redirections if needed. -/// -/// The command is returned if it failed to write but could be immediately retried. -/// -/// Errors from this function should end the connection task. -async fn handle_router_response( - inner: &RefCount, - router: &mut Router, - rx: Option, -) -> Result, RedisError> { - if let Some(rx) = rx { - _debug!(inner, "Waiting on router channel."); - let response = match rx.await { - Ok(response) => response, - Err(e) => { - _warn!(inner, "Dropped router response channel with error: {:?}", e); - return Ok(None); - }, - }; - - _debug!(inner, "Recv router response."); - match response { - RouterResponse::Continue => Ok(None), - RouterResponse::Ask((slot, server, mut command)) => { - if let Err(e) = command.decr_check_redirections() { - command.respond_to_caller(Err(e)); - Ok(None) - } else { - utils::send_asking_with_policy(inner, router, &server, slot).await?; - command.hasher = ClusterHash::Custom(slot); - command.use_replica = false; - command.attempts_remaining += 1; - Ok(Some(command)) - } - }, - RouterResponse::Moved((slot, server, mut command)) => { - // check if slot belongs to server, if not then run sync cluster - if !router.cluster_node_owns_slot(slot, &server) { - utils::sync_cluster_with_policy(inner, router).await?; - } - - if let Err(e) = command.decr_check_redirections() { - command.finish(inner, Err(e)); - Ok(None) - } else { - command.hasher = ClusterHash::Custom(slot); - command.use_replica = false; - command.attempts_remaining += 1; - Ok(Some(command)) - } - }, - RouterResponse::ConnectionClosed((error, command)) => { - let command = if command.should_finish_with_error(inner) { - command.finish(inner, Err(error.clone())); - None - } else { - Some(command) - }; - - utils::reconnect_with_policy(inner, router).await?; - Ok(command) - }, - RouterResponse::TransactionError(_) | RouterResponse::TransactionResult(_) => { - _error!(inner, "Unexpected transaction response. This is a bug."); - Err(RedisError::new( - RedisErrorKind::Unknown, - "Invalid transaction response.", - )) - }, - } - } else { - Ok(None) - } -} - -/// Continuously write the command until it is sent, queued to try later, or fails with a fatal error. -async fn write_with_backpressure( - inner: &RefCount, - router: &mut Router, - command: RedisCommand, - force_pipeline: bool, -) -> Result<(), RedisError> { - Box::pin(async { - _trace!(inner, "Writing command: {:?}", command); - - let mut _command: Option = Some(command); - let mut _backpressure: Option = None; - loop { - let mut command = match _command.take() { - Some(command) => command, - None => return Err(RedisError::new(RedisErrorKind::Unknown, "Missing command.")), - }; - if let Err(e) = command.decr_check_attempted() { - command.finish(inner, Err(e)); - break; - } - - // apply backpressure first if needed. as a part of that check we may decide to block on the next command. - let router_rx = match _backpressure { - Some(backpressure) => match backpressure.wait(inner, &mut command).await { - Ok(Some(rx)) => Some(rx), - Ok(None) => { - if command.should_auto_pipeline(inner, force_pipeline) { - None - } else { - Some(command.create_router_channel()) - } - }, - Err(e) => { - command.respond_to_caller(Err(e)); - return Ok(()); - }, - }, - None => { - if command.should_auto_pipeline(inner, force_pipeline) { - None - } else { - Some(command.create_router_channel()) - } - }, - }; - let closes_connection = command.kind.closes_connection(); - let is_blocking = command.blocks_connection(); - let use_replica = command.use_replica; - - let result = if use_replica { - router.write_replica(command, false).await - } else { - router.write(command, false).await - }; - - match result { - Written::Backpressure((mut command, backpressure)) => { - _debug!(inner, "Recv backpressure again for {}.", command.kind.to_str_debug()); - // backpressure doesn't count as a write attempt - command.attempts_remaining += 1; - _command = Some(command); - _backpressure = Some(backpressure); - - continue; - }, - Written::Disconnected((server, command, error)) => { - _debug!(inner, "Handle disconnect for {:?} due to {:?}", server, error); - let commands = router.connections.disconnect(inner, server.as_ref()).await; - router.buffer_commands(commands); - if let Some(command) = command { - if command.should_finish_with_error(inner) { - command.finish(inner, Err(error)); - } else { - router.buffer_command(command); - } - } - - utils::defer_reconnect(inner); - break; - }, - Written::NotFound(mut command) => { - if let Err(e) = command.decr_check_redirections() { - command.finish(inner, Err(e)); - utils::defer_reconnect(inner); - break; - } - - _debug!(inner, "Perform cluster sync after missing hash slot lookup."); - if let Err(error) = router.sync_cluster().await { - // try to sync the cluster once, and failing that buffer the command. - _warn!(inner, "Failed to sync cluster after NotFound: {:?}", error); - utils::defer_reconnect(inner); - router.buffer_command(command); - utils::delay_cluster_sync(inner).await?; - break; - } else { - _command = Some(command); - _backpressure = None; - continue; - } - }, - Written::Ignore => { - _trace!(inner, "Ignore `Written` response."); - break; - }, - Written::SentAll => { - _trace!(inner, "Sent command to all servers."); - let _ = router.check_and_flush().await; - if let Some(command) = handle_router_response(inner, router, router_rx).await? { - // commands that are sent to all nodes are not retried after a connection closing - _warn!(inner, "Responding with canceled error after all nodes command failure."); - command.finish(inner, Err(RedisError::new_canceled())); - break; - } else { - if closes_connection { - _trace!(inner, "Ending command loop after QUIT or SHUTDOWN."); - return Err(RedisError::new_canceled()); - } - - break; - } - }, - Written::Sent((server, flushed)) => { - _trace!(inner, "Sent command to {}. Flushed: {}", server, flushed); - if is_blocking { - inner.backchannel.write().await.set_blocked(&server); - } - if !flushed { - let _ = router.check_and_flush().await; - } - - let should_interrupt = - is_blocking && inner.counters.read_cmd_buffer_len() > 0 && inner.config.blocking == Blocking::Interrupt; - if should_interrupt { - // if there's other commands in the queue then interrupt the command that was just sent - _debug!(inner, "Interrupt after write."); - if let Err(e) = client_utils::interrupt_blocked_connection(inner, ClientUnblockFlag::Error).await { - _warn!(inner, "Failed to unblock connection: {:?}", e); - } - } - - if let Some(command) = handle_router_response(inner, router, router_rx).await? { - _command = Some(command); - _backpressure = None; - continue; - } else { - if closes_connection { - _trace!(inner, "Ending command loop after QUIT or SHUTDOWN."); - return Err(RedisError::new_canceled()); - } - - break; - } - }, - Written::Error((error, command)) => { - _debug!(inner, "Fatal error writing command: {:?}", error); - if let Some(command) = command { - command.finish(inner, Err(error.clone())); - } - inner.notifications.broadcast_error(error.clone()); - - utils::defer_reconnect(inner); - return Err(error); - }, - #[cfg(feature = "replicas")] - Written::Fallback(command) => { - _error!( - inner, - "Unexpected replica response to {} ({})", - command.kind.to_str_debug(), - command.debug_id() - ); - command.finish( - inner, - Err(RedisError::new(RedisErrorKind::Replica, "Unexpected replica response.")), - ); - break; - }, - } - } - - Ok(()) - }) - .await -} - -#[cfg(feature = "full-tracing")] -async fn write_with_backpressure_t( - inner: &RefCount, - router: &mut Router, - mut command: RedisCommand, - force_pipeline: bool, -) -> Result<(), RedisError> { - if inner.should_trace() { - command.take_queued_span(); - let span = fspan!(command, inner.full_tracing_span_level(), "fred.write"); - write_with_backpressure(inner, router, command, force_pipeline) - .instrument(span) - .await - } else { - write_with_backpressure(inner, router, command, force_pipeline).await - } -} - -#[cfg(not(feature = "full-tracing"))] -async fn write_with_backpressure_t( - inner: &RefCount, - router: &mut Router, - command: RedisCommand, - force_pipeline: bool, -) -> Result<(), RedisError> { - write_with_backpressure(inner, router, command, force_pipeline).await -} - -/// Run a pipelined series of commands, queueing commands to run later if needed. -async fn process_pipeline( - inner: &RefCount, - router: &mut Router, - commands: Vec, -) -> Result<(), RedisError> { - _debug!(inner, "Writing pipeline with {} commands", commands.len()); - - for mut command in commands.into_iter() { - // trying to pipeline `SSUBSCRIBE` is problematic since successful responses arrive out-of-order via pubsub push - // frames, but error redirections are returned in-order and the client is expected to follow them. this makes it - // very difficult to accurately associate redirections with `ssubscribe` calls within a pipeline. to avoid this we - // never pipeline `ssubscribe`, even if the caller asks. - let force_pipeline = if command.kind == RedisCommandKind::Ssubscribe { - command.can_pipeline = false; - false - } else { - command.can_pipeline = true; - !command.is_all_cluster_nodes() - }; - command.skip_backpressure = true; - - if let Err(e) = write_with_backpressure_t(inner, router, command, force_pipeline).await { - // if the command cannot be written it will be queued to run later. - // if a connection is dropped due to an error the reader will send a command to reconnect and retry later. - _debug!(inner, "Error writing command in pipeline: {:?}", e); - } - } - - Ok(()) -} - -/// Send ASKING to the provided server, then retry the provided command. -async fn process_ask( - inner: &RefCount, - router: &mut Router, - server: Server, - slot: u16, - mut command: RedisCommand, -) -> Result<(), RedisError> { - command.use_replica = false; - command.hasher = ClusterHash::Custom(slot); - - if let Err(e) = command.decr_check_redirections() { - command.respond_to_caller(Err(e)); - return Ok(()); - } - if let Err(e) = utils::send_asking_with_policy(inner, router, &server, slot).await { - command.respond_to_caller(Err(e.clone())); - return Err(e); - } - if let Err(error) = write_with_backpressure_t(inner, router, command, false).await { - _debug!(inner, "Error sending command after ASKING: {:?}", error); - Err(error) - } else { - Ok(()) - } -} - -/// Sync the cluster state then retry the command. -async fn process_moved( - inner: &RefCount, - router: &mut Router, - server: Server, - slot: u16, - mut command: RedisCommand, -) -> Result<(), RedisError> { - command.use_replica = false; - command.hasher = ClusterHash::Custom(slot); - - utils::delay_cluster_sync(inner).await?; - _debug!(inner, "Syncing cluster after MOVED {} {}", slot, server); - if let Err(e) = utils::sync_cluster_with_policy(inner, router).await { - command.respond_to_caller(Err(e.clone())); - return Err(e); - } - if let Err(e) = command.decr_check_redirections() { - command.respond_to_caller(Err(e)); - return Ok(()); - } - if let Err(error) = write_with_backpressure_t(inner, router, command, false).await { - _debug!(inner, "Error sending command after MOVED: {:?}", error); - Err(error) - } else { - Ok(()) - } -} - -#[cfg(feature = "replicas")] -async fn process_replica_reconnect( - inner: &RefCount, - router: &mut Router, - server: Option, - force: bool, - tx: Option, - replica: bool, -) -> Result<(), RedisError> { - #[allow(unused_mut)] - if replica { - let result = utils::sync_replicas_with_policy(inner, router, false).await; - if let Some(mut tx) = tx { - let _ = tx.send(result.map(|_| Resp3Frame::Null)); - } - - Ok(()) - } else { - process_reconnect(inner, router, server, force, tx).await - } -} - -/// Reconnect to the server(s). -#[allow(unused_mut)] -async fn process_reconnect( - inner: &RefCount, - router: &mut Router, - server: Option, - force: bool, - tx: Option, -) -> Result<(), RedisError> { - _debug!(inner, "Maybe reconnecting to {:?} (force: {})", server, force); - - if let Some(server) = server { - let has_connection = router.connections.has_server_connection(&server); - _debug!(inner, "Has working connection: {}", has_connection); - - if has_connection && !force { - _debug!(inner, "Skip reconnecting to {}", server); - if let Some(mut tx) = tx { - let _ = tx.send(Ok(Resp3Frame::Null)); - } - - return Ok(()); - } - } - - if !force && router.has_healthy_centralized_connection() { - _debug!(inner, "Skip reconnecting to centralized host"); - if let Some(mut tx) = tx { - let _ = tx.send(Ok(Resp3Frame::Null)); - } - return Ok(()); - } - - _debug!(inner, "Starting reconnection loop..."); - if let Err(e) = utils::reconnect_with_policy(inner, router).await { - if let Some(mut tx) = tx { - let _ = tx.send(Err(e.clone())); - } - - Err(e) - } else { - if let Some(mut tx) = tx { - let _ = tx.send(Ok(Resp3Frame::Null)); - } - - Ok(()) - } -} - -#[cfg(feature = "replicas")] -#[allow(unused_mut)] -async fn process_sync_replicas( - inner: &RefCount, - router: &mut Router, - mut tx: OneshotSender>, - reset: bool, -) -> Result<(), RedisError> { - let result = utils::sync_replicas_with_policy(inner, router, reset).await; - let _ = tx.send(result); - Ok(()) -} - -/// Sync and update the cached cluster state. -#[allow(unused_mut)] -async fn process_sync_cluster( - inner: &RefCount, - router: &mut Router, - mut tx: OneshotSender>, -) -> Result<(), RedisError> { - let result = utils::sync_cluster_with_policy(inner, router).await; - let _ = tx.send(result.clone()); - result -} - -/// Send a single command to the server(s). -async fn process_normal_command( - inner: &RefCount, - router: &mut Router, - command: RedisCommand, -) -> Result<(), RedisError> { - write_with_backpressure_t(inner, router, command, false).await -} - -/// Read the set of active connections managed by the client. -#[allow(unused_mut)] -fn process_connections( - inner: &RefCount, - router: &Router, - mut tx: OneshotSender>, -) -> Result<(), RedisError> { - #[allow(unused_mut)] - let mut connections = router.connections.active_connections(); - #[cfg(feature = "replicas")] - connections.extend(router.replicas.writers.keys().cloned()); - - _debug!(inner, "Active connections: {:?}", connections); - let _ = tx.send(connections); - Ok(()) -} - -/// Process any kind of router command. -async fn process_command( - inner: &RefCount, - router: &mut Router, - command: RouterCommand, -) -> Result<(), RedisError> { - match command { - RouterCommand::Ask { server, slot, command } => process_ask(inner, router, server, slot, command).await, - RouterCommand::Moved { server, slot, command } => process_moved(inner, router, server, slot, command).await, - RouterCommand::SyncCluster { tx } => process_sync_cluster(inner, router, tx).await, - #[cfg(feature = "transactions")] - RouterCommand::Transaction { - commands, - watched, - id, - tx, - abort_on_error, - } => transactions::run(inner, router, commands, watched, id, abort_on_error, tx).await, - RouterCommand::Pipeline { commands } => process_pipeline(inner, router, commands).await, - RouterCommand::Command(command) => process_normal_command(inner, router, command).await, - RouterCommand::Connections { tx } => process_connections(inner, router, tx), - #[cfg(feature = "replicas")] - RouterCommand::SyncReplicas { tx, reset } => process_sync_replicas(inner, router, tx, reset).await, - #[cfg(not(feature = "replicas"))] - RouterCommand::Reconnect { server, force, tx } => process_reconnect(inner, router, server, force, tx).await, - #[cfg(feature = "replicas")] - RouterCommand::Reconnect { - server, - force, - tx, - replica, - } => process_replica_reconnect(inner, router, server, force, tx, replica).await, - } -} - -/// Start processing commands from the client front end. -async fn process_commands( - inner: &RefCount, - router: &mut Router, - rx: &mut CommandReceiver, -) -> Result<(), RedisError> { - _debug!(inner, "Starting command processing stream..."); - while let Some(command) = rx.recv().await { - inner.counters.decr_cmd_buffer_len(); - - _trace!(inner, "Recv command: {:?}", command); - if let Err(e) = process_command(inner, router, command).await { - // errors on this interface end the client connection task - if e.is_canceled() { - break; - } else { - _error!(inner, "Disconnecting after error processing command: {:?}", e); - let _ = router.disconnect_all().await; - router.clear_retry_buffer(); - return Err(e); - } - } - } - - _debug!(inner, "Disconnecting after command stream closes."); - let _ = router.disconnect_all().await; - router.clear_retry_buffer(); - Ok(()) -} - -/// Start the command processing stream, initiating new connections in the process. -pub async fn start(inner: &RefCount) -> Result<(), RedisError> { - #[cfg(feature = "mocks")] - if let Some(ref mocks) = inner.config.mocks { - return mocking::start(inner, mocks).await; - } - - let mut rx = match inner.take_command_rx() { - Some(rx) => rx, - None => { - // the `_lock` field on inner synchronizes the getters/setters on the command channel halves, so if this field - // is None then another task must have set and removed the receiver concurrently. - return Err(RedisError::new( - RedisErrorKind::Config, - "Another connection task is already running.", - )); - }, - }; - - inner.reset_reconnection_attempts(); - let mut router = Router::new(inner); - _debug!(inner, "Initializing router with policy: {:?}", inner.reconnect_policy()); - let result = if inner.config.fail_fast { - if let Err(e) = Box::pin(router.connect()).await { - inner.notifications.broadcast_connect(Err(e.clone())); - inner.notifications.broadcast_error(e.clone()); - Err(e) - } else { - client_utils::set_client_state(&inner.state, ClientState::Connected); - inner.notifications.broadcast_connect(Ok(())); - Ok(()) - } - } else { - utils::reconnect_with_policy(inner, &mut router).await - }; - - if let Err(error) = result { - inner.store_command_rx(rx, false); - Err(error) - } else { - let result = Box::pin(process_commands(inner, &mut router, &mut rx)).await; - inner.store_command_rx(rx, false); - result - } -} - -#[cfg(feature = "mocks")] -#[allow(unused_mut)] -mod mocking { - use super::*; - use crate::{modules::mocks::Mocks, protocol::utils as protocol_utils}; - - /// Process any kind of router command. - pub fn process_command(mocks: &RefCount, command: RouterCommand) -> Result<(), RedisError> { - match command { - #[cfg(feature = "transactions")] - RouterCommand::Transaction { commands, mut tx, .. } => { - let mocked = commands.into_iter().skip(1).map(|c| c.to_mocked()).collect(); - - match mocks.process_transaction(mocked) { - Ok(result) => { - let _ = tx.send(Ok(protocol_utils::mocked_value_to_frame(result))); - Ok(()) - }, - Err(err) => { - let _ = tx.send(Err(err)); - Ok(()) - }, - } - }, - RouterCommand::Pipeline { commands } => { - for mut command in commands.into_iter() { - let mocked = command.to_mocked(); - let result = mocks.process_command(mocked).map(protocol_utils::mocked_value_to_frame); - - command.respond_to_caller(result); - } - - Ok(()) - }, - RouterCommand::Command(mut command) => { - let result = mocks - .process_command(command.to_mocked()) - .map(protocol_utils::mocked_value_to_frame); - command.respond_to_caller(result); - - Ok(()) - }, - _ => Err(RedisError::new(RedisErrorKind::Unknown, "Unimplemented.")), - } - } - - pub async fn process_commands( - inner: &RefCount, - mocks: &RefCount, - rx: &mut CommandReceiver, - ) -> Result<(), RedisError> { - while let Some(command) = rx.recv().await { - inner.counters.decr_cmd_buffer_len(); - - _trace!(inner, "Recv mock command: {:?}", command); - if let Err(e) = process_command(mocks, command) { - // errors on this interface end the client connection task - _error!(inner, "Ending early after error processing mock command: {:?}", e); - if e.is_canceled() { - break; - } else { - return Err(e); - } - } - } - - Ok(()) - } - - pub async fn start(inner: &RefCount, mocks: &RefCount) -> Result<(), RedisError> { - _debug!(inner, "Starting mocking layer"); - - #[cfg(feature = "glommio")] - glommio::yield_if_needed().await; - #[cfg(not(feature = "glommio"))] - tokio::task::yield_now().await; - - let mut rx = match inner.take_command_rx() { - Some(rx) => rx, - None => { - return Err(RedisError::new( - RedisErrorKind::Config, - "Redis client is already initialized.", - )) - }, - }; - - inner.notifications.broadcast_connect(Ok(())); - let result = process_commands(inner, mocks, &mut rx).await; - inner.store_command_rx(rx, false); - result - } -} diff --git a/src/router/mod.rs b/src/router/mod.rs deleted file mode 100644 index d3043244..00000000 --- a/src/router/mod.rs +++ /dev/null @@ -1,970 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - modules::inner::RedisClientInner, - protocol::{ - command::{RedisCommand, RouterReceiver}, - connection::{self, CommandBuffer, Counters, RedisWriter}, - types::{ClusterRouting, Server}, - }, - runtime::RefCount, - trace, - utils as client_utils, -}; -use futures::future::try_join_all; -use semver::Version; -use std::{ - collections::{HashMap, VecDeque}, - fmt, - fmt::Formatter, - time::Duration, -}; - -#[cfg(feature = "transactions")] -use crate::runtime::oneshot_channel; -#[cfg(feature = "transactions")] -use crate::{protocol::command::ClusterErrorKind, protocol::responders::ResponseKind}; -#[cfg(feature = "replicas")] -use std::collections::HashSet; - -pub mod centralized; -pub mod clustered; -pub mod commands; -pub mod reader; -pub mod replicas; -pub mod responses; -pub mod sentinel; -pub mod types; -pub mod utils; - -#[cfg(feature = "transactions")] -pub mod transactions; - -#[cfg(feature = "replicas")] -use crate::router::replicas::Replicas; - -/// The result of an attempt to send a command to the server. -// This is not an ideal pattern, but it mostly comes from the requirement that the shared buffer interface take -// ownership over the command. -pub enum Written { - /// Apply backpressure to the command before retrying. - Backpressure((RedisCommand, Backpressure)), - /// Indicates that the command was sent to the associated server and whether the socket was flushed. - Sent((Server, bool)), - /// Indicates that the command was sent to all servers. - SentAll, - /// The command could not be written since the connection is down. - Disconnected((Option, Option, RedisError)), - /// Ignore the result and move on to the next command. - Ignore, - /// The command could not be routed to any server. - NotFound(RedisCommand), - /// A fatal error that should interrupt the router. - Error((RedisError, Option)), - /// Restart the write process on a primary node connection. - #[cfg(feature = "replicas")] - Fallback(RedisCommand), -} - -impl fmt::Display for Written { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", match self { - Written::Backpressure(_) => "Backpressure", - Written::Sent(_) => "Sent", - Written::SentAll => "SentAll", - Written::Disconnected(_) => "Disconnected", - Written::Ignore => "Ignore", - Written::NotFound(_) => "NotFound", - Written::Error(_) => "Error", - #[cfg(feature = "replicas")] - Written::Fallback(_) => "Fallback", - }) - } -} - -pub enum Backpressure { - /// The amount of time to wait. - Wait(Duration), - /// Block the client until the command receives a response. - Block, - /// Return a backpressure error to the caller of the command. - Error(RedisError), -} - -impl Backpressure { - /// Apply the backpressure policy. - pub async fn wait( - self, - inner: &RefCount, - command: &mut RedisCommand, - ) -> Result, RedisError> { - match self { - Backpressure::Error(e) => Err(e), - Backpressure::Wait(duration) => { - _debug!(inner, "Backpressure policy (wait): {:?}", duration); - trace::backpressure_event(command, Some(duration.as_millis())); - inner.wait_with_interrupt(duration).await?; - Ok(None) - }, - Backpressure::Block => { - _debug!(inner, "Backpressure (block)"); - trace::backpressure_event(command, None); - if !command.has_router_channel() { - _trace!( - inner, - "Blocking router for backpressure for {}", - command.kind.to_str_debug() - ); - command.skip_backpressure = true; - Ok(Some(command.create_router_channel())) - } else { - Ok(None) - } - }, - } - } -} - -/// Connection maps for the supported deployment types. -pub enum Connections { - Centralized { - /// The connection to the primary server. - writer: Option, - }, - Clustered { - /// The cached cluster routing table used for mapping keys to server IDs. - cache: ClusterRouting, - /// A map of server IDs and connections. - writers: HashMap, - }, - Sentinel { - /// The connection to the primary server. - writer: Option, - }, -} - -impl Connections { - pub fn new_centralized() -> Self { - Connections::Centralized { writer: None } - } - - pub fn new_sentinel() -> Self { - Connections::Sentinel { writer: None } - } - - pub fn new_clustered() -> Self { - Connections::Clustered { - cache: ClusterRouting::new(), - writers: HashMap::new(), - } - } - - /// Discover and return a mapping of replica nodes to their associated primary node. - #[cfg(feature = "replicas")] - pub async fn replica_map( - &mut self, - inner: &RefCount, - ) -> Result, RedisError> { - Ok(match self { - Connections::Centralized { ref mut writer } | Connections::Sentinel { ref mut writer } => { - if let Some(writer) = writer { - writer - .discover_replicas(inner) - .await? - .into_iter() - .map(|replica| (replica, writer.server.clone())) - .collect() - } else { - HashMap::new() - } - }, - Connections::Clustered { ref writers, .. } => { - let mut out = HashMap::with_capacity(writers.len()); - - for primary in writers.keys() { - let replicas = inner - .with_cluster_state(|state| Ok(state.replicas(primary))) - .ok() - .unwrap_or_default(); - - for replica in replicas.into_iter() { - out.insert(replica, primary.clone()); - } - } - out - }, - }) - } - - /// Whether the connection map has a connection to the provided server`. - pub fn has_server_connection(&mut self, server: &Server) -> bool { - match self { - Connections::Centralized { ref mut writer } | Connections::Sentinel { ref mut writer } => { - if let Some(writer) = writer.as_mut() { - if writer.server == *server { - writer.is_working() - } else { - false - } - } else { - false - } - }, - Connections::Clustered { ref mut writers, .. } => { - for (_, writer) in writers.iter_mut() { - if writer.server == *server { - return writer.is_working(); - } - } - - false - }, - } - } - - /// Get the connection writer half for the provided server. - pub fn get_connection_mut(&mut self, server: &Server) -> Option<&mut RedisWriter> { - match self { - Connections::Centralized { ref mut writer } => { - writer - .as_mut() - .and_then(|writer| if writer.server == *server { Some(writer) } else { None }) - }, - Connections::Sentinel { ref mut writer } => { - writer - .as_mut() - .and_then(|writer| if writer.server == *server { Some(writer) } else { None }) - }, - Connections::Clustered { ref mut writers, .. } => writers.get_mut(server), - } - } - - /// Initialize the underlying connection(s) and update the cached backchannel information. - pub async fn initialize( - &mut self, - inner: &RefCount, - buffer: &mut VecDeque, - ) -> Result<(), RedisError> { - let result = if inner.config.server.is_clustered() { - Box::pin(clustered::initialize_connections(inner, self, buffer)).await - } else if inner.config.server.is_centralized() || inner.config.server.is_unix_socket() { - Box::pin(centralized::initialize_connection(inner, self, buffer)).await - } else if inner.config.server.is_sentinel() { - Box::pin(sentinel::initialize_connection(inner, self, buffer)).await - } else { - return Err(RedisError::new(RedisErrorKind::Config, "Invalid client configuration.")); - }; - - // TODO clean this up - if result.is_ok() { - if let Some(version) = self.server_version() { - inner.server_state.write().kind.set_server_version(version); - } - - let mut backchannel = inner.backchannel.write().await; - backchannel.connection_ids = self.connection_ids(); - } - result - } - - /// Read the counters associated with a connection to a server. - pub fn counters(&self, server: Option<&Server>) -> Option<&Counters> { - match self { - Connections::Centralized { ref writer } => writer.as_ref().map(|w| &w.counters), - Connections::Sentinel { ref writer, .. } => writer.as_ref().map(|w| &w.counters), - Connections::Clustered { ref writers, .. } => { - server.and_then(|server| writers.get(server).map(|w| &w.counters)) - }, - } - } - - /// Read the server version, if known. - pub fn server_version(&self) -> Option { - match self { - Connections::Centralized { ref writer } => writer.as_ref().and_then(|w| w.version.clone()), - Connections::Clustered { ref writers, .. } => writers.iter().find_map(|(_, w)| w.version.clone()), - Connections::Sentinel { ref writer, .. } => writer.as_ref().and_then(|w| w.version.clone()), - } - } - - /// Disconnect from the provided server, using the default centralized connection if `None` is provided. - pub async fn disconnect(&mut self, inner: &RefCount, server: Option<&Server>) -> CommandBuffer { - match self { - Connections::Centralized { ref mut writer } => { - if let Some(writer) = writer.take() { - _debug!(inner, "Disconnecting from {}", writer.server); - writer.graceful_close().await - } else { - Vec::new() - } - }, - Connections::Clustered { ref mut writers, .. } => { - let mut out = VecDeque::new(); - - if let Some(server) = server { - if let Some(writer) = writers.remove(server) { - _debug!(inner, "Disconnecting from {}", writer.server); - let commands = writer.graceful_close().await; - out.extend(commands); - } - } - out.into_iter().collect() - }, - Connections::Sentinel { ref mut writer } => { - if let Some(writer) = writer.take() { - _debug!(inner, "Disconnecting from {}", writer.server); - writer.graceful_close().await - } else { - Vec::new() - } - }, - } - } - - /// Disconnect and clear local state for all connections, returning all in-flight commands. - pub async fn disconnect_all(&mut self, inner: &RefCount) -> CommandBuffer { - match self { - Connections::Centralized { ref mut writer } => { - if let Some(writer) = writer.take() { - _debug!(inner, "Disconnecting from {}", writer.server); - writer.graceful_close().await - } else { - Vec::new() - } - }, - Connections::Clustered { ref mut writers, .. } => { - let mut out = VecDeque::new(); - for (_, writer) in writers.drain() { - _debug!(inner, "Disconnecting from {}", writer.server); - let commands = writer.graceful_close().await; - out.extend(commands.into_iter()); - } - out.into_iter().collect() - }, - Connections::Sentinel { ref mut writer } => { - if let Some(writer) = writer.take() { - _debug!(inner, "Disconnecting from {}", writer.server); - writer.graceful_close().await - } else { - Vec::new() - } - }, - } - } - - /// Read a map of connection IDs (via `CLIENT ID`) for each inner connections. - pub fn connection_ids(&self) -> HashMap { - let mut out = HashMap::new(); - - match self { - Connections::Centralized { writer } => { - if let Some(writer) = writer { - if let Some(id) = writer.id { - out.insert(writer.server.clone(), id); - } - } - }, - Connections::Sentinel { writer, .. } => { - if let Some(writer) = writer { - if let Some(id) = writer.id { - out.insert(writer.server.clone(), id); - } - } - }, - Connections::Clustered { writers, .. } => { - for (server, writer) in writers.iter() { - if let Some(id) = writer.id { - out.insert(server.clone(), id); - } - } - }, - } - - out - } - - /// Flush the socket(s) associated with each server if they have pending frames. - pub async fn check_and_flush(&mut self, inner: &RefCount) -> Result<(), RedisError> { - _trace!(inner, "Checking and flushing sockets..."); - - match self { - Connections::Centralized { ref mut writer } => { - if let Some(writer) = writer { - writer.flush().await - } else { - Ok(()) - } - }, - Connections::Sentinel { ref mut writer, .. } => { - if let Some(writer) = writer { - writer.flush().await - } else { - Ok(()) - } - }, - Connections::Clustered { ref mut writers, .. } => { - try_join_all(writers.values_mut().map(|writer| writer.flush())) - .await - .map(|_| ()) - }, - } - } - - /// Send a command to the server(s). - pub async fn write( - &mut self, - inner: &RefCount, - command: RedisCommand, - force_flush: bool, - ) -> Written { - match self { - Connections::Clustered { - ref mut writers, - ref mut cache, - } => clustered::write(inner, writers, cache, command, force_flush).await, - Connections::Centralized { ref mut writer } => centralized::write(inner, writer, command, force_flush).await, - Connections::Sentinel { ref mut writer, .. } => centralized::write(inner, writer, command, force_flush).await, - } - } - - /// Send a command to all servers in a cluster. - pub async fn write_all_cluster(&mut self, inner: &RefCount, command: RedisCommand) -> Written { - if let Connections::Clustered { ref mut writers, .. } = self { - if let Err(error) = clustered::send_all_cluster_command(inner, writers, command).await { - Written::Disconnected((None, None, error)) - } else { - Written::SentAll - } - } else { - Written::Error(( - RedisError::new(RedisErrorKind::Config, "Expected clustered configuration."), - None, - )) - } - } - - /// Check if the provided `server` node owns the provided `slot`. - pub fn check_cluster_owner(&self, slot: u16, server: &Server) -> bool { - match self { - Connections::Clustered { ref cache, .. } => cache - .get_server(slot) - .map(|owner| { - trace!("Comparing cached cluster owner for {}: {} == {}", slot, owner, server); - owner == server - }) - .unwrap_or(false), - _ => false, - } - } - - /// Connect or reconnect to the provided `host:port`. - pub async fn add_connection( - &mut self, - inner: &RefCount, - server: &Server, - ) -> Result<(), RedisError> { - if let Connections::Clustered { ref mut writers, .. } = self { - let mut transport = connection::create(inner, server, None).await?; - transport.setup(inner, None).await?; - - let (server, writer) = connection::split(inner, transport, false, clustered::spawn_reader_task)?; - writers.insert(server, writer); - Ok(()) - } else { - Err(RedisError::new( - RedisErrorKind::Config, - "Expected clustered configuration.", - )) - } - } - - /// Read the list of active/working connections. - pub fn active_connections(&self) -> Vec { - match self { - Connections::Clustered { ref writers, .. } => writers - .iter() - .filter_map(|(server, writer)| { - if writer.is_working() { - Some(server.clone()) - } else { - None - } - }) - .collect(), - Connections::Centralized { ref writer } | Connections::Sentinel { ref writer, .. } => writer - .as_ref() - .and_then(|writer| { - if writer.is_working() { - Some(vec![writer.server.clone()]) - } else { - None - } - }) - .unwrap_or(Vec::new()), - } - } -} - -/// A struct for routing commands to the server(s). -pub struct Router { - /// The connection map for each deployment type. - pub connections: Connections, - /// The inner client state associated with the router. - pub inner: RefCount, - /// Storage for commands that should be deferred or retried later. - pub buffer: VecDeque, - /// The replica routing interface. - #[cfg(feature = "replicas")] - pub replicas: Replicas, -} - -impl Router { - /// Create a new `Router` without connecting to the server(s). - pub fn new(inner: &RefCount) -> Self { - let connections = if inner.config.server.is_clustered() { - Connections::new_clustered() - } else if inner.config.server.is_sentinel() { - Connections::new_sentinel() - } else { - Connections::new_centralized() - }; - - Router { - buffer: VecDeque::new(), - inner: inner.clone(), - connections, - #[cfg(feature = "replicas")] - replicas: Replicas::new(), - } - } - - /// Read the server that should receive the provided command. - #[cfg(any(feature = "transactions", feature = "replicas"))] - pub fn find_connection(&self, command: &RedisCommand) -> Option<&Server> { - match self.connections { - Connections::Centralized { ref writer } => writer.as_ref().map(|w| &w.server), - Connections::Sentinel { ref writer } => writer.as_ref().map(|w| &w.server), - Connections::Clustered { ref cache, .. } => command.cluster_hash().and_then(|slot| cache.get_server(slot)), - } - } - - pub fn has_healthy_centralized_connection(&self) -> bool { - match self.connections { - Connections::Centralized { ref writer } | Connections::Sentinel { ref writer } => { - writer.as_ref().map(|w| w.is_working()).unwrap_or(false) - }, - Connections::Clustered { .. } => false, - } - } - - /// Attempt to send the command to the server. - pub async fn write(&mut self, command: RedisCommand, force_flush: bool) -> Written { - let send_all_cluster_nodes = - self.inner.config.server.is_clustered() && (command.is_all_cluster_nodes() || command.kind.closes_connection()); - - if command.write_attempts >= 1 { - self.inner.counters.incr_redelivery_count(); - } - if send_all_cluster_nodes { - self.connections.write_all_cluster(&self.inner, command).await - } else { - self.connections.write(&self.inner, command, force_flush).await - } - } - - /// Write a command to a replica node if possible, falling back to a primary node if configured. - #[cfg(feature = "replicas")] - pub async fn write_replica(&mut self, mut command: RedisCommand, force_flush: bool) -> Written { - if !command.use_replica { - return self.write(command, force_flush).await; - } - - let primary = match self.find_connection(&command) { - Some(server) => server.clone(), - None => { - return if self.inner.connection.replica.primary_fallback { - debug!( - "{}: Fallback to primary node connection for {} ({})", - self.inner.id, - command.kind.to_str_debug(), - command.debug_id() - ); - - command.use_replica = false; - self.write(command, force_flush).await - } else { - command.finish( - &self.inner, - Err(RedisError::new( - RedisErrorKind::Replica, - "Missing primary node connection.", - )), - ); - - Written::Ignore - } - }, - }; - - let result = self.replicas.write(&self.inner, &primary, command, force_flush).await; - match result { - Written::Fallback(mut command) => { - debug!( - "{}: Fall back to primary node for {} ({}) after replica error", - self.inner.id, - command.kind.to_str_debug(), - command.debug_id(), - ); - - command.use_replica = false; - self.write(command, force_flush).await - }, - _ => result, - } - } - - /// Write a command to a replica node if possible, falling back to a primary node if configured. - #[cfg(not(feature = "replicas"))] - pub async fn write_replica(&mut self, command: RedisCommand, force_flush: bool) -> Written { - self.write(command, force_flush).await - } - - /// Attempt to write the command to a specific server without backpressure. - pub async fn write_direct(&mut self, mut command: RedisCommand, server: &Server) -> Written { - debug!( - "{}: Direct write `{}` command to {}, ID: {}", - self.inner.id, - command.kind.to_str_debug(), - server, - command.debug_id() - ); - - let writer = match self.connections.get_connection_mut(server) { - Some(writer) => writer, - None => { - trace!("{}: Missing connection to {}", self.inner.id, server); - return Written::NotFound(command); - }, - }; - let frame = match utils::prepare_command(&self.inner, &writer.counters, &mut command) { - Ok((frame, _)) => frame, - Err(e) => { - warn!( - "{}: Frame encoding error for {}", - self.inner.id, - command.kind.to_str_debug() - ); - // do not retry commands that trigger frame encoding errors - command.finish(&self.inner, Err(e)); - return Written::Ignore; - }, - }; - let blocks_connection = command.blocks_connection(); - command.write_attempts += 1; - - if !writer.is_working() { - let error = RedisError::new(RedisErrorKind::IO, "Connection closed."); - debug!("{}: Error sending command: {:?}", self.inner.id, error); - return Written::Disconnected((Some(writer.server.clone()), Some(command), error)); - } - - let no_incr = command.has_no_responses(); - writer.push_command(&self.inner, command); - if let Err(err) = writer.write_frame(frame, true, no_incr).await { - Written::Disconnected((Some(writer.server.clone()), None, err)) - } else { - if blocks_connection { - self.inner.backchannel.write().await.set_blocked(&writer.server); - } - Written::Sent((writer.server.clone(), true)) - } - } - - /// Disconnect from all the servers, moving the in-flight messages to the internal command buffer and triggering a - /// reconnection, if necessary. - pub async fn disconnect_all(&mut self) { - let commands = self.connections.disconnect_all(&self.inner).await; - self.buffer_commands(commands); - self.disconnect_replicas().await; - } - - /// Disconnect from all the servers, moving the in-flight messages to the internal command buffer and triggering a - /// reconnection, if necessary. - #[cfg(feature = "replicas")] - pub async fn disconnect_replicas(&mut self) { - if let Err(e) = self.replicas.clear_connections(&self.inner).await { - warn!("{}: Error disconnecting replicas: {:?}", self.inner.id, e); - } - } - - #[cfg(not(feature = "replicas"))] - pub async fn disconnect_replicas(&mut self) {} - - /// Add the provided commands to the retry buffer. - pub fn buffer_commands(&mut self, commands: impl IntoIterator) { - for command in commands.into_iter() { - self.buffer_command(command); - } - } - - /// Add the provided command to the retry buffer. - pub fn buffer_command(&mut self, command: RedisCommand) { - trace!( - "{}: Adding {} ({}) command to retry buffer.", - self.inner.id, - command.kind.to_str_debug(), - command.debug_id() - ); - self.buffer.push_back(command); - } - - /// Clear all the commands in the retry buffer. - pub fn clear_retry_buffer(&mut self) { - trace!( - "{}: Clearing retry buffer with {} commands.", - self.inner.id, - self.buffer.len() - ); - self.buffer.clear(); - } - - /// Connect to the server(s), discarding any previous connection state. - pub async fn connect(&mut self) -> Result<(), RedisError> { - self.disconnect_all().await; - let result = self.connections.initialize(&self.inner, &mut self.buffer).await; - - if result.is_ok() { - #[cfg(feature = "replicas")] - self.refresh_replica_routing().await?; - - Ok(()) - } else { - result - } - } - - /// Gracefully reset the replica routing table. - #[cfg(feature = "replicas")] - pub async fn refresh_replica_routing(&mut self) -> Result<(), RedisError> { - self.replicas.clear_routing(); - if let Err(e) = self.sync_replicas().await { - if !self.inner.ignore_replica_reconnect_errors() { - return Err(e); - } - } - - Ok(()) - } - - /// Sync the cached cluster state with the server via `CLUSTER SLOTS`. - /// - /// This will also create new connections or drop old connections as needed. - pub async fn sync_cluster(&mut self) -> Result<(), RedisError> { - let result = clustered::sync(&self.inner, &mut self.connections, &mut self.buffer).await; - - if result.is_ok() { - #[cfg(feature = "replicas")] - self.refresh_replica_routing().await?; - self.retry_buffer().await; - } - - result - } - - /// Rebuild the cached replica routing table based on the primary node connections. - #[cfg(feature = "replicas")] - pub async fn sync_replicas(&mut self) -> Result<(), RedisError> { - debug!("{}: Syncing replicas...", self.inner.id); - self.replicas.drop_broken_connections().await; - let old_connections = self.replicas.active_connections(); - let new_replica_map = self.connections.replica_map(&self.inner).await?; - - let old_connections_idx: HashSet<_> = old_connections.iter().collect(); - let new_connections_idx: HashSet<_> = new_replica_map.keys().collect(); - let remove: Vec<_> = old_connections_idx.difference(&new_connections_idx).collect(); - - for server in remove.into_iter() { - debug!("{}: Dropping replica connection to {}", self.inner.id, server); - self.replicas.drop_writer(server).await; - self.replicas.remove_replica(server); - } - - for (mut replica, primary) in new_replica_map.into_iter() { - let should_use = if let Some(filter) = self.inner.connection.replica.filter.as_ref() { - filter.filter(&primary, &replica).await - } else { - true - }; - - if should_use { - replicas::map_replica_tls_names(&self.inner, &primary, &mut replica); - - self - .replicas - .add_connection(&self.inner, primary, replica, false) - .await?; - } - } - - self - .inner - .server_state - .write() - .update_replicas(self.replicas.routing_table()); - Ok(()) - } - - /// Attempt to replay all queued commands on the internal buffer without backpressure. - pub async fn retry_buffer(&mut self) { - let mut failed_commands: VecDeque<_> = VecDeque::new(); - let mut commands: VecDeque<_> = self.buffer.drain(..).collect(); - #[cfg(feature = "replicas")] - commands.extend(self.replicas.take_retry_buffer()); - - while let Some(mut command) = commands.pop_front() { - if client_utils::read_bool_atomic(&command.timed_out) { - debug!( - "{}: Ignore retrying timed out command: {}", - self.inner.id, - command.kind.to_str_debug() - ); - continue; - } - - if let Err(e) = command.decr_check_attempted() { - command.finish(&self.inner, Err(e)); - continue; - } - command.skip_backpressure = true; - trace!( - "{}: Retry `{}` ({}) command, attempts left: {}", - self.inner.id, - command.kind.to_str_debug(), - command.debug_id(), - command.attempts_remaining, - ); - - let result = if command.use_replica { - self.write_replica(command, true).await - } else { - self.write(command, true).await - }; - - match result { - Written::Disconnected((server, command, error)) => { - if let Some(command) = command { - failed_commands.push_back(command); - } - - debug!( - "{}: Disconnect while retrying after write error: {:?}", - &self.inner.id, error - ); - self.connections.disconnect(&self.inner, server.as_ref()).await; - utils::defer_reconnect(&self.inner); - break; - }, - Written::NotFound(command) => { - failed_commands.push_back(command); - - warn!( - "{}: Disconnect and re-sync cluster state after routing error while retrying commands.", - self.inner.id - ); - self.disconnect_all().await; - utils::defer_reconnect(&self.inner); - break; - }, - Written::Error((error, command)) => { - warn!("{}: Error replaying command: {:?}", self.inner.id, error); - if let Some(command) = command { - command.finish(&self.inner, Err(error)); - } - self.disconnect_all().await; - utils::defer_reconnect(&self.inner); - break; - }, - _ => {}, - } - } - - failed_commands.extend(commands); - self.buffer_commands(failed_commands); - } - - /// Check each connection for pending frames that have not been flushed, and flush the connection if needed. - #[cfg(feature = "replicas")] - pub async fn check_and_flush(&mut self) -> Result<(), RedisError> { - if let Err(e) = self.replicas.check_and_flush().await { - warn!("{}: Error flushing replica connections: {:?}", self.inner.id, e); - } - self.connections.check_and_flush(&self.inner).await - } - - #[cfg(not(feature = "replicas"))] - pub async fn check_and_flush(&mut self) -> Result<(), RedisError> { - self.connections.check_and_flush(&self.inner).await - } - - /// Returns whether the provided `server` owns the provided `slot`. - pub fn cluster_node_owns_slot(&self, slot: u16, server: &Server) -> bool { - match self.connections { - Connections::Clustered { ref cache, .. } => cache.get_server(slot).map(|node| node == server).unwrap_or(false), - _ => false, - } - } - - /// Modify connection state according to the cluster redirection error. - /// - /// * Synchronizes the cached cluster state in response to MOVED - /// * Connects and sends `ASKING` to the provided server in response to ASKED - #[cfg(feature = "transactions")] - pub async fn cluster_redirection( - &mut self, - kind: &ClusterErrorKind, - slot: u16, - server: &Server, - ) -> Result<(), RedisError> { - debug!( - "{}: Handling cluster redirect {:?} {} {}", - &self.inner.id, kind, slot, server - ); - - if *kind == ClusterErrorKind::Moved { - let should_sync = self - .inner - .with_cluster_state(|state| Ok(state.get_server(slot).map(|owner| server != owner).unwrap_or(true))) - .unwrap_or(true); - - if should_sync { - self.sync_cluster().await?; - } - } else if *kind == ClusterErrorKind::Ask { - if !self.connections.has_server_connection(server) { - self.connections.add_connection(&self.inner, server).await?; - self - .inner - .backchannel - .write() - .await - .update_connection_ids(&self.connections); - } - - // can't use request_response since there may be pipelined commands ahead of this - let (tx, rx) = oneshot_channel(); - let mut command = RedisCommand::new_asking(slot); - command.response = ResponseKind::Respond(Some(tx)); - command.skip_backpressure = true; - - match self.write_direct(command, server).await { - Written::Error((error, _)) => return Err(error), - Written::Disconnected((_, _, error)) => return Err(error), - Written::NotFound(_) => return Err(RedisError::new(RedisErrorKind::Cluster, "Connection not found.")), - _ => {}, - }; - - let _ = client_utils::timeout(rx, self.inner.internal_command_timeout()).await??; - } - - Ok(()) - } -} diff --git a/src/router/reader.rs b/src/router/reader.rs deleted file mode 100644 index 8b137891..00000000 --- a/src/router/reader.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/router/replicas.rs b/src/router/replicas.rs deleted file mode 100644 index 9c015913..00000000 --- a/src/router/replicas.rs +++ /dev/null @@ -1,555 +0,0 @@ -#[cfg(all(feature = "replicas", any(feature = "enable-native-tls", feature = "enable-rustls")))] -use crate::types::TlsHostMapping; -#[cfg(feature = "replicas")] -use crate::{ - error::{RedisError, RedisErrorKind}, - modules::inner::RedisClientInner, - protocol::{ - command::RedisCommand, - connection, - connection::{CommandBuffer, RedisWriter}, - }, - router::{centralized, clustered, utils, Written}, - runtime::RefCount, - types::Server, -}; -#[cfg(feature = "replicas")] -use std::{ - collections::{HashMap, VecDeque}, - fmt, - fmt::Formatter, -}; - -/// An interface used to filter the list of available replica nodes. -#[cfg(feature = "replicas")] -#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] -#[async_trait] -pub trait ReplicaFilter: Send + Sync + 'static { - /// Returns whether the replica node mapping can be used when routing commands to replicas. - #[allow(unused_variables)] - async fn filter(&self, primary: &Server, replica: &Server) -> bool { - true - } -} - -/// Configuration options for replica node connections. -#[cfg(feature = "replicas")] -#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] -#[derive(Clone)] -pub struct ReplicaConfig { - /// Whether the client should lazily connect to replica nodes. - /// - /// Default: `true` - pub lazy_connections: bool, - /// An optional interface for filtering available replica nodes. - /// - /// Default: `None` - pub filter: Option>, - /// Whether the client should ignore errors from replicas that occur when the max reconnection count is reached. - /// - /// Default: `true` - pub ignore_reconnection_errors: bool, - /// The number of times a command can fail with a replica connection error before being sent to a primary node. - /// - /// Default: `0` (unlimited) - pub connection_error_count: u32, - /// Whether the client should use the associated primary node if no replica exists that can serve a command. - /// - /// Default: `true` - pub primary_fallback: bool, -} - -#[cfg(feature = "replicas")] -impl fmt::Debug for ReplicaConfig { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("ReplicaConfig") - .field("lazy_connections", &self.lazy_connections) - .field("ignore_reconnection_errors", &self.ignore_reconnection_errors) - .field("connection_error_count", &self.connection_error_count) - .field("primary_fallback", &self.primary_fallback) - .finish() - } -} - -#[cfg(feature = "replicas")] -impl PartialEq for ReplicaConfig { - fn eq(&self, other: &Self) -> bool { - self.lazy_connections == other.lazy_connections - && self.ignore_reconnection_errors == other.ignore_reconnection_errors - && self.connection_error_count == other.connection_error_count - && self.primary_fallback == other.primary_fallback - } -} - -#[cfg(feature = "replicas")] -impl Eq for ReplicaConfig {} - -#[cfg(feature = "replicas")] -impl Default for ReplicaConfig { - fn default() -> Self { - ReplicaConfig { - lazy_connections: true, - filter: None, - ignore_reconnection_errors: true, - connection_error_count: 0, - primary_fallback: true, - } - } -} - -/// A container for round-robin routing among replica nodes. -// This implementation optimizes for next() at the cost of add() and remove() -#[derive(Clone, Debug, PartialEq, Eq, Default)] -#[cfg(feature = "replicas")] -#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] -pub struct ReplicaRouter { - counter: usize, - servers: Vec, -} - -#[cfg(feature = "replicas")] -impl ReplicaRouter { - /// Read the server that should receive the next command. - pub fn next(&mut self) -> Option<&Server> { - self.counter = (self.counter + 1) % self.servers.len(); - self.servers.get(self.counter) - } - - /// Conditionally add the server to the replica set. - pub fn add(&mut self, server: Server) { - if !self.servers.contains(&server) { - self.servers.push(server); - } - } - - /// Remove the server from the replica set. - pub fn remove(&mut self, server: &Server) { - self.servers = self.servers.drain(..).filter(|_server| server != _server).collect(); - } - - /// The size of the replica set. - pub fn len(&self) -> usize { - self.servers.len() - } - - /// Iterate over the replica set. - pub fn iter(&self) -> impl Iterator { - self.servers.iter() - } -} - -/// A container for round-robin routing to replica servers. -#[cfg(feature = "replicas")] -#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] -#[derive(Clone, Debug, Eq, PartialEq, Default)] -pub struct ReplicaSet { - /// A map of primary server IDs to a counter and set of replica server IDs. - servers: HashMap, -} - -#[cfg(feature = "replicas")] -#[allow(dead_code)] -impl ReplicaSet { - /// Create a new empty replica set. - pub fn new() -> ReplicaSet { - ReplicaSet { - servers: HashMap::new(), - } - } - - /// Add a replica node to the routing table. - pub fn add(&mut self, primary: Server, replica: Server) { - self.servers.entry(primary).or_default().add(replica); - } - - /// Remove a replica node mapping from the routing table. - pub fn remove(&mut self, primary: &Server, replica: &Server) { - let should_remove = if let Some(router) = self.servers.get_mut(primary) { - router.remove(replica); - router.len() == 0 - } else { - false - }; - - if should_remove { - self.servers.remove(primary); - } - } - - /// Remove the replica from all routing sets. - pub fn remove_replica(&mut self, replica: &Server) { - self.servers = self - .servers - .drain() - .filter_map(|(primary, mut routing)| { - routing.remove(replica); - - if routing.len() > 0 { - Some((primary, routing)) - } else { - None - } - }) - .collect(); - } - - /// Read the server ID of the next replica that should receive a command. - pub fn next_replica(&mut self, primary: &Server) -> Option<&Server> { - self.servers.get_mut(primary).and_then(|router| router.next()) - } - - /// Read all the replicas associated with the provided primary node. - pub fn replicas(&self, primary: &Server) -> impl Iterator { - self - .servers - .get(primary) - .map(|router| router.iter()) - .into_iter() - .flatten() - } - - /// Return a map of replica nodes to primary nodes. - pub fn to_map(&self) -> HashMap { - let mut out = HashMap::with_capacity(self.servers.len()); - for (primary, replicas) in self.servers.iter() { - for replica in replicas.iter() { - out.insert(replica.clone(), primary.clone()); - } - } - - out - } - - /// Read the set of all known replica nodes for all primary nodes. - pub fn all_replicas(&self) -> Vec { - let mut out = Vec::with_capacity(self.servers.len()); - for (_, replicas) in self.servers.iter() { - for replica in replicas.iter() { - out.push(replica.clone()); - } - } - - out - } - - /// Clear the routing table. - pub fn clear(&mut self) { - self.servers.clear(); - } -} - -/// A struct for routing commands to replica nodes. -#[cfg(feature = "replicas")] -pub struct Replicas { - pub(crate) writers: HashMap, - routing: ReplicaSet, - buffer: VecDeque, -} - -#[cfg(feature = "replicas")] -#[allow(dead_code)] -impl Replicas { - pub fn new() -> Replicas { - Replicas { - writers: HashMap::new(), - routing: ReplicaSet::new(), - buffer: VecDeque::new(), - } - } - - /// Sync the connection map in place based on the cached routing table. - pub async fn sync_connections(&mut self, inner: &RefCount) -> Result<(), RedisError> { - for (_, writer) in self.writers.drain() { - let commands = writer.graceful_close().await; - self.buffer.extend(commands); - } - - for (replica, primary) in self.routing.to_map() { - self.add_connection(inner, primary, replica, false).await?; - } - - Ok(()) - } - - /// Drop all connections and clear the cached routing table. - pub async fn clear_connections(&mut self, inner: &RefCount) -> Result<(), RedisError> { - self.routing.clear(); - self.sync_connections(inner).await - } - - /// Clear the cached routing table without dropping connections. - pub fn clear_routing(&mut self) { - self.routing.clear(); - } - - /// Connect to the replica and add it to the cached routing table. - pub async fn add_connection( - &mut self, - inner: &RefCount, - primary: Server, - replica: Server, - force: bool, - ) -> Result<(), RedisError> { - _debug!( - inner, - "Adding replica connection {} (replica) -> {} (primary)", - replica, - primary - ); - - if !inner.connection.replica.lazy_connections || force { - let mut transport = connection::create(inner, &replica, None).await?; - transport.setup(inner, None).await?; - - let (_, writer) = if inner.config.server.is_clustered() { - transport.readonly(inner, None).await?; - connection::split(inner, transport, true, clustered::spawn_reader_task)? - } else { - connection::split(inner, transport, true, centralized::spawn_reader_task)? - }; - - self.writers.insert(replica.clone(), writer); - } - - self.routing.add(primary, replica); - Ok(()) - } - - /// Drop the socket associated with the provided server. - pub async fn drop_writer(&mut self, replica: &Server) { - if let Some(writer) = self.writers.remove(replica) { - let commands = writer.graceful_close().await; - self.buffer.extend(commands); - } - } - - /// Remove the replica from the routing table. - pub fn remove_replica(&mut self, replica: &Server) { - self.routing.remove_replica(replica); - } - - /// Close the replica connection and optionally remove the replica from the routing table. - pub async fn remove_connection( - &mut self, - inner: &RefCount, - primary: &Server, - replica: &Server, - keep_routable: bool, - ) -> Result<(), RedisError> { - _debug!( - inner, - "Removing replica connection {} (replica) -> {} (primary)", - replica, - primary - ); - self.drop_writer(replica).await; - - if !keep_routable { - self.routing.remove(primary, replica); - } - Ok(()) - } - - /// Check and flush all the sockets managed by the replica routing state. - pub async fn check_and_flush(&mut self) -> Result<(), RedisError> { - for (_, writer) in self.writers.iter_mut() { - writer.flush().await?; - } - - Ok(()) - } - - /// Whether a working connection exists to any replica for the provided primary node. - pub fn has_replica_connection(&self, primary: &Server) -> bool { - for replica in self.routing.replicas(primary) { - if self.has_connection(replica) { - return true; - } - } - - false - } - - /// Whether a connection exists to the provided replica node. - pub fn has_connection(&self, replica: &Server) -> bool { - self.writers.get(replica).map(|w| w.is_working()).unwrap_or(false) - } - - /// Return a map of `replica` -> `primary` server identifiers. - pub fn routing_table(&self) -> HashMap { - self.routing.to_map() - } - - /// Check the active connections and drop any without a working reader task. - pub async fn drop_broken_connections(&mut self) { - let mut new_writers = HashMap::with_capacity(self.writers.len()); - for (server, writer) in self.writers.drain() { - if writer.is_working() { - new_writers.insert(server, writer); - } else { - let commands = writer.graceful_close().await; - self.buffer.extend(commands); - self.routing.remove_replica(&server); - } - } - - self.writers = new_writers; - } - - /// Read the set of all active connections. - pub fn active_connections(&self) -> Vec { - self - .writers - .iter() - .filter_map(|(server, writer)| { - if writer.is_working() { - Some(server.clone()) - } else { - None - } - }) - .collect() - } - - /// Send a command to one of the replicas associated with the provided primary server. - pub async fn write( - &mut self, - inner: &RefCount, - primary: &Server, - mut command: RedisCommand, - force_flush: bool, - ) -> Written { - let replica = match command.cluster_node { - Some(ref server) => server.clone(), - None => match self.routing.next_replica(primary) { - Some(replica) => replica.clone(), - None => { - // we do not know of any replica node associated with the primary node - return if inner.connection.replica.primary_fallback { - Written::Fallback(command) - } else { - command.finish( - inner, - Err(RedisError::new(RedisErrorKind::Replica, "Missing replica node.")), - ); - Written::Ignore - }; - }, - }, - }; - - _trace!( - inner, - "Found replica {} (primary: {}) for {} ({})", - replica, - primary, - command.kind.to_str_debug(), - command.debug_id() - ); - - let writer = match self.writers.get_mut(&replica) { - Some(writer) => writer, - None => { - // these errors indicate that we know a replica node should exist, but we are not connected or cannot - // connect to it. in this case we want to hide the error, trigger a reconnect, and retry the command later. - if inner.connection.replica.lazy_connections { - _debug!(inner, "Lazily adding {} replica connection", replica); - if let Err(e) = self.add_connection(inner, primary.clone(), replica.clone(), true).await { - // we tried connecting once but failed. - self.routing.remove_replica(&replica); - // since we didn't get to actually send the command - command.attempts_remaining += 1; - return Written::Disconnected((Some(replica.clone()), Some(command), e)); - } - - match self.writers.get_mut(&replica) { - Some(writer) => writer, - None => { - self.routing.remove_replica(&replica); - // the connection should be here if self.add_connection succeeded - return Written::Disconnected(( - Some(replica.clone()), - Some(command), - RedisError::new(RedisErrorKind::Replica, "Missing connection."), - )); - }, - } - } else { - // we don't have a connection to the replica and we're not configured to lazily create new ones - return Written::NotFound(command); - } - }, - }; - let (frame, should_flush) = match utils::prepare_command(inner, &writer.counters, &mut command) { - Ok((frame, should_flush)) => (frame, should_flush || force_flush), - Err(e) => { - _warn!(inner, "Frame encoding error for {}", command.kind.to_str_debug()); - // do not retry commands that trigger frame encoding errors - command.finish(inner, Err(e)); - return Written::Ignore; - }, - }; - - let blocks_connection = command.blocks_connection(); - _debug!( - inner, - "Sending {} ({}) to replica {}", - command.kind.to_str_debug(), - command.debug_id(), - replica - ); - command.write_attempts += 1; - - if !writer.is_working() { - let error = RedisError::new(RedisErrorKind::IO, "Connection closed."); - - _debug!( - inner, - "Error sending replica command {}: {:?}", - command.kind.to_str_debug(), - error - ); - self.routing.remove_replica(&writer.server); - return Written::Disconnected((Some(writer.server.clone()), Some(command), error)); - } - - writer.push_command(inner, command); - if let Err(err) = writer.write_frame(frame, should_flush, false).await { - self.routing.remove_replica(&writer.server); - Written::Disconnected((Some(writer.server.clone()), None, err)) - } else { - if blocks_connection { - inner.backchannel.write().await.set_blocked(&writer.server); - } - Written::Sent((writer.server.clone(), should_flush)) - } - } - - /// Take the commands stored for retry later. - pub fn take_retry_buffer(&mut self) -> CommandBuffer { - self.buffer.drain(..).collect() - } -} - -#[cfg(all(feature = "replicas", any(feature = "enable-native-tls", feature = "enable-rustls")))] -pub fn map_replica_tls_names(inner: &RefCount, primary: &Server, replica: &mut Server) { - let policy = match inner.config.tls { - Some(ref config) => &config.hostnames, - None => { - _trace!(inner, "Skip modifying TLS hostname for replicas."); - return; - }, - }; - if *policy == TlsHostMapping::None { - _trace!(inner, "Skip modifying TLS hostnames for replicas."); - return; - } - - replica.set_tls_server_name(policy, &primary.host); -} - -#[cfg(all( - feature = "replicas", - not(any(feature = "enable-native-tls", feature = "enable-rustls")) -))] -pub fn map_replica_tls_names(_: &RefCount, _: &Server, _: &mut Server) {} diff --git a/src/router/responses.rs b/src/router/responses.rs deleted file mode 100644 index 1cde3b52..00000000 --- a/src/router/responses.rs +++ /dev/null @@ -1,335 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - modules::inner::RedisClientInner, - protocol::{command::RedisCommand, types::Server, utils as protocol_utils, utils::pretty_error}, - runtime::RefCount, - trace, - types::{ClientState, KeyspaceEvent, Message, RedisKey, RedisValue}, - utils, -}; -use redis_protocol::{ - resp3::types::{BytesFrame as Resp3Frame, FrameKind, Resp3Frame as _Resp3Frame}, - types::PUBSUB_PUSH_PREFIX, -}; -use std::str; - -#[cfg(feature = "i-tracking")] -use crate::types::Invalidation; - -const KEYSPACE_PREFIX: &str = "__keyspace@"; -const KEYEVENT_PREFIX: &str = "__keyevent@"; -#[cfg(feature = "i-tracking")] -const INVALIDATION_CHANNEL: &str = "__redis__:invalidate"; - -fn parse_keyspace_notification(channel: &str, message: &RedisValue) -> Option { - if channel.starts_with(KEYEVENT_PREFIX) { - let parts: Vec<&str> = channel.splitn(2, '@').collect(); - if parts.len() < 2 { - return None; - } - - let suffix: Vec<&str> = parts[1].splitn(2, ':').collect(); - if suffix.len() < 2 { - return None; - } - - let db = suffix[0].replace("__", "").parse::().ok()?; - let operation = suffix[1].to_owned(); - let key: RedisKey = message.clone().try_into().ok()?; - - Some(KeyspaceEvent { db, key, operation }) - } else if channel.starts_with(KEYSPACE_PREFIX) { - let parts: Vec<&str> = channel.splitn(2, '@').collect(); - if parts.len() < 2 { - return None; - } - - let suffix: Vec<&str> = parts[1].splitn(2, ':').collect(); - if suffix.len() < 2 { - return None; - } - - let db = suffix[0].replace("__", "").parse::().ok()?; - let key: RedisKey = suffix[1].to_owned().into(); - let operation = message.as_string()?; - - Some(KeyspaceEvent { db, key, operation }) - } else { - None - } -} - -#[cfg(feature = "i-tracking")] -fn broadcast_pubsub_invalidation(inner: &RefCount, message: Message, server: &Server) { - if let Some(invalidation) = Invalidation::from_message(message, server) { - inner.notifications.broadcast_invalidation(invalidation); - } else { - _debug!( - inner, - "Dropping pubsub message on invalidation channel that cannot be parsed as an invalidation message." - ); - } -} - -#[cfg(not(feature = "i-tracking"))] -fn broadcast_pubsub_invalidation(_: &RefCount, _: Message, _: &Server) {} - -#[cfg(feature = "i-tracking")] -fn is_pubsub_invalidation(message: &Message) -> bool { - message.channel == INVALIDATION_CHANNEL -} - -#[cfg(not(feature = "i-tracking"))] -fn is_pubsub_invalidation(_: &Message) -> bool { - false -} - -#[cfg(feature = "i-tracking")] -fn broadcast_resp3_invalidation(inner: &RefCount, server: &Server, frame: Resp3Frame) { - if let Resp3Frame::Push { mut data, .. } = frame { - if data.len() != 2 { - return; - } - - // RESP3 example: Push { data: [BlobString { data: b"invalidate", attributes: None }, Array { data: - // [BlobString { data: b"foo", attributes: None }], attributes: None }], attributes: None } - if let Resp3Frame::Array { data, .. } = data[1].take() { - inner.notifications.broadcast_invalidation(Invalidation { - keys: data - .into_iter() - .filter_map(|f| f.as_bytes().map(|b| b.into())) - .collect(), - server: server.clone(), - }) - } - } -} - -#[cfg(not(feature = "i-tracking"))] -fn broadcast_resp3_invalidation(_: &RefCount, _: &Server, _: Resp3Frame) {} - -#[cfg(feature = "i-tracking")] -fn is_resp3_invalidation(frame: &Resp3Frame) -> bool { - // RESP3 example: Push { data: [BlobString { data: b"invalidate", attributes: None }, Array { data: - // [BlobString { data: b"foo", attributes: None }], attributes: None }], attributes: None } - if let Resp3Frame::Push { ref data, .. } = frame { - data - .first() - .and_then(|f| f.as_str()) - .map(|s| s == "invalidate") - .unwrap_or(false) - } else { - false - } -} - -// `SSUBSCRIBE` is intentionally not included so that we can handle MOVED errors. this works as long as we never -// pipeline ssubscribe calls. -fn is_subscribe_prefix(s: &str) -> bool { - s == "subscribe" || s == "psubscribe" -} - -fn is_unsubscribe_prefix(s: &str) -> bool { - s == "unsubscribe" || s == "punsubscribe" || s == "sunsubscribe" -} - -/// Whether the response frame represents a response to any of the subscription interface commands. -fn is_subscription_response(frame: &Resp3Frame) -> bool { - match frame { - Resp3Frame::Array { ref data, .. } | Resp3Frame::Push { ref data, .. } => { - if data.len() >= 3 && data.len() <= 4 { - // check for ["pubsub", "punsubscribe"|"sunsubscribe", ..] or ["punsubscribe"|"sunsubscribe", ..] - (data[0].as_str().map(|s| s == PUBSUB_PUSH_PREFIX).unwrap_or(false) - && data[1] - .as_str() - .map(|s| is_subscribe_prefix(s) || is_unsubscribe_prefix(s)) - .unwrap_or(false)) - || (data[0] - .as_str() - .map(|s| is_subscribe_prefix(s) || is_unsubscribe_prefix(s)) - .unwrap_or(false)) - } else { - false - } - }, - _ => false, - } -} - -#[cfg(not(feature = "i-tracking"))] -fn is_resp3_invalidation(_: &Resp3Frame) -> bool { - false -} - -/// Check if the frame is part of a pubsub message, and if so route it to any listeners. -/// -/// If not then return it to the caller for further processing. -pub fn check_pubsub_message( - inner: &RefCount, - server: &Server, - frame: Resp3Frame, -) -> Option { - if is_subscription_response(&frame) { - _debug!(inner, "Dropping unused subscription response."); - return None; - } - if is_resp3_invalidation(&frame) { - broadcast_resp3_invalidation(inner, server, frame); - return None; - } - - let is_pubsub = - frame.is_normal_pubsub_message() || frame.is_pattern_pubsub_message() || frame.is_shard_pubsub_message(); - if !is_pubsub { - return Some(frame); - } - - let span = trace::create_pubsub_span(inner, &frame); - _trace!(inner, "Processing pubsub message from {}.", server); - let parsed_frame = if let Some(ref span) = span { - #[allow(clippy::let_unit_value)] - let _guard = span.enter(); - protocol_utils::frame_to_pubsub(server, frame) - } else { - protocol_utils::frame_to_pubsub(server, frame) - }; - - let message = match parsed_frame { - Ok(data) => data, - Err(err) => { - _warn!(inner, "Invalid message on pubsub interface: {:?}", err); - return None; - }, - }; - if let Some(ref span) = span { - span.record("channel", &*message.channel); - } - - if is_pubsub_invalidation(&message) { - broadcast_pubsub_invalidation(inner, message, server); - } else if let Some(event) = parse_keyspace_notification(&message.channel, &message.value) { - inner.notifications.broadcast_keyspace(event); - } else { - inner.notifications.broadcast_pubsub(message); - } - - None -} - -// TODO cleanup and rename -// this is called by the reader task after a blocking command finishes in order to mark the connection as unblocked -pub async fn check_and_set_unblocked_flag(inner: &RefCount, command: &RedisCommand) { - if command.blocks_connection() { - inner.backchannel.write().await.set_unblocked(); - } -} - -/// Parse the response frame to see if it's an auth error. -fn parse_redis_auth_error(frame: &Resp3Frame) -> Option { - if matches!(frame.kind(), FrameKind::SimpleError | FrameKind::BlobError) { - match protocol_utils::frame_to_results(frame.clone()) { - Ok(_) => None, - Err(e) => match e.kind() { - RedisErrorKind::Auth => Some(e), - _ => None, - }, - } - } else { - None - } -} - -#[cfg(feature = "custom-reconnect-errors")] -fn check_global_reconnect_errors(inner: &RefCount, frame: &Resp3Frame) -> Option { - if let Resp3Frame::SimpleError { ref data, .. } = frame { - for prefix in inner.connection.reconnect_errors.iter() { - if data.starts_with(prefix.to_str()) { - _warn!(inner, "Found reconnection error: {}", data); - let error = protocol_utils::pretty_error(data); - inner.notifications.broadcast_error(error.clone()); - return Some(error); - } - } - - None - } else { - None - } -} - -#[cfg(not(feature = "custom-reconnect-errors"))] -fn check_global_reconnect_errors(_: &RefCount, _: &Resp3Frame) -> Option { - None -} - -fn is_clusterdown_error(frame: &Resp3Frame) -> Option<&str> { - match frame { - Resp3Frame::SimpleError { data, .. } => { - if data.trim().starts_with("CLUSTERDOWN") { - Some(data) - } else { - None - } - }, - Resp3Frame::BlobError { data, .. } => { - let parsed = match str::from_utf8(data) { - Ok(s) => s, - Err(_) => return None, - }; - - if parsed.trim().starts_with("CLUSTERDOWN") { - Some(parsed) - } else { - None - } - }, - _ => None, - } -} - -/// Check for special errors configured by the caller to initiate a reconnection process. -pub fn check_special_errors(inner: &RefCount, frame: &Resp3Frame) -> Option { - if inner.connection.reconnect_on_auth_error { - if let Some(auth_error) = parse_redis_auth_error(frame) { - return Some(auth_error); - } - } - if let Some(error) = is_clusterdown_error(frame) { - return Some(pretty_error(error)); - } - - check_global_reconnect_errors(inner, frame) -} - -/// Handle an error in the reader task that should end the connection. -pub fn broadcast_reader_error(inner: &RefCount, server: &Server, error: Option) { - _warn!(inner, "Ending reader task from {} due to {:?}", server, error); - - if inner.should_reconnect() { - inner.send_reconnect(Some(server.clone()), false, None); - } - if utils::read_locked(&inner.state) != ClientState::Disconnecting { - inner - .notifications - .broadcast_error(error.unwrap_or(RedisError::new_canceled())); - } -} - -#[cfg(not(feature = "replicas"))] -pub fn broadcast_replica_error(inner: &RefCount, server: &Server, error: Option) { - broadcast_reader_error(inner, server, error); -} - -#[cfg(feature = "replicas")] -pub fn broadcast_replica_error(inner: &RefCount, server: &Server, error: Option) { - _warn!(inner, "Ending replica reader task from {} due to {:?}", server, error); - - if inner.should_reconnect() { - inner.send_replica_reconnect(server); - } - if utils::read_locked(&inner.state) != ClientState::Disconnecting { - inner - .notifications - .broadcast_error(error.unwrap_or(RedisError::new_canceled())); - } -} diff --git a/src/router/sentinel.rs b/src/router/sentinel.rs deleted file mode 100644 index 043c4f09..00000000 --- a/src/router/sentinel.rs +++ /dev/null @@ -1,346 +0,0 @@ -#![allow(dead_code)] - -use crate::{ - error::{RedisError, RedisErrorKind}, - modules::inner::RedisClientInner, - protocol::{ - command::{RedisCommand, RedisCommandKind}, - connection::{self, RedisTransport, RedisWriter}, - utils as protocol_utils, - }, - router::{centralized, Connections}, - runtime::RefCount, - types::{RedisValue, Server, ServerConfig}, - utils, -}; -use bytes_utils::Str; -use std::collections::{HashMap, HashSet, VecDeque}; - -pub static CONFIG: &str = "CONFIG"; -pub static SET: &str = "SET"; -pub static CKQUORUM: &str = "CKQUORUM"; -pub static FLUSHCONFIG: &str = "FLUSHCONFIG"; -pub static FAILOVER: &str = "FAILOVER"; -pub static GET_MASTER_ADDR_BY_NAME: &str = "GET-MASTER-ADDR-BY-NAME"; -pub static INFO_CACHE: &str = "INFO-CACHE"; -pub static MASTERS: &str = "MASTERS"; -pub static MASTER: &str = "MASTER"; -pub static MONITOR: &str = "MONITOR"; -pub static MYID: &str = "MYID"; -pub static PENDING_SCRIPTS: &str = "PENDING-SCRIPTS"; -pub static REMOVE: &str = "REMOVE"; -pub static REPLICAS: &str = "REPLICAS"; -pub static SENTINELS: &str = "SENTINELS"; -pub static SIMULATE_FAILURE: &str = "SIMULATE-FAILURE"; - -macro_rules! stry ( - ($expr:expr) => { - match $expr { - Ok(r) => r, - Err(mut e) => { - e.change_kind(RedisErrorKind::Sentinel); - return Err(e); - } - } - } -); - -fn parse_sentinel_nodes_response( - inner: &RefCount, - value: RedisValue, -) -> Result, RedisError> { - let result_maps: Vec> = stry!(value.convert()); - let mut out = Vec::with_capacity(result_maps.len()); - - for mut map in result_maps.into_iter() { - let ip = match map.remove("ip") { - Some(ip) => ip, - None => { - _warn!(inner, "Failed to read IP for sentinel node."); - return Err(RedisError::new( - RedisErrorKind::Sentinel, - "Failed to read sentinel node IP address.", - )); - }, - }; - let port = match map.get("port") { - Some(port) => port.parse::()?, - None => { - _warn!(inner, "Failed to read port for sentinel node."); - return Err(RedisError::new( - RedisErrorKind::Sentinel, - "Failed to read sentinel node port.", - )); - }, - }; - - out.push(Server::new(ip, port)); - } - Ok(out) -} - -fn has_different_sentinel_nodes(old: &[(String, u16)], new: &[(String, u16)]) -> bool { - let mut old_set = HashSet::with_capacity(old.len()); - let mut new_set = HashSet::with_capacity(new.len()); - - for (host, port) in old.iter() { - old_set.insert((host, port)); - } - for (host, port) in new.iter() { - new_set.insert((host, port)); - } - - old_set.symmetric_difference(&new_set).count() > 0 -} - -#[cfg(feature = "sentinel-auth")] -fn read_sentinel_auth(inner: &RefCount) -> Result<(Option, Option), RedisError> { - match inner.config.server { - ServerConfig::Sentinel { - ref username, - ref password, - .. - } => Ok((username.clone(), password.clone())), - _ => Err(RedisError::new( - RedisErrorKind::Config, - "Expected sentinel server configuration.", - )), - } -} - -#[cfg(not(feature = "sentinel-auth"))] -fn read_sentinel_auth(inner: &RefCount) -> Result<(Option, Option), RedisError> { - Ok((inner.config.username.clone(), inner.config.password.clone())) -} - -/// Read the `(host, port)` tuples for the known sentinel nodes, and the credentials to use when connecting. -fn read_sentinel_nodes_and_auth( - inner: &RefCount, -) -> Result<(Vec, (Option, Option)), RedisError> { - let (username, password) = read_sentinel_auth(inner)?; - let hosts = match inner.server_state.read().kind.read_sentinel_nodes(&inner.config.server) { - Some(hosts) => hosts, - None => { - return Err(RedisError::new( - RedisErrorKind::Sentinel, - "Failed to read cached sentinel nodes.", - )) - }, - }; - - Ok((hosts, (username, password))) -} - -/// Read the set of sentinel nodes via `SENTINEL sentinels`. -async fn read_sentinels( - inner: &RefCount, - sentinel: &mut RedisTransport, -) -> Result, RedisError> { - let service_name = read_service_name(inner)?; - - let command = RedisCommand::new(RedisCommandKind::Sentinel, vec![ - static_val!(SENTINELS), - service_name.into(), - ]); - let frame = sentinel.request_response(command, false).await?; - let response = stry!(protocol_utils::frame_to_results(frame)); - _trace!(inner, "Read sentinel `sentinels` response: {:?}", response); - let sentinel_nodes = stry!(parse_sentinel_nodes_response(inner, response)); - - if sentinel_nodes.is_empty() { - _warn!(inner, "Failed to read any known sentinel nodes.") - } - - Ok(sentinel_nodes) -} - -/// Connect to any of the sentinel nodes provided on the associated `RedisConfig`. -async fn connect_to_sentinel(inner: &RefCount) -> Result { - let (hosts, (username, password)) = read_sentinel_nodes_and_auth(inner)?; - - for server in hosts.into_iter() { - _debug!(inner, "Connecting to sentinel {}", server); - let mut transport = try_or_continue!(connection::create(inner, &server, None).await); - try_or_continue!( - utils::timeout( - transport.authenticate(&inner.id, username.clone(), password.clone(), false), - inner.internal_command_timeout() - ) - .await - ); - - return Ok(transport); - } - - Err(RedisError::new( - RedisErrorKind::Sentinel, - "Failed to connect to all sentinel nodes.", - )) -} - -fn read_service_name(inner: &RefCount) -> Result { - match inner.config.server { - ServerConfig::Sentinel { ref service_name, .. } => Ok(service_name.to_owned()), - _ => Err(RedisError::new( - RedisErrorKind::Sentinel, - "Missing sentinel service name.", - )), - } -} - -/// Read the `(host, port)` tuple for the primary Redis server, as identified by the `SENTINEL -/// get-master-addr-by-name` command, then return a connection to that node. -async fn discover_primary_node( - inner: &RefCount, - sentinel: &mut RedisTransport, -) -> Result { - let service_name = read_service_name(inner)?; - let command = RedisCommand::new(RedisCommandKind::Sentinel, vec![ - static_val!(GET_MASTER_ADDR_BY_NAME), - service_name.into(), - ]); - let frame = utils::timeout( - sentinel.request_response(command, false), - inner.internal_command_timeout(), - ) - .await?; - let response = stry!(protocol_utils::frame_to_results(frame)); - let server = if response.is_null() { - return Err(RedisError::new( - RedisErrorKind::Sentinel, - "Missing primary address in response from sentinel node.", - )); - } else { - let (host, port): (Str, u16) = stry!(response.convert()); - Server { - host, - port, - #[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" - ))] - tls_server_name: None, - } - }; - - let mut transport = stry!(connection::create(inner, &server, None).await); - stry!(transport.setup(inner, None).await); - Ok(transport) -} - -/// Verify that the Redis server is a primary node and not a replica. -async fn check_primary_node_role( - inner: &RefCount, - transport: &mut RedisTransport, -) -> Result<(), RedisError> { - let command = RedisCommand::new(RedisCommandKind::Role, Vec::new()); - _debug!(inner, "Checking role for redis server at {}", transport.server); - - let frame = stry!(transport.request_response(command, inner.is_resp3()).await); - let response = stry!(protocol_utils::frame_to_results(frame)); - - if let RedisValue::Array(values) = response { - if let Some(first) = values.first() { - let is_master = first.as_str().map(|s| s == "master").unwrap_or(false); - - if is_master { - Ok(()) - } else { - Err(RedisError::new( - RedisErrorKind::Sentinel, - format!("Invalid role: {:?}", first.as_str()), - )) - } - } else { - Err(RedisError::new(RedisErrorKind::Sentinel, "Invalid role response.")) - } - } else { - Err(RedisError::new( - RedisErrorKind::Sentinel, - "Could not read redis server role.", - )) - } -} - -/// Update the cached backchannel state with the new connection information, disconnecting the old connection if -/// needed. -async fn update_sentinel_backchannel( - inner: &RefCount, - transport: &RedisTransport, -) -> Result<(), RedisError> { - let mut backchannel = inner.backchannel.write().await; - backchannel.check_and_disconnect(inner, Some(&transport.server)).await; - backchannel.connection_ids.clear(); - if let Some(id) = transport.id { - backchannel.connection_ids.insert(transport.server.clone(), id); - } - - Ok(()) -} - -/// Update the cached client and connection state with the latest sentinel state. -/// -/// This does the following: -/// * Call `SENTINEL sentinels` on the sentinels -/// * Store the updated sentinel node list on `inner`. -/// * Update the primary node on `inner`. -/// * Update the cached backchannel information. -/// * Split and store the primary node transport on `writer`. -async fn update_cached_client_state( - inner: &RefCount, - writer: &mut Option, - mut sentinel: RedisTransport, - transport: RedisTransport, -) -> Result<(), RedisError> { - let sentinels = read_sentinels(inner, &mut sentinel).await?; - inner - .server_state - .write() - .kind - .update_sentinel_nodes(&transport.server, sentinels); - let _ = update_sentinel_backchannel(inner, &transport).await; - - let (_, _writer) = connection::split(inner, transport, false, centralized::spawn_reader_task)?; - *writer = Some(_writer); - Ok(()) -} - -/// Initialize fresh connections to the server, dropping any old connections and saving in-flight commands on -/// `buffer`. -/// -/// -pub async fn initialize_connection( - inner: &RefCount, - connections: &mut Connections, - buffer: &mut VecDeque, -) -> Result<(), RedisError> { - _debug!(inner, "Initializing sentinel connection."); - let commands = connections.disconnect_all(inner).await; - buffer.extend(commands); - - match connections { - Connections::Sentinel { writer } => { - let mut sentinel = connect_to_sentinel(inner).await?; - let mut transport = discover_primary_node(inner, &mut sentinel).await?; - let server = transport.server.clone(); - - utils::timeout( - Box::pin(async { - check_primary_node_role(inner, &mut transport).await?; - update_cached_client_state(inner, writer, sentinel, transport).await?; - Ok::<_, RedisError>(()) - }), - inner.internal_command_timeout(), - ) - .await?; - - inner.notifications.broadcast_reconnect(server); - Ok(()) - }, - _ => Err(RedisError::new( - RedisErrorKind::Config, - "Expected sentinel connections.", - )), - } -} diff --git a/src/router/transactions.rs b/src/router/transactions.rs deleted file mode 100644 index 51b3580a..00000000 --- a/src/router/transactions.rs +++ /dev/null @@ -1,340 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - interfaces::Resp3Frame, - modules::inner::RedisClientInner, - protocol::{ - command::{ClusterErrorKind, RedisCommand, RedisCommandKind, ResponseSender, RouterReceiver, RouterResponse}, - responders::ResponseKind, - }, - router::{utils, Router, Written}, - runtime::RefCount, - types::{ClusterHash, Server}, - utils as client_utils, -}; - -/// An internal enum describing the result of an attempt to send a transaction command. -#[derive(Debug)] -enum TransactionResponse { - /// Retry the entire transaction again after reconnecting or resetting connections. - /// - /// Returned in response to a write error or a connection closing. - Retry(RedisError), - /// Send `DISCARD` and retry the entire transaction against the provided server/hash slot. - Redirection((ClusterErrorKind, u16, Server)), - /// Finish the transaction with the associated response. - Finished(Resp3Frame), - /// Continue the transaction. - /// - /// Note: if `abort_on_error` is true the transaction will continue even after errors from the server. If `false` - /// the error will instead be returned as a `Result::Err` that ends the transaction. - Continue, -} - -/// Write a command in the context of a transaction and process the router response. -/// -/// Returns the command result policy or a fatal error that should end the transaction. -async fn write_command( - inner: &RefCount, - router: &mut Router, - server: &Server, - command: RedisCommand, - abort_on_error: bool, - rx: RouterReceiver, -) -> Result { - _trace!( - inner, - "Sending trx command {} ({}) to {}", - command.kind.to_str_debug(), - command.debug_id(), - server - ); - - let timeout_dur = command.timeout_dur.unwrap_or_else(|| inner.default_command_timeout()); - let result = match router.write_direct(command, server).await { - Written::Error((error, _)) => Err(error), - Written::Disconnected((_, _, error)) => Err(error), - Written::NotFound(_) => Err(RedisError::new(RedisErrorKind::Cluster, "Connection not found.")), - _ => Ok(()), - }; - if let Err(e) = result { - // TODO check fail fast and exit early w/o retry here? - _debug!(inner, "Error writing trx command: {:?}", e); - return Ok(TransactionResponse::Retry(e)); - } - - match client_utils::timeout(rx, timeout_dur).await? { - RouterResponse::Continue => Ok(TransactionResponse::Continue), - RouterResponse::Ask((slot, server, _)) => { - Ok(TransactionResponse::Redirection((ClusterErrorKind::Ask, slot, server))) - }, - RouterResponse::Moved((slot, server, _)) => Ok(TransactionResponse::Redirection(( - ClusterErrorKind::Moved, - slot, - server, - ))), - RouterResponse::ConnectionClosed((err, _)) => Ok(TransactionResponse::Retry(err)), - RouterResponse::TransactionError((err, _)) => { - if abort_on_error { - Err(err) - } else { - Ok(TransactionResponse::Continue) - } - }, - RouterResponse::TransactionResult(frame) => Ok(TransactionResponse::Finished(frame)), - } -} - -/// Send EXEC to the provided server. -async fn send_exec( - inner: &RefCount, - router: &mut Router, - server: &Server, - id: u64, -) -> Result { - let mut command = RedisCommand::new(RedisCommandKind::Exec, vec![]); - command.can_pipeline = false; - command.skip_backpressure = true; - command.transaction_id = Some(id); - let rx = command.create_router_channel(); - - write_command(inner, router, server, command, true, rx).await -} - -/// Send DISCARD to the provided server. -async fn send_discard( - inner: &RefCount, - router: &mut Router, - server: &Server, - id: u64, -) -> Result { - let mut command = RedisCommand::new(RedisCommandKind::Discard, vec![]); - command.can_pipeline = false; - command.skip_backpressure = true; - command.transaction_id = Some(id); - let rx = command.create_router_channel(); - - write_command(inner, router, server, command, true, rx).await -} - -fn update_hash_slot(commands: &mut [RedisCommand], slot: u16) { - for command in commands.iter_mut() { - command.hasher = ClusterHash::Custom(slot); - } -} - -/// Run the transaction, following cluster redirects and reconnecting as needed. -// this would be a lot cleaner with GATs if we could abstract the inner loops with async closures -#[allow(unused_mut)] -pub async fn run( - inner: &RefCount, - router: &mut Router, - mut commands: Vec, - watched: Option, - id: u64, - abort_on_error: bool, - mut tx: ResponseSender, -) -> Result<(), RedisError> { - if commands.is_empty() { - let _ = tx.send(Ok(Resp3Frame::Null)); - return Ok(()); - } - // each of the commands should have the same options - let max_attempts = if commands[0].attempts_remaining == 0 { - inner.max_command_attempts() - } else { - commands[0].attempts_remaining - }; - let max_redirections = if commands[0].redirections_remaining == 0 { - inner.connection.max_redirections - } else { - commands[0].redirections_remaining - }; - - let mut attempted = 0; - let mut redirections = 0; - 'outer: loop { - _debug!(inner, "Starting transaction {} (attempted: {})", id, attempted); - - let server = if let Some(server) = commands[0].cluster_node.as_ref() { - server.clone() - } else { - match router.find_connection(&commands[0]) { - Some(server) => server.clone(), - None => { - if inner.config.server.is_clustered() { - // optimistically sync the cluster, then fall back to a full reconnect - if router.sync_cluster().await.is_err() { - utils::delay_cluster_sync(inner).await?; - utils::reconnect_with_policy(inner, router).await? - } - } else { - utils::reconnect_with_policy(inner, router).await? - }; - - continue; - }, - } - }; - let mut idx = 0; - - // send the WATCH command before any of the trx commands - if let Some(watch) = watched.as_ref() { - let watch = watch.duplicate(ResponseKind::Skip); - let rx = watch.create_router_channel(); - - _debug!( - inner, - "Sending WATCH for {} keys in trx {} to {}", - watch.args().len(), - id, - server - ); - match write_command(inner, router, &server, watch, false, rx).await { - Ok(TransactionResponse::Continue) => { - _debug!(inner, "Successfully sent WATCH command before transaction {}.", id); - }, - Ok(TransactionResponse::Retry(error)) => { - _debug!(inner, "Retrying trx {} after WATCH error: {:?}.", id, error); - - attempted += 1; - if attempted >= max_attempts { - let _ = tx.send(Err(error)); - return Ok(()); - } else { - utils::reconnect_with_policy(inner, router).await?; - } - - continue 'outer; - }, - Ok(TransactionResponse::Redirection((kind, slot, server))) => { - redirections += 1; - if redirections > max_redirections { - let _ = tx.send(Err(RedisError::new( - RedisErrorKind::Cluster, - "Too many cluster redirections.", - ))); - return Ok(()); - } - - _debug!(inner, "Recv {} redirection to {} for WATCH in trx {}", kind, server, id); - update_hash_slot(&mut commands, slot); - utils::delay_cluster_sync(inner).await?; - utils::cluster_redirect_with_policy(inner, router, kind, slot, &server).await?; - continue 'outer; - }, - Ok(TransactionResponse::Finished(frame)) => { - _warn!(inner, "Unexpected trx finished frame after WATCH."); - let _ = tx.send(Ok(frame)); - return Ok(()); - }, - Err(error) => { - let _ = tx.send(Err(error)); - return Ok(()); - }, - }; - } - - if attempted > 0 { - inner.counters.incr_redelivery_count(); - } - // send each of the commands. the first one is always MULTI - 'inner: while idx < commands.len() { - let command = commands[idx].duplicate(ResponseKind::Skip); - let rx = command.create_router_channel(); - - match write_command(inner, router, &server, command, abort_on_error, rx).await { - Ok(TransactionResponse::Continue) => { - idx += 1; - continue 'inner; - }, - Ok(TransactionResponse::Retry(error)) => { - _debug!(inner, "Retrying trx {} after error: {:?}", id, error); - if let Err(e) = send_discard(inner, router, &server, id).await { - _warn!(inner, "Error sending DISCARD in trx {}: {:?}", id, e); - } - - attempted += 1; - if attempted >= max_attempts { - let _ = tx.send(Err(error)); - return Ok(()); - } else { - utils::reconnect_with_policy(inner, router).await?; - } - - continue 'outer; - }, - Ok(TransactionResponse::Redirection((kind, slot, server))) => { - redirections += 1; - if redirections > max_redirections { - let _ = tx.send(Err(RedisError::new( - RedisErrorKind::Cluster, - "Too many cluster redirections.", - ))); - return Ok(()); - } - - update_hash_slot(&mut commands, slot); - if let Err(e) = send_discard(inner, router, &server, id).await { - _warn!(inner, "Error sending DISCARD in trx {}: {:?}", id, e); - } - utils::cluster_redirect_with_policy(inner, router, kind, slot, &server).await?; - - continue 'outer; - }, - Ok(TransactionResponse::Finished(frame)) => { - let _ = tx.send(Ok(frame)); - return Ok(()); - }, - Err(error) => { - // fatal errors that end the transaction - let _ = send_discard(inner, router, &server, id).await; - let _ = tx.send(Err(error)); - return Ok(()); - }, - } - } - - match send_exec(inner, router, &server, id).await { - Ok(TransactionResponse::Finished(frame)) => { - let _ = tx.send(Ok(frame)); - return Ok(()); - }, - Ok(TransactionResponse::Retry(error)) => { - _debug!(inner, "Retrying trx {} after error: {:?}", id, error); - if let Err(e) = send_discard(inner, router, &server, id).await { - _warn!(inner, "Error sending DISCARD in trx {}: {:?}", id, e); - } - - attempted += 1; - if attempted >= max_attempts { - let _ = tx.send(Err(error)); - return Ok(()); - } else { - utils::reconnect_with_policy(inner, router).await?; - } - - continue 'outer; - }, - Ok(TransactionResponse::Redirection((kind, slot, dest))) => { - // doesn't make sense on EXEC, but return it as an error so it isn't lost - let _ = send_discard(inner, router, &server, id).await; - let _ = tx.send(Err(RedisError::new( - RedisErrorKind::Cluster, - format!("{} {} {}", kind, slot, dest), - ))); - return Ok(()); - }, - Ok(TransactionResponse::Continue) => { - _warn!(inner, "Invalid final response to transaction {}", id); - let _ = send_discard(inner, router, &server, id).await; - let _ = tx.send(Err(RedisError::new_canceled())); - return Ok(()); - }, - Err(error) => { - let _ = send_discard(inner, router, &server, id).await; - let _ = tx.send(Err(error)); - return Ok(()); - }, - }; - } -} diff --git a/src/router/types.rs b/src/router/types.rs deleted file mode 100644 index 6a549292..00000000 --- a/src/router/types.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::protocol::types::Server; - -/// Options describing how to change connections in a cluster. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ClusterChange { - pub add: Vec, - pub remove: Vec, -} - -impl Default for ClusterChange { - fn default() -> Self { - ClusterChange { - add: Vec::new(), - remove: Vec::new(), - } - } -} diff --git a/src/router/utils.rs b/src/router/utils.rs deleted file mode 100644 index 8a7d110c..00000000 --- a/src/router/utils.rs +++ /dev/null @@ -1,603 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - interfaces, - modules::inner::RedisClientInner, - protocol::{ - command::{RedisCommand, RouterCommand, RouterResponse}, - connection::{RedisWriter, SharedBuffer, SplitStreamKind}, - responders::ResponseKind, - types::*, - utils as protocol_utils, - }, - router::{utils, Backpressure, Counters, Router, Written}, - runtime::{oneshot_channel, sleep, RefCount}, - types::*, - utils as client_utils, -}; -use futures::TryStreamExt; -use std::{ - cmp, - time::{Duration, Instant}, -}; - -#[cfg(feature = "transactions")] -use crate::protocol::command::ClusterErrorKind; - -/// Check the connection state and command flags to determine the backpressure policy to apply, if any. -pub fn check_backpressure( - inner: &RefCount, - counters: &Counters, - command: &RedisCommand, -) -> Result, RedisError> { - if command.skip_backpressure { - return Ok(None); - } - let in_flight = client_utils::read_atomic(&counters.in_flight); - - inner.with_perf_config(|perf_config| { - // TODO clean this up and write better docs - if in_flight as u64 > perf_config.backpressure.max_in_flight_commands { - if perf_config.backpressure.disable_auto_backpressure { - Err(RedisError::new_backpressure()) - } else { - match perf_config.backpressure.policy { - BackpressurePolicy::Drain => Ok(Some(Backpressure::Block)), - BackpressurePolicy::Sleep { - disable_backpressure_scaling, - min_sleep_duration, - } => { - let duration = if disable_backpressure_scaling { - min_sleep_duration - } else { - Duration::from_millis(cmp::max(min_sleep_duration.as_millis() as u64, in_flight as u64)) - }; - - Ok(Some(Backpressure::Wait(duration))) - }, - } - } - } else { - Ok(None) - } - }) -} - -#[cfg(feature = "partial-tracing")] -fn set_command_trace(inner: &RefCount, command: &mut RedisCommand) { - if inner.should_trace() { - crate::trace::set_network_span(inner, command, true); - } -} - -#[cfg(not(feature = "partial-tracing"))] -fn set_command_trace(_inner: &RefCount, _: &mut RedisCommand) {} - -/// Prepare the command, updating flags in place. -/// -/// Returns the RESP frame and whether the socket should be flushed. -pub fn prepare_command( - inner: &RefCount, - counters: &Counters, - command: &mut RedisCommand, -) -> Result<(ProtocolFrame, bool), RedisError> { - let frame = protocol_utils::encode_frame(inner, command)?; - - // flush the socket under any of the following conditions: - // * we don't know of any queued commands following this command - // * we've fed up to the max feed count commands already - // * the command closes the connection - // * the command ends a transaction - // * the command does some form of authentication - // * the command goes to multiple sockets at once - // * the command blocks the router command loop - let should_flush = counters.should_send(inner) - || command.kind.should_flush() - || command.is_all_cluster_nodes() - || command.has_router_channel(); - - command.network_start = Some(Instant::now()); - set_command_trace(inner, command); - - Ok((frame, should_flush)) -} - -/// Write a command on the provided writer half of a socket. -pub async fn write_command( - inner: &RefCount, - writer: &mut RedisWriter, - mut command: RedisCommand, - force_flush: bool, -) -> Written { - _trace!( - inner, - "Writing {} ({}). Timed out: {}, Force flush: {}", - command.kind.to_str_debug(), - command.debug_id(), - client_utils::read_bool_atomic(&command.timed_out), - force_flush - ); - if client_utils::read_bool_atomic(&command.timed_out) { - _debug!( - inner, - "Ignore writing timed out command: {}", - command.kind.to_str_debug() - ); - return Written::Ignore; - } - - match check_backpressure(inner, &writer.counters, &command) { - Ok(Some(backpressure)) => { - _trace!(inner, "Returning backpressure for {}", command.kind.to_str_debug()); - return Written::Backpressure((command, backpressure)); - }, - Err(e) => { - // return manual backpressure errors directly to the caller - command.finish(inner, Err(e)); - return Written::Ignore; - }, - _ => {}, - }; - - let (frame, should_flush) = match prepare_command(inner, &writer.counters, &mut command) { - Ok((frame, should_flush)) => (frame, should_flush || force_flush), - Err(e) => { - _warn!(inner, "Frame encoding error for {}", command.kind.to_str_debug()); - // do not retry commands that trigger frame encoding errors - command.finish(inner, Err(e)); - return Written::Ignore; - }, - }; - - _trace!( - inner, - "Sending command {} ({}) to {}", - command.kind.to_str_debug(), - command.debug_id(), - writer.server - ); - command.write_attempts += 1; - - if !writer.is_working() { - let error = RedisError::new(RedisErrorKind::IO, "Connection closed."); - _debug!(inner, "Error sending command: {:?}", error); - return Written::Disconnected((Some(writer.server.clone()), Some(command), error)); - } - - let no_incr = command.has_no_responses(); - writer.push_command(inner, command); - if let Err(err) = writer.write_frame(frame, should_flush, no_incr).await { - Written::Disconnected((Some(writer.server.clone()), None, err)) - } else { - Written::Sent((writer.server.clone(), should_flush)) - } -} - -/// Check the shared connection command buffer to see if the oldest command blocks the router task on a -/// response (not pipelined). -pub fn check_blocked_router(inner: &RefCount, buffer: &SharedBuffer, error: &Option) { - let command = match buffer.pop() { - Some(cmd) => cmd, - None => return, - }; - if command.has_router_channel() { - #[allow(unused_mut)] - let mut tx = match command.take_router_tx() { - Some(tx) => tx, - None => return, - }; - let error = error - .clone() - .unwrap_or(RedisError::new(RedisErrorKind::IO, "Connection Closed")); - - if let Err(_) = tx.send(RouterResponse::ConnectionClosed((error, command))) { - _warn!(inner, "Failed to send router connection closed error."); - } - } else { - // this is safe to rearrange since the connection has closed and we can't guarantee command ordering when - // connections close while an entire pipeline is in flight - buffer.push(command); - } -} - -/// Filter the shared buffer, removing commands that reached the max number of attempts and responding to each caller -/// with the underlying error. -pub fn check_final_write_attempt( - inner: &RefCount, - buffer: &SharedBuffer, - error: &Option, -) { - buffer - .drain() - .into_iter() - .filter_map(|command| { - if command.should_finish_with_error(inner) { - command.finish( - inner, - Err( - error - .clone() - .unwrap_or(RedisError::new(RedisErrorKind::IO, "Connection Closed")), - ), - ); - - None - } else { - Some(command) - } - }) - .for_each(|command| { - buffer.push(command); - }); -} - -/// Read the next reconnection delay for the client. -pub fn next_reconnection_delay(inner: &RefCount) -> Result { - inner - .policy - .write() - .as_mut() - .and_then(|policy| policy.next_delay()) - .map(Duration::from_millis) - .ok_or_else(|| RedisError::new(RedisErrorKind::Canceled, "Max reconnection attempts reached.")) -} - -/// Attempt to reconnect and replay queued commands. -pub async fn reconnect_once(inner: &RefCount, router: &mut Router) -> Result<(), RedisError> { - client_utils::set_client_state(&inner.state, ClientState::Connecting); - if let Err(e) = Box::pin(router.connect()).await { - _debug!(inner, "Failed reconnecting with error: {:?}", e); - client_utils::set_client_state(&inner.state, ClientState::Disconnected); - inner.notifications.broadcast_error(e.clone()); - Err(e) - } else { - #[cfg(feature = "replicas")] - if let Err(e) = router.refresh_replica_routing().await { - _warn!(inner, "Error syncing replicas: {:?}", e); - if !inner.ignore_replica_reconnect_errors() { - client_utils::set_client_state(&inner.state, ClientState::Disconnected); - inner.notifications.broadcast_error(e.clone()); - return Err(e); - } - } - // try to flush any previously in-flight commands - router.retry_buffer().await; - - client_utils::set_client_state(&inner.state, ClientState::Connected); - inner.notifications.broadcast_connect(Ok(())); - inner.reset_reconnection_attempts(); - Ok(()) - } -} - -/// Reconnect to the server(s) until the max reconnect policy attempts are reached. -/// -/// Errors from this function should end the connection task. -pub async fn reconnect_with_policy( - inner: &RefCount, - router: &mut Router, -) -> Result<(), RedisError> { - let mut delay = utils::next_reconnection_delay(inner)?; - - loop { - if !delay.is_zero() { - _debug!(inner, "Sleeping for {} ms.", delay.as_millis()); - inner.wait_with_interrupt(delay).await?; - } - - if let Err(e) = reconnect_once(inner, router).await { - if e.should_not_reconnect() { - return Err(e); - } - - delay = match next_reconnection_delay(inner) { - Ok(delay) => delay, - Err(_) => return Err(e), - }; - - continue; - } else { - break; - } - } - - Ok(()) -} - -/// Attempt to follow a cluster redirect, reconnecting as needed until the max reconnections attempts is reached. -#[cfg(feature = "transactions")] -pub async fn cluster_redirect_with_policy( - inner: &RefCount, - router: &mut Router, - kind: ClusterErrorKind, - slot: u16, - server: &Server, -) -> Result<(), RedisError> { - let mut delay = inner.connection.cluster_cache_update_delay; - - loop { - if !delay.is_zero() { - _debug!(inner, "Sleeping for {} ms.", delay.as_millis()); - inner.wait_with_interrupt(delay).await?; - } - - if let Err(e) = router.cluster_redirection(&kind, slot, server).await { - delay = next_reconnection_delay(inner).map_err(|_| e)?; - - continue; - } else { - break; - } - } - - Ok(()) -} - -/// Repeatedly try to send `ASKING` to the provided server, reconnecting as needed.f -/// -/// Errors from this function should end the connection task. -pub async fn send_asking_with_policy( - inner: &RefCount, - router: &mut Router, - server: &Server, - slot: u16, -) -> Result<(), RedisError> { - let mut delay = inner.connection.cluster_cache_update_delay; - - loop { - if !delay.is_zero() { - _debug!(inner, "Sleeping for {} ms.", delay.as_millis()); - inner.wait_with_interrupt(delay).await?; - } - - if !router.connections.has_server_connection(server) { - if let Err(e) = router.sync_cluster().await { - _debug!(inner, "Error syncing cluster before ASKING: {:?}", e); - delay = utils::next_reconnection_delay(inner)?; - continue; - } - } - - let mut command = RedisCommand::new_asking(slot); - let (tx, rx) = oneshot_channel(); - command.skip_backpressure = true; - command.response = ResponseKind::Respond(Some(tx)); - - let result = match router.write_direct(command, server).await { - Written::Error((error, _)) => Err(error), - Written::Disconnected((_, _, error)) => Err(error), - Written::NotFound(_) => Err(RedisError::new(RedisErrorKind::Cluster, "Connection not found.")), - _ => Ok(()), - }; - - if let Err(error) = result { - if error.should_not_reconnect() { - break; - } else if let Err(_) = reconnect_once(inner, router).await { - delay = utils::next_reconnection_delay(inner)?; - continue; - } else { - delay = Duration::from_millis(0); - continue; - } - } else { - match client_utils::timeout(rx, inner.internal_command_timeout()).await { - Ok(Err(e)) => { - // error writing the command - _debug!(inner, "Reconnect once after error from ASKING: {:?}", e); - if let Err(_) = reconnect_once(inner, router).await { - delay = utils::next_reconnection_delay(inner)?; - continue; - } else { - delay = Duration::from_millis(0); - continue; - } - }, - Err(e) => { - // command was dropped due to connection closing - _debug!(inner, "Reconnect once after rx error from ASKING: {:?}", e); - if let Err(_) = reconnect_once(inner, router).await { - delay = utils::next_reconnection_delay(inner)?; - continue; - } else { - delay = Duration::from_millis(0); - continue; - } - }, - _ => break, - } - } - } - - inner.reset_reconnection_attempts(); - Ok(()) -} - -#[cfg(feature = "replicas")] -async fn sync_cluster_replicas( - inner: &RefCount, - router: &mut Router, - reset: bool, -) -> Result<(), RedisError> { - if reset { - router.replicas.clear_connections(inner).await?; - } - - if inner.config.server.is_clustered() { - router.sync_cluster().await - } else { - router.sync_replicas().await - } -} - -/// Repeatedly try to sync the cluster state, reconnecting as needed until the max reconnection attempts is reached. -#[cfg(feature = "replicas")] -pub async fn sync_replicas_with_policy( - inner: &RefCount, - router: &mut Router, - reset: bool, -) -> Result<(), RedisError> { - let mut delay = Duration::from_millis(0); - - loop { - if !delay.is_zero() { - _debug!(inner, "Sleeping for {} ms.", delay.as_millis()); - inner.wait_with_interrupt(delay).await?; - } - - if let Err(e) = sync_cluster_replicas(inner, router, reset).await { - _warn!(inner, "Error syncing replicas: {:?}", e); - - if e.should_not_reconnect() { - break; - } else { - // return the underlying error on the last attempt - delay = match utils::next_reconnection_delay(inner) { - Ok(delay) => delay, - Err(_) => return Err(e), - }; - - continue; - } - } else { - break; - } - } - - Ok(()) -} - -/// Wait for `inner.connection.cluster_cache_update_delay`. -pub async fn delay_cluster_sync(inner: &RefCount) -> Result<(), RedisError> { - if inner.config.server.is_clustered() && !inner.connection.cluster_cache_update_delay.is_zero() { - inner - .wait_with_interrupt(inner.connection.cluster_cache_update_delay) - .await - } else { - Ok(()) - } -} - -/// Repeatedly try to sync the cluster state, reconnecting as needed until the max reconnection attempts is reached. -/// -/// Errors from this function should end the connection task. -pub async fn sync_cluster_with_policy( - inner: &RefCount, - router: &mut Router, -) -> Result<(), RedisError> { - let mut delay = Duration::from_millis(0); - - loop { - if !delay.is_zero() { - _debug!(inner, "Sleeping for {} ms.", delay.as_millis()); - inner.wait_with_interrupt(delay).await?; - } - - if let Err(e) = router.sync_cluster().await { - _warn!(inner, "Error syncing cluster after redirect: {:?}", e); - - if e.should_not_reconnect() { - break; - } else { - // return the underlying error on the last attempt - delay = match utils::next_reconnection_delay(inner) { - Ok(delay) => delay, - Err(_) => return Err(e), - }; - - continue; - } - } else { - break; - } - } - - Ok(()) -} - -pub fn defer_reconnect(inner: &RefCount) { - if inner.config.server.is_clustered() { - let (tx, _) = oneshot_channel(); - let cmd = RouterCommand::SyncCluster { tx }; - if let Err(_) = interfaces::send_to_router(inner, cmd) { - _warn!(inner, "Failed to send deferred cluster sync.") - } - } else { - let cmd = RouterCommand::Reconnect { - server: None, - tx: None, - force: false, - #[cfg(feature = "replicas")] - replica: false, - }; - if let Err(_) = interfaces::send_to_router(inner, cmd) { - _warn!(inner, "Failed to send deferred cluster sync.") - } - } -} - -/// Attempt to read the next frame from the reader half of a connection. -pub async fn next_frame( - inner: &RefCount, - conn: &mut SplitStreamKind, - server: &Server, - buffer: &SharedBuffer, -) -> Result, RedisError> { - if let Some(ref max_resp_latency) = inner.connection.unresponsive.max_timeout { - // These shenanigans were implemented in an attempt to strike a balance between a few recent changes. - // - // The entire request-response path can be lock-free if we use crossbeam-queue types under the shared buffer - // between socket halves, but these types do not support `peek` or `push_front`. Unfortunately this really limits - // or prevents most forms of conditional `pop_front` use cases. There are 3-4 places in the code where this - // matters, and this is one of them. - // - // The `UnresponsiveConfig` interface implements a heuristic where callers can express that a connection should be - // considered unresponsive if a command waits too long for a response. Before switching to crossbeam types we used - // a `Mutex` container which made this scenario easier to implement, but with crossbeam types it's more - // complicated. - // - // The approach here implements a ~~hack~~ heuristic where we measure the time since first noticing a new - // frame in the shared buffer from the reader task's perspective. This only works because we use `Stream::next` - // which is noted to be cancellation-safe in the tokio::select! docs. With this implementation the worst case - // error margin is an extra `interval`. - - let mut last_frame_sent: Option = None; - 'outer: loop { - tokio::select! { - // prefer polling the connection first - biased; - frame = conn.try_next() => return frame, - - // repeatedly check the duration since we first noticed a pending frame - _ = sleep(inner.connection.unresponsive.interval) => { - _trace!(inner, "Checking unresponsive connection to {}", server); - - // continue early if the buffer is empty or we're waiting on a blocking command. this isn't ideal, but - // this strategy just doesn't work well with blocking commands. - let buffer_len = buffer.len(); - if buffer_len == 0 || buffer.is_blocked() { - last_frame_sent = None; - continue 'outer; - } else if buffer_len > 0 && last_frame_sent.is_none() { - _trace!(inner, "Observed new request frame in unresponsive loop"); - last_frame_sent = Some(Instant::now()); - } - - if let Some(ref last_frame_sent) = last_frame_sent { - let latency = Instant::now().saturating_duration_since(*last_frame_sent); - if latency > *max_resp_latency { - _warn!(inner, "Unresponsive connection to {} after {:?}", server, latency); - inner.notifications.broadcast_unresponsive(server.clone()); - return Err(RedisError::new(RedisErrorKind::IO, "Unresponsive connection.")) - } - } - }, - } - } - } else { - _trace!(inner, "Skip waiting on interrupt rx."); - conn.try_next().await - } -} - -#[cfg(test)] -mod tests {} diff --git a/src/trace/README.md b/src/trace/README.md deleted file mode 100644 index 354d0a20..00000000 --- a/src/trace/README.md +++ /dev/null @@ -1,48 +0,0 @@ -Tracing -======= - -Tracing is implemented via the [tracing](https://github.com/tokio-rs/tracing) crate. This page describes the spans used -by the client and the fields emitted on each of the spans. - -![](../../tests/jaeger-2024.jpg) - -See the [benchmark](../../bin/benchmark) application for an example showing how to configure tracing with a -local Jaeger instance. This crate ships with a [small example](../../tests/docker/compose/jaeger.yml) that -uses `docker-compose` to run a local Jaeger instance. - -## Spans - -This table shows the spans emitted by the client. The `Partial Trace` column describes whether the span will appear when -only the `partial-tracing` feature flag is enabled. - -| Name | Description | Partial Trace | -|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------| -| fred.command | The top level span used for all redis commands. | x | -| fred.prepare | Time spent checking and preparing arguments. | | -| fred.queued | Time spent waiting in the in-memory queue before being sent to the server. Pipelining and backpressure settings can significantly affect this. | | -| fred.write | Time spent routing and writing a command to the socket. | | -| fred.rtt | Time spent waiting on a response from the server, starting from when the first byte is fed to the socket and ending when the full response has been decoded. | x | -| fred.pubsub | Time spent parsing a publish-subscribe message. | | - -Tracing levels for the two tracing features can be configured separately through the `TracingConfig`. - -## Events - -| Name | Description | -|-------------------|------------------------------------------------------------------------------| -| fred.backpressure | Emitted when a command hits backpressure due to too many in-flight commands. | - -## Attributes - -| Name | Description | -|---------------|--------------------------------------------------------------------| -| client.id | The ID of the client instance (`client.id()`). | -| client.queued | The length of the in-memory command queue. | -| cmd.name | The redis command name. | -| cmd.req | The size (in bytes) of the command's arguments. | -| cmd.res | The size (in bytes) of the command's response. | -| cmd.args | The number of arguments being sent to the server. | -| cmd.pipelined | Whether the command was pipelined. | -| cmd.flush | Whether the socket was flushed while sending the command. | -| msg.channel | The channel on which a pubsub message was received. | -| duration | The duration of a pause, in milliseconds, of a backpressure event. | \ No newline at end of file diff --git a/src/trace/disabled.rs b/src/trace/disabled.rs deleted file mode 100644 index c5af3b78..00000000 --- a/src/trace/disabled.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![allow(dead_code)] - -#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))] -use crate::modules::inner::RedisClientInner; -#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))] -use crate::protocol::command::RedisCommand; -#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))] -use crate::runtime::RefCount; -#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))] -use redis_protocol::resp3::types::BytesFrame as Frame; - -/// Fake span for mocking tracing functions. -#[cfg(not(feature = "full-tracing"))] -pub struct Span {} - -#[cfg(not(feature = "full-tracing"))] -impl Span { - pub fn enter(&self) {} - - pub fn record(&self, _field: &Q, _value: &V) -> &Self { - self - } -} - -#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))] -pub fn set_network_span(_inner: &RefCount, _command: &mut RedisCommand, _flush: bool) {} - -#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))] -pub fn create_pubsub_span(_inner: &RefCount, _frame: &Frame) -> Option { - Some(Span {}) -} - -#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))] -pub fn backpressure_event(_cmd: &RedisCommand, _: Option) {} diff --git a/src/trace/enabled.rs b/src/trace/enabled.rs deleted file mode 100644 index 719c6d9e..00000000 --- a/src/trace/enabled.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::{modules::inner::RedisClientInner, protocol::command::RedisCommand, runtime::RefCount}; -use redis_protocol::resp3::types::{BytesFrame as Resp3Frame, Resp3Frame as _Resp3Frame}; -use std::{fmt, ops::Deref}; -pub use tracing::span::Span; -use tracing::{event, field::Empty, Id as TraceId, Level}; - -#[cfg(not(feature = "full-tracing"))] -use crate::trace::disabled::Span as FakeSpan; - -/// Struct for storing spans used by the client when sending a command. -pub struct CommandTraces { - pub cmd: Option, - pub network: Option, - #[cfg(feature = "full-tracing")] - pub queued: Option, - #[cfg(not(feature = "full-tracing"))] - pub queued: Option, -} - -/// Enter the network span when the command is dropped after receiving a response. -impl Drop for CommandTraces { - fn drop(&mut self) { - if let Some(span) = self.network.take() { - let _enter = span.enter(); - } - } -} - -impl Default for CommandTraces { - fn default() -> Self { - CommandTraces { - cmd: None, - queued: None, - network: None, - } - } -} - -impl fmt::Debug for CommandTraces { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "[Command Traces]") - } -} - -pub fn set_network_span(inner: &RefCount, command: &mut RedisCommand, flush: bool) { - trace!("Setting network span from command {}", command.debug_id()); - let span = fspan!(command, inner.tracing_span_level(), "fred.rtt", "cmd.flush" = flush); - span.in_scope(|| {}); - command.traces.network = Some(span); -} - -pub fn record_response_size(span: &Span, frame: &Resp3Frame) { - #[allow(clippy::needless_borrows_for_generic_args)] - span.record("cmd.res", &frame.encode_len()); -} - -pub fn create_command_span(inner: &RefCount) -> Span { - span_lvl!( - inner.tracing_span_level(), - "fred.command", - module = "fred", - "client.id" = &inner.id.deref(), - "cmd.name" = Empty, - "cmd.req" = Empty, - "cmd.res" = Empty - ) -} - -#[cfg(feature = "full-tracing")] -pub fn create_args_span(parent: Option, inner: &RefCount) -> Span { - span_lvl!(inner.full_tracing_span_level(), parent: parent, "fred.prepare", "cmd.args" = Empty) -} - -#[cfg(not(feature = "full-tracing"))] -pub fn create_args_span(_parent: Option, _inner: &RefCount) -> FakeSpan { - FakeSpan {} -} - -#[cfg(feature = "full-tracing")] -pub fn create_queued_span(parent: Option, inner: &RefCount) -> Span { - let buf_len = inner.counters.read_cmd_buffer_len(); - span_lvl!(inner.full_tracing_span_level(), parent: parent, "fred.queued", buf_len) -} - -#[cfg(not(feature = "full-tracing"))] -pub fn create_queued_span(_parent: Option, _inner: &RefCount) -> FakeSpan { - FakeSpan {} -} - -#[cfg(feature = "full-tracing")] -pub fn create_pubsub_span(inner: &RefCount, frame: &Resp3Frame) -> Option { - if inner.should_trace() { - let span = span_lvl!( - inner.full_tracing_span_level(), - parent: None, - "fred.pubsub", - module = "fred", - "client.id" = &inner.id.deref(), - "cmd.res" = &frame.encode_len(), - "msg.channel" = Empty - ); - - Some(span) - } else { - None - } -} - -#[cfg(not(feature = "full-tracing"))] -pub fn create_pubsub_span(_inner: &RefCount, _frame: &Resp3Frame) -> Option { - Some(FakeSpan {}) -} - -pub fn backpressure_event(cmd: &RedisCommand, duration: Option) { - let id = cmd.traces.cmd.as_ref().and_then(|c| c.id()); - if let Some(duration) = duration { - event!(parent: id, Level::INFO, "fred.backpressure duration={}", duration); - } else { - event!(parent: id, Level::INFO, "fred.backpressure drain"); - } -} diff --git a/src/trace/mod.rs b/src/trace/mod.rs deleted file mode 100644 index 6ba741e2..00000000 --- a/src/trace/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![doc = include_str!("./README.md")] - -pub mod disabled; -#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))] -mod enabled; - -#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))] -pub use disabled::*; -#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))] -pub use enabled::*; diff --git a/src/types/args.rs b/src/types/args.rs deleted file mode 100644 index 3a333e7d..00000000 --- a/src/types/args.rs +++ /dev/null @@ -1,1704 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - interfaces::{ClientLike, Resp3Frame}, - protocol::{connection::OK, utils as protocol_utils}, - types::{FromRedis, FromRedisKey, Server, QUEUED}, - utils, -}; -use bytes::Bytes; -use bytes_utils::Str; -use float_cmp::approx_eq; -use redis_protocol::resp2::types::NULL; -use std::{ - borrow::Cow, - collections::{BTreeMap, HashMap, HashSet, VecDeque}, - convert::{TryFrom, TryInto}, - fmt, - hash::{Hash, Hasher}, - iter::FromIterator, - mem, - ops::{Deref, DerefMut}, - str, -}; - -#[cfg(feature = "i-scripts")] -use crate::types::Function; -#[cfg(feature = "i-geo")] -use crate::types::{GeoPosition, GeoRadiusInfo}; -#[cfg(feature = "i-streams")] -use crate::types::{XReadResponse, XReadValue}; -#[cfg(feature = "serde-json")] -use serde_json::Value; - -static TRUE_STR: Str = utils::static_str("true"); -static FALSE_STR: Str = utils::static_str("false"); - -macro_rules! impl_string_or_number( - ($t:ty) => { - impl From<$t> for StringOrNumber { - fn from(val: $t) -> Self { - StringOrNumber::Number(val as i64) - } - } - } -); - -macro_rules! impl_from_str_for_redis_key( - ($t:ty) => { - impl From<$t> for RedisKey { - fn from(val: $t) -> Self { - RedisKey { key: val.to_string().into() } - } - } - } -); - -/// An argument representing a string or number. -#[derive(Clone, Debug)] -pub enum StringOrNumber { - String(Str), - Number(i64), - Double(f64), -} - -impl PartialEq for StringOrNumber { - fn eq(&self, other: &Self) -> bool { - match *self { - StringOrNumber::String(ref s) => match *other { - StringOrNumber::String(ref _s) => s == _s, - _ => false, - }, - StringOrNumber::Number(ref i) => match *other { - StringOrNumber::Number(ref _i) => *i == *_i, - _ => false, - }, - StringOrNumber::Double(ref d) => match *other { - StringOrNumber::Double(ref _d) => utils::f64_eq(*d, *_d), - _ => false, - }, - } - } -} - -impl Eq for StringOrNumber {} - -impl StringOrNumber { - /// An optimized way to convert from `&'static str` that avoids copying or moving the underlying bytes. - pub fn from_static_str(s: &'static str) -> Self { - StringOrNumber::String(utils::static_str(s)) - } - - #[cfg(feature = "i-streams")] - pub(crate) fn into_arg(self) -> RedisValue { - match self { - StringOrNumber::String(s) => RedisValue::String(s), - StringOrNumber::Number(n) => RedisValue::Integer(n), - StringOrNumber::Double(f) => RedisValue::Double(f), - } - } -} - -impl TryFrom for StringOrNumber { - type Error = RedisError; - - fn try_from(value: RedisValue) -> Result { - let val = match value { - RedisValue::String(s) => StringOrNumber::String(s), - RedisValue::Integer(i) => StringOrNumber::Number(i), - RedisValue::Double(f) => StringOrNumber::Double(f), - RedisValue::Bytes(b) => StringOrNumber::String(Str::from_inner(b)?), - _ => return Err(RedisError::new(RedisErrorKind::InvalidArgument, "")), - }; - - Ok(val) - } -} - -impl<'a> From<&'a str> for StringOrNumber { - fn from(s: &'a str) -> Self { - StringOrNumber::String(s.into()) - } -} - -impl From for StringOrNumber { - fn from(s: String) -> Self { - StringOrNumber::String(s.into()) - } -} - -impl From for StringOrNumber { - fn from(s: Str) -> Self { - StringOrNumber::String(s) - } -} - -impl_string_or_number!(i8); -impl_string_or_number!(i16); -impl_string_or_number!(i32); -impl_string_or_number!(i64); -impl_string_or_number!(isize); -impl_string_or_number!(u8); -impl_string_or_number!(u16); -impl_string_or_number!(u32); -impl_string_or_number!(u64); -impl_string_or_number!(usize); - -impl From for StringOrNumber { - fn from(f: f32) -> Self { - StringOrNumber::Double(f as f64) - } -} - -impl From for StringOrNumber { - fn from(f: f64) -> Self { - StringOrNumber::Double(f) - } -} - -/// A key in Redis. -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct RedisKey { - key: Bytes, -} - -impl RedisKey { - /// Create a new `RedisKey` from static bytes without copying. - pub const fn from_static(b: &'static [u8]) -> Self { - RedisKey { - key: Bytes::from_static(b), - } - } - - /// Create a new `RedisKey` from a `&'static str` without copying. - pub const fn from_static_str(b: &'static str) -> Self { - RedisKey { - key: Bytes::from_static(b.as_bytes()), - } - } - - /// Read the key as a str slice if it can be parsed as a UTF8 string. - pub fn as_str(&self) -> Option<&str> { - str::from_utf8(&self.key).ok() - } - - /// Read the key as a byte slice. - pub fn as_bytes(&self) -> &[u8] { - &self.key - } - - /// Read the inner `Bytes` struct. - pub fn inner(&self) -> &Bytes { - &self.key - } - - /// Read the key as a lossy UTF8 string with `String::from_utf8_lossy`. - pub fn as_str_lossy(&self) -> Cow { - String::from_utf8_lossy(&self.key) - } - - /// Convert the key to a UTF8 string, if possible. - pub fn into_string(self) -> Option { - String::from_utf8(self.key.to_vec()).ok() - } - - /// Read the inner bytes making up the key. - pub fn into_bytes(self) -> Bytes { - self.key - } - - /// Parse and return the key as a `Str` without copying the inner contents. - pub fn as_bytes_str(&self) -> Option { - Str::from_inner(self.key.clone()).ok() - } - - /// Hash the key to find the associated cluster [hash slot](https://redis.io/topics/cluster-spec#keys-distribution-model). - pub fn cluster_hash(&self) -> u16 { - redis_protocol::redis_keyslot(&self.key) - } - - /// Read the `host:port` of the cluster node that owns the key if the client is clustered and the cluster state is - /// known. - pub fn cluster_owner(&self, client: &C) -> Option - where - C: ClientLike, - { - if client.is_clustered() { - let hash_slot = self.cluster_hash(); - client - .inner() - .with_cluster_state(|state| Ok(state.get_server(hash_slot).cloned())) - .ok() - .and_then(|server| server) - } else { - None - } - } - - /// Replace this key with an empty byte array, returning the bytes from the original key. - pub fn take(&mut self) -> Bytes { - self.key.split_to(self.key.len()) - } - - /// Attempt to convert the key to any type that implements [FromRedisKey](crate::types::FromRedisKey). - /// - /// See the [RedisValue::convert](crate::types::RedisValue::convert) documentation for more information. - pub fn convert(self) -> Result - where - K: FromRedisKey, - { - K::from_key(self) - } -} - -impl TryFrom for RedisKey { - type Error = RedisError; - - fn try_from(value: RedisValue) -> Result { - let val = match value { - RedisValue::String(s) => RedisKey { key: s.into_inner() }, - RedisValue::Integer(i) => RedisKey { - key: i.to_string().into(), - }, - RedisValue::Double(f) => RedisKey { - key: f.to_string().into(), - }, - RedisValue::Bytes(b) => RedisKey { key: b }, - RedisValue::Boolean(b) => match b { - true => RedisKey { - key: TRUE_STR.clone().into_inner(), - }, - false => RedisKey { - key: FALSE_STR.clone().into_inner(), - }, - }, - RedisValue::Queued => utils::static_str(QUEUED).into(), - _ => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Cannot convert to key.", - )) - }, - }; - - Ok(val) - } -} - -impl From for RedisKey { - fn from(b: Bytes) -> Self { - RedisKey { key: b } - } -} - -impl From> for RedisKey { - fn from(b: Box<[u8]>) -> Self { - RedisKey { key: b.into() } - } -} - -impl<'a> From<&'a [u8]> for RedisKey { - fn from(b: &'a [u8]) -> Self { - RedisKey { key: b.to_vec().into() } - } -} - -impl From for RedisKey { - fn from(s: String) -> Self { - RedisKey { key: s.into() } - } -} - -impl From<&str> for RedisKey { - fn from(s: &str) -> Self { - RedisKey { - key: s.as_bytes().to_vec().into(), - } - } -} - -impl From<&String> for RedisKey { - fn from(s: &String) -> Self { - RedisKey { key: s.clone().into() } - } -} - -impl From for RedisKey { - fn from(s: Str) -> Self { - RedisKey { key: s.into_inner() } - } -} - -impl From<&Str> for RedisKey { - fn from(s: &Str) -> Self { - RedisKey { key: s.inner().clone() } - } -} - -impl From<&RedisKey> for RedisKey { - fn from(k: &RedisKey) -> RedisKey { - k.clone() - } -} - -impl From for RedisKey { - fn from(b: bool) -> Self { - match b { - true => RedisKey::from_static_str("true"), - false => RedisKey::from_static_str("false"), - } - } -} - -impl_from_str_for_redis_key!(u8); -impl_from_str_for_redis_key!(u16); -impl_from_str_for_redis_key!(u32); -impl_from_str_for_redis_key!(u64); -impl_from_str_for_redis_key!(u128); -impl_from_str_for_redis_key!(usize); -impl_from_str_for_redis_key!(i8); -impl_from_str_for_redis_key!(i16); -impl_from_str_for_redis_key!(i32); -impl_from_str_for_redis_key!(i64); -impl_from_str_for_redis_key!(i128); -impl_from_str_for_redis_key!(isize); -impl_from_str_for_redis_key!(f32); -impl_from_str_for_redis_key!(f64); - -/// A map of `(RedisKey, RedisValue)` pairs. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct RedisMap { - pub(crate) inner: HashMap, -} - -impl RedisMap { - /// Create a new empty map. - pub fn new() -> Self { - RedisMap { inner: HashMap::new() } - } - - /// Replace the value an empty map, returning the original value. - pub fn take(&mut self) -> Self { - RedisMap { - inner: mem::take(&mut self.inner), - } - } - - /// Read the number of (key, value) pairs in the map. - pub fn len(&self) -> usize { - self.inner.len() - } - - /// Take the inner `HashMap`. - pub fn inner(self) -> HashMap { - self.inner - } -} - -impl Deref for RedisMap { - type Target = HashMap; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl DerefMut for RedisMap { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} - -impl<'a> From<&'a RedisMap> for RedisMap { - fn from(vals: &'a RedisMap) -> Self { - vals.clone() - } -} - -impl TryFrom> for RedisMap -where - K: TryInto, - K::Error: Into, - V: TryInto, - V::Error: Into, -{ - type Error = RedisError; - - fn try_from(value: HashMap) -> Result { - Ok(RedisMap { - inner: utils::into_redis_map(value.into_iter())?, - }) - } -} - -impl TryFrom> for RedisMap -where - K: TryInto, - K::Error: Into, - V: TryInto, - V::Error: Into, -{ - type Error = RedisError; - - fn try_from(value: BTreeMap) -> Result { - Ok(RedisMap { - inner: utils::into_redis_map(value.into_iter())?, - }) - } -} - -impl From<()> for RedisMap { - fn from(_: ()) -> Self { - RedisMap::new() - } -} - -impl TryFrom<(K, V)> for RedisMap -where - K: TryInto, - K::Error: Into, - V: TryInto, - V::Error: Into, -{ - type Error = RedisError; - - fn try_from((key, value): (K, V)) -> Result { - let mut inner = HashMap::with_capacity(1); - inner.insert(to!(key)?, to!(value)?); - Ok(RedisMap { inner }) - } -} - -impl TryFrom> for RedisMap -where - K: TryInto, - K::Error: Into, - V: TryInto, - V::Error: Into, -{ - type Error = RedisError; - - fn try_from(values: Vec<(K, V)>) -> Result { - let mut inner = HashMap::with_capacity(values.len()); - for (key, value) in values.into_iter() { - inner.insert(to!(key)?, to!(value)?); - } - Ok(RedisMap { inner }) - } -} - -impl TryFrom<[(K, V); N]> for RedisMap -where - K: TryInto, - K::Error: Into, - V: TryInto, - V::Error: Into, -{ - type Error = RedisError; - - fn try_from(value: [(K, V); N]) -> Result { - let mut inner = HashMap::with_capacity(value.len()); - for (key, value) in value.into_iter() { - inner.insert(to!(key)?, to!(value)?); - } - - Ok(RedisMap { inner }) - } -} -impl<'a, K, V, const N: usize> TryFrom<&'a [(K, V); N]> for RedisMap -where - K: TryInto + Clone, - K::Error: Into, - V: TryInto + Clone, - V::Error: Into, -{ - type Error = RedisError; - - fn try_from(value: &'a [(K, V); N]) -> Result { - let mut inner = HashMap::with_capacity(value.len()); - for (key, value) in value.iter() { - let (key, value) = (key.clone(), value.clone()); - inner.insert(to!(key)?, to!(value)?); - } - - Ok(RedisMap { inner }) - } -} - -impl TryFrom> for RedisMap -where - K: TryInto, - K::Error: Into, - V: TryInto, - V::Error: Into, -{ - type Error = RedisError; - - fn try_from(values: VecDeque<(K, V)>) -> Result { - let mut inner = HashMap::with_capacity(values.len()); - for (key, value) in values.into_iter() { - inner.insert(to!(key)?, to!(value)?); - } - Ok(RedisMap { inner }) - } -} - -impl FromIterator<(K, V)> for RedisMap -where - K: Into, - V: Into, -{ - fn from_iter>(iter: T) -> Self { - Self { - inner: HashMap::from_iter(iter.into_iter().map(|(k, v)| (k.into(), v.into()))), - } - } -} - -/// The kind of value from Redis. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum RedisValueKind { - Boolean, - Integer, - Double, - String, - Bytes, - Null, - Queued, - Map, - Array, -} - -impl fmt::Display for RedisValueKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match *self { - RedisValueKind::Boolean => "Boolean", - RedisValueKind::Integer => "Integer", - RedisValueKind::Double => "Double", - RedisValueKind::String => "String", - RedisValueKind::Bytes => "Bytes", - RedisValueKind::Null => "nil", - RedisValueKind::Queued => "Queued", - RedisValueKind::Map => "Map", - RedisValueKind::Array => "Array", - }; - - write!(f, "{}", s) - } -} - -/// A value used in a Redis command. -#[derive(Clone, Debug)] -pub enum RedisValue { - /// A boolean value. - Boolean(bool), - /// An integer value. - Integer(i64), - /// A double floating point number. - Double(f64), - /// A string value. - String(Str), - /// A byte array value. - Bytes(Bytes), - /// A `nil` value. - Null, - /// A special value used to indicate a MULTI block command was received by the server. - Queued, - /// A map of key/value pairs, primarily used in RESP3 mode. - Map(RedisMap), - /// An ordered list of values. - /// - /// In RESP2 mode the server usually sends map structures as an array of key/value pairs. - Array(Vec), -} - -#[allow(clippy::match_like_matches_macro)] -impl PartialEq for RedisValue { - fn eq(&self, other: &Self) -> bool { - use RedisValue::*; - - match self { - Boolean(ref s) => match other { - Boolean(ref o) => *s == *o, - _ => false, - }, - Integer(ref s) => match other { - Integer(ref o) => *s == *o, - _ => false, - }, - Double(ref s) => match other { - Double(ref o) => approx_eq!(f64, *s, *o, ulps = 2), - _ => false, - }, - String(ref s) => match other { - String(ref o) => s == o, - _ => false, - }, - Bytes(ref s) => match other { - Bytes(ref o) => s == o, - _ => false, - }, - Null => match other { - Null => true, - _ => false, - }, - Queued => match other { - Queued => true, - _ => false, - }, - Map(ref s) => match other { - Map(ref o) => s == o, - _ => false, - }, - Array(ref s) => match other { - Array(ref o) => s == o, - _ => false, - }, - } - } -} - -impl Eq for RedisValue {} - -impl RedisValue { - /// Create a new `RedisValue::Bytes` from a static byte slice without copying. - pub fn from_static(b: &'static [u8]) -> Self { - RedisValue::Bytes(Bytes::from_static(b)) - } - - /// Create a new `RedisValue::String` from a static `str` without copying. - pub fn from_static_str(s: &'static str) -> Self { - RedisValue::String(utils::static_str(s)) - } - - /// Create a new `RedisValue` with the `OK` status. - pub fn new_ok() -> Self { - Self::from_static_str(OK) - } - - /// Whether the value is a simple string OK value. - pub fn is_ok(&self) -> bool { - match *self { - RedisValue::String(ref s) => *s == OK, - _ => false, - } - } - - /// Attempt to convert the value into an integer, returning the original string as an error if the parsing fails. - pub fn into_integer(self) -> Result { - match self { - RedisValue::String(s) => match s.parse::() { - Ok(i) => Ok(RedisValue::Integer(i)), - Err(_) => Err(RedisValue::String(s)), - }, - RedisValue::Integer(i) => Ok(RedisValue::Integer(i)), - _ => Err(self), - } - } - - /// Read the type of the value without any associated data. - pub fn kind(&self) -> RedisValueKind { - match *self { - RedisValue::Boolean(_) => RedisValueKind::Boolean, - RedisValue::Integer(_) => RedisValueKind::Integer, - RedisValue::Double(_) => RedisValueKind::Double, - RedisValue::String(_) => RedisValueKind::String, - RedisValue::Bytes(_) => RedisValueKind::Bytes, - RedisValue::Null => RedisValueKind::Null, - RedisValue::Queued => RedisValueKind::Queued, - RedisValue::Map(_) => RedisValueKind::Map, - RedisValue::Array(_) => RedisValueKind::Array, - } - } - - /// Check if the value is null. - pub fn is_null(&self) -> bool { - matches!(*self, RedisValue::Null) - } - - /// Check if the value is an integer. - pub fn is_integer(&self) -> bool { - matches!(self, RedisValue::Integer(_)) - } - - /// Check if the value is a string. - pub fn is_string(&self) -> bool { - matches!(*self, RedisValue::String(_)) - } - - /// Check if the value is an array of bytes. - pub fn is_bytes(&self) -> bool { - matches!(*self, RedisValue::Bytes(_)) - } - - /// Whether the value is a boolean value or can be parsed as a boolean value. - #[allow(clippy::match_like_matches_macro)] - pub fn is_boolean(&self) -> bool { - match *self { - RedisValue::Boolean(_) => true, - RedisValue::Integer(0 | 1) => true, - RedisValue::Integer(_) => false, - RedisValue::String(ref s) => match s.as_bytes() { - b"true" | b"false" | b"t" | b"f" | b"TRUE" | b"FALSE" | b"T" | b"F" | b"1" | b"0" => true, - _ => false, - }, - _ => false, - } - } - - /// Whether the inner value is a double or can be parsed as a double. - pub fn is_double(&self) -> bool { - match *self { - RedisValue::Double(_) => true, - RedisValue::String(ref s) => utils::redis_string_to_f64(s).is_ok(), - _ => false, - } - } - - /// Check if the value is a `QUEUED` response. - pub fn is_queued(&self) -> bool { - matches!(*self, RedisValue::Queued) - } - - /// Whether the value is an array or map. - pub fn is_aggregate_type(&self) -> bool { - matches!(*self, RedisValue::Array(_) | RedisValue::Map(_)) - } - - /// Whether the value is a `RedisMap`. - /// - /// See [is_maybe_map](Self::is_maybe_map) for a function that also checks for arrays that likely represent a map in - /// RESP2 mode. - pub fn is_map(&self) -> bool { - matches!(*self, RedisValue::Map(_)) - } - - /// Whether the value is a `RedisMap` or an array with an even number of elements where each even-numbered - /// element is not an aggregate type. - /// - /// RESP2 and RESP3 encode maps differently, and this function can be used to duck-type maps across protocol - /// versions. - pub fn is_maybe_map(&self) -> bool { - match *self { - RedisValue::Map(_) => true, - RedisValue::Array(ref arr) => utils::is_maybe_array_map(arr), - _ => false, - } - } - - /// Whether the value is an array. - pub fn is_array(&self) -> bool { - matches!(*self, RedisValue::Array(_)) - } - - /// Read and return the inner value as a `u64`, if possible. - pub fn as_u64(&self) -> Option { - match self { - RedisValue::Integer(ref i) => { - if *i >= 0 { - Some(*i as u64) - } else { - None - } - }, - RedisValue::String(ref s) => s.parse::().ok(), - RedisValue::Array(ref inner) => { - if inner.len() == 1 { - inner.first().and_then(|v| v.as_u64()) - } else { - None - } - }, - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Some(0), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => None, - _ => None, - } - } - - /// Read and return the inner value as a `i64`, if possible. - pub fn as_i64(&self) -> Option { - match self { - RedisValue::Integer(ref i) => Some(*i), - RedisValue::String(ref s) => s.parse::().ok(), - RedisValue::Array(ref inner) => { - if inner.len() == 1 { - inner.first().and_then(|v| v.as_i64()) - } else { - None - } - }, - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Some(0), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => None, - _ => None, - } - } - - /// Read and return the inner value as a `usize`, if possible. - pub fn as_usize(&self) -> Option { - match self { - RedisValue::Integer(i) => { - if *i >= 0 { - Some(*i as usize) - } else { - None - } - }, - RedisValue::String(ref s) => s.parse::().ok(), - RedisValue::Array(ref inner) => { - if inner.len() == 1 { - inner.first().and_then(|v| v.as_usize()) - } else { - None - } - }, - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Some(0), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => None, - _ => None, - } - } - - /// Read and return the inner value as a `f64`, if possible. - pub fn as_f64(&self) -> Option { - match self { - RedisValue::Double(ref f) => Some(*f), - RedisValue::String(ref s) => utils::redis_string_to_f64(s).ok(), - RedisValue::Integer(ref i) => Some(*i as f64), - RedisValue::Array(ref inner) => { - if inner.len() == 1 { - inner.first().and_then(|v| v.as_f64()) - } else { - None - } - }, - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Some(0.0), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => None, - _ => None, - } - } - - /// Read and return the inner `String` if the value is a string or scalar value. - pub fn into_string(self) -> Option { - match self { - RedisValue::Boolean(b) => Some(b.to_string()), - RedisValue::Double(f) => Some(f.to_string()), - RedisValue::String(s) => Some(s.to_string()), - RedisValue::Bytes(b) => String::from_utf8(b.to_vec()).ok(), - RedisValue::Integer(i) => Some(i.to_string()), - RedisValue::Queued => Some(QUEUED.to_owned()), - RedisValue::Array(mut inner) => { - if inner.len() == 1 { - inner.pop().and_then(|v| v.into_string()) - } else { - None - } - }, - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Some(String::new()), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => None, - _ => None, - } - } - - /// Read and return the inner data as a `Str` from the `bytes` crate. - pub fn into_bytes_str(self) -> Option { - match self { - RedisValue::Boolean(b) => match b { - true => Some(TRUE_STR.clone()), - false => Some(FALSE_STR.clone()), - }, - RedisValue::Double(f) => Some(f.to_string().into()), - RedisValue::String(s) => Some(s), - RedisValue::Bytes(b) => Str::from_inner(b).ok(), - RedisValue::Integer(i) => Some(i.to_string().into()), - RedisValue::Queued => Some(utils::static_str(QUEUED)), - RedisValue::Array(mut inner) => { - if inner.len() == 1 { - inner.pop().and_then(|v| v.into_bytes_str()) - } else { - None - } - }, - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Some(Str::new()), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => None, - _ => None, - } - } - - /// Read the inner value as a `Str`. - pub fn as_bytes_str(&self) -> Option { - match self { - RedisValue::Boolean(ref b) => match *b { - true => Some(TRUE_STR.clone()), - false => Some(FALSE_STR.clone()), - }, - RedisValue::Double(ref f) => Some(f.to_string().into()), - RedisValue::String(ref s) => Some(s.clone()), - RedisValue::Bytes(ref b) => Str::from_inner(b.clone()).ok(), - RedisValue::Integer(ref i) => Some(i.to_string().into()), - RedisValue::Queued => Some(utils::static_str(QUEUED)), - RedisValue::Array(ref inner) => { - if inner.len() == 1 { - inner[0].as_bytes_str() - } else { - None - } - }, - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Some(Str::new()), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => None, - _ => None, - } - } - - /// Read and return the inner `String` if the value is a string or scalar value. - /// - /// Note: this will cast integers and doubles to strings. - pub fn as_string(&self) -> Option { - match self { - RedisValue::Boolean(ref b) => Some(b.to_string()), - RedisValue::Double(ref f) => Some(f.to_string()), - RedisValue::String(ref s) => Some(s.to_string()), - RedisValue::Bytes(ref b) => str::from_utf8(b).ok().map(|s| s.to_owned()), - RedisValue::Integer(ref i) => Some(i.to_string()), - RedisValue::Queued => Some(QUEUED.to_owned()), - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Some(String::new()), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => None, - _ => None, - } - } - - /// Read the inner value as a string slice. - /// - /// Null is returned as `"nil"` and scalar values are cast to a string. - pub fn as_str(&self) -> Option> { - let s: Cow = match *self { - RedisValue::Double(ref f) => Cow::Owned(f.to_string()), - RedisValue::Boolean(ref b) => Cow::Owned(b.to_string()), - RedisValue::String(ref s) => Cow::Borrowed(s.deref()), - RedisValue::Integer(ref i) => Cow::Owned(i.to_string()), - RedisValue::Queued => Cow::Borrowed(QUEUED), - RedisValue::Bytes(ref b) => return str::from_utf8(b).ok().map(Cow::Borrowed), - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Cow::Borrowed(""), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => return None, - _ => return None, - }; - - Some(s) - } - - /// Read the inner value as a string, using `String::from_utf8_lossy` on byte slices. - pub fn as_str_lossy(&self) -> Option> { - let s: Cow = match *self { - RedisValue::Boolean(ref b) => Cow::Owned(b.to_string()), - RedisValue::Double(ref f) => Cow::Owned(f.to_string()), - RedisValue::String(ref s) => Cow::Borrowed(s.deref()), - RedisValue::Integer(ref i) => Cow::Owned(i.to_string()), - RedisValue::Queued => Cow::Borrowed(QUEUED), - RedisValue::Bytes(ref b) => String::from_utf8_lossy(b), - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Cow::Borrowed(""), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => return None, - _ => return None, - }; - - Some(s) - } - - /// Read the inner value as an array of bytes, if possible. - pub fn as_bytes(&self) -> Option<&[u8]> { - match *self { - RedisValue::String(ref s) => Some(s.as_bytes()), - RedisValue::Bytes(ref b) => Some(b), - RedisValue::Queued => Some(QUEUED.as_bytes()), - _ => None, - } - } - - /// Attempt to convert the value to a `bool`. - pub fn as_bool(&self) -> Option { - match *self { - RedisValue::Boolean(b) => Some(b), - RedisValue::Integer(ref i) => match *i { - 0 => Some(false), - 1 => Some(true), - _ => None, - }, - RedisValue::String(ref s) => match s.as_bytes() { - b"true" | b"TRUE" | b"t" | b"T" | b"1" => Some(true), - b"false" | b"FALSE" | b"f" | b"F" | b"0" => Some(false), - _ => None, - }, - RedisValue::Array(ref inner) => { - if inner.len() == 1 { - inner.first().and_then(|v| v.as_bool()) - } else { - None - } - }, - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Some(false), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => None, - _ => None, - } - } - - /// Attempt to convert this value to a Redis map if it's an array with an even number of elements. - pub fn into_map(self) -> Result { - match self { - RedisValue::Map(map) => Ok(map), - RedisValue::Array(mut values) => { - if values.len() % 2 != 0 { - return Err(RedisError::new( - RedisErrorKind::Unknown, - "Expected an even number of elements.", - )); - } - let mut inner = HashMap::with_capacity(values.len() / 2); - while values.len() >= 2 { - let value = values.pop().unwrap(); - let key: RedisKey = values.pop().unwrap().try_into()?; - - inner.insert(key, value); - } - - Ok(RedisMap { inner }) - }, - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Ok(RedisMap::new()), - _ => Err(RedisError::new(RedisErrorKind::Unknown, "Could not convert to map.")), - } - } - - pub(crate) fn into_multiple_values(self) -> Vec { - match self { - RedisValue::Array(values) => values, - RedisValue::Map(map) => map - .inner() - .into_iter() - .flat_map(|(k, v)| [RedisValue::Bytes(k.into_bytes()), v]) - .collect(), - RedisValue::Null => Vec::new(), - _ => vec![self], - } - } - - /// Convert the array value to a set, if possible. - pub fn into_set(self) -> Result, RedisError> { - match self { - RedisValue::Array(values) => Ok(values.into_iter().collect()), - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Ok(HashSet::new()), - _ => Err(RedisError::new_parse("Could not convert to set.")), - } - } - - /// Convert a `RedisValue` to `Vec<(RedisValue, f64)>`, if possible. - pub fn into_zset_result(self) -> Result, RedisError> { - protocol_utils::value_to_zset_result(self) - } - - /// Convert this value to an array if it's an array or map. - /// - /// If the value is not an array or map this returns a single-element array containing the original value. - pub fn into_array(self) -> Vec { - match self { - RedisValue::Array(values) => values, - RedisValue::Map(map) => { - let mut out = Vec::with_capacity(map.len() * 2); - for (key, value) in map.inner().into_iter() { - out.extend([key.into(), value]); - } - out - }, - _ => vec![self], - } - } - - /// Convert the value to an array of bytes, if possible. - pub fn into_owned_bytes(self) -> Option> { - let v = match self { - RedisValue::String(s) => s.to_string().into_bytes(), - RedisValue::Bytes(b) => b.to_vec(), - RedisValue::Queued => QUEUED.as_bytes().to_vec(), - RedisValue::Array(mut inner) => { - if inner.len() == 1 { - return inner.pop().and_then(|v| v.into_owned_bytes()); - } else { - return None; - } - }, - RedisValue::Integer(i) => i.to_string().into_bytes(), - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Vec::new(), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => return None, - _ => return None, - }; - - Some(v) - } - - /// Convert the value into a `Bytes` view. - pub fn into_bytes(self) -> Option { - let v = match self { - RedisValue::String(s) => s.inner().clone(), - RedisValue::Bytes(b) => b, - RedisValue::Queued => Bytes::from_static(QUEUED.as_bytes()), - RedisValue::Array(mut inner) => { - if inner.len() == 1 { - return inner.pop().and_then(|v| v.into_bytes()); - } else { - return None; - } - }, - RedisValue::Integer(i) => i.to_string().into(), - #[cfg(feature = "default-nil-types")] - RedisValue::Null => Bytes::new(), - #[cfg(not(feature = "default-nil-types"))] - RedisValue::Null => return None, - _ => return None, - }; - - Some(v) - } - - /// Return the length of the inner array if the value is an array. - pub fn array_len(&self) -> Option { - match self { - RedisValue::Array(ref a) => Some(a.len()), - _ => None, - } - } - - /// Whether the value is an array with one element. - pub(crate) fn is_single_element_vec(&self) -> bool { - if let RedisValue::Array(ref d) = self { - d.len() == 1 - } else { - false - } - } - - /// Pop the first value in the inner array or return the original value. - /// - /// This uses unwrap. Use [is_single_element_vec] first. - pub(crate) fn pop_or_take(self) -> Self { - if let RedisValue::Array(mut values) = self { - values.pop().unwrap() - } else { - self - } - } - - /// Flatten adjacent nested arrays to the provided depth. - /// - /// See the [XREAD](crate::interfaces::StreamsInterface::xread) documentation for an example of when this might be - /// useful. - pub fn flatten_array_values(self, depth: usize) -> Self { - utils::flatten_nested_array_values(self, depth) - } - - /// A utility function to convert the response from `XREAD` or `XREADGROUP` into a type with a less verbose type - /// declaration. - /// - /// This function supports responses in both RESP2 and RESP3 formats. - /// - /// See the [XREAD](crate::interfaces::StreamsInterface::xread) (or `XREADGROUP`) documentation for more - /// information. - #[cfg(feature = "i-streams")] - #[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))] - pub fn into_xread_response(self) -> Result, RedisError> - where - K1: FromRedisKey + Hash + Eq, - K2: FromRedisKey + Hash + Eq, - I: FromRedis, - V: FromRedis, - { - self.flatten_array_values(2).convert() - } - - /// A utility function to convert the response from `XCLAIM`, etc into a type with a less verbose type declaration. - /// - /// This function supports responses in both RESP2 and RESP3 formats. - #[cfg(feature = "i-streams")] - #[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))] - pub fn into_xread_value(self) -> Result>, RedisError> - where - K: FromRedisKey + Hash + Eq, - I: FromRedis, - V: FromRedis, - { - self.flatten_array_values(1).convert() - } - - /// A utility function to convert the response from `XAUTOCLAIM` into a type with a less verbose type declaration. - /// - /// This function supports responses in both RESP2 and RESP3 formats. - /// - /// Note: the new (as of Redis 7.x) return value containing message PIDs that were deleted from the PEL are dropped. - /// Callers should use `xautoclaim` instead if this data is needed. - #[cfg(feature = "i-streams")] - #[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))] - pub fn into_xautoclaim_values(self) -> Result<(String, Vec>), RedisError> - where - K: FromRedisKey + Hash + Eq, - I: FromRedis, - V: FromRedis, - { - if let RedisValue::Array(mut values) = self { - if values.len() == 3 { - // convert the redis 7.x response format to the v6 format - trace!("Removing the third message PID elements from XAUTOCLAIM response."); - values.pop(); - } - - // unwrap checked above - let entries = values.pop().unwrap(); - let cursor: String = values.pop().unwrap().convert()?; - - Ok((cursor, entries.flatten_array_values(1).convert()?)) - } else { - Err(RedisError::new_parse("Expected array response.")) - } - } - - /// Parse the value as the response from `FUNCTION LIST`, including only functions with the provided library `name`. - #[cfg(feature = "i-scripts")] - #[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] - pub fn as_functions(&self, name: &str) -> Result, RedisError> { - utils::value_to_functions(self, name) - } - - /// Convert the value into a `GeoPosition`, if possible. - /// - /// Null values are returned as `None` to work more easily with the result of the `GEOPOS` command. - #[cfg(feature = "i-geo")] - #[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))] - pub fn as_geo_position(&self) -> Result, RedisError> { - if self.is_null() { - Ok(None) - } else { - GeoPosition::try_from(self.clone()).map(Some) - } - } - - /// Parse the value as the response to any of the relevant GEO commands that return an array of - /// [GeoRadiusInfo](crate::types::GeoRadiusInfo) values, such as `GEOSEARCH`, GEORADIUS`, etc. - #[cfg(feature = "i-geo")] - #[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))] - pub fn into_geo_radius_result( - self, - withcoord: bool, - withdist: bool, - withhash: bool, - ) -> Result, RedisError> { - match self { - RedisValue::Array(data) => data - .into_iter() - .map(|value| GeoRadiusInfo::from_redis_value(value, withcoord, withdist, withhash)) - .collect(), - RedisValue::Null => Ok(Vec::new()), - _ => Err(RedisError::new(RedisErrorKind::Parse, "Expected array.")), - } - } - - /// Replace this value with `RedisValue::Null`, returning the original value. - pub fn take(&mut self) -> RedisValue { - mem::replace(self, RedisValue::Null) - } - - /// Attempt to convert this value to any value that implements the [FromRedis](crate::types::FromRedis) trait. - pub fn convert(self) -> Result - where - R: FromRedis, - { - R::from_value(self) - } - - /// Whether the value can be hashed. - /// - /// Some use cases require using `RedisValue` types as keys in a `HashMap`, etc. Trying to do so with an aggregate - /// type can panic, and this function can be used to more gracefully handle this situation. - pub fn can_hash(&self) -> bool { - matches!( - self.kind(), - RedisValueKind::String - | RedisValueKind::Boolean - | RedisValueKind::Double - | RedisValueKind::Integer - | RedisValueKind::Bytes - | RedisValueKind::Null - | RedisValueKind::Array - | RedisValueKind::Queued - ) - } - - /// Convert the value to JSON. - #[cfg(feature = "serde-json")] - #[cfg_attr(docsrs, doc(cfg(feature = "serde-json")))] - pub fn into_json(self) -> Result { - Value::from_value(self) - } -} - -impl Hash for RedisValue { - fn hash(&self, state: &mut H) { - // used to prevent collisions between different types - let prefix = match self.kind() { - RedisValueKind::Boolean => b'B', - RedisValueKind::Double => b'd', - RedisValueKind::Integer => b'i', - RedisValueKind::String => b's', - RedisValueKind::Null => b'n', - RedisValueKind::Queued => b'q', - RedisValueKind::Array => b'a', - RedisValueKind::Map => b'm', - RedisValueKind::Bytes => b'b', - }; - prefix.hash(state); - - match *self { - RedisValue::Boolean(b) => b.hash(state), - RedisValue::Double(f) => f.to_be_bytes().hash(state), - RedisValue::Integer(d) => d.hash(state), - RedisValue::String(ref s) => s.hash(state), - RedisValue::Bytes(ref b) => b.hash(state), - RedisValue::Null => NULL.hash(state), - RedisValue::Queued => QUEUED.hash(state), - RedisValue::Array(ref arr) => { - for value in arr.iter() { - value.hash(state); - } - }, - _ => panic!("Cannot hash aggregate value."), - } - } -} - -impl From for RedisValue { - fn from(d: u8) -> Self { - RedisValue::Integer(d as i64) - } -} - -impl From for RedisValue { - fn from(d: u16) -> Self { - RedisValue::Integer(d as i64) - } -} - -impl From for RedisValue { - fn from(d: u32) -> Self { - RedisValue::Integer(d as i64) - } -} - -impl From for RedisValue { - fn from(d: i8) -> Self { - RedisValue::Integer(d as i64) - } -} - -impl From for RedisValue { - fn from(d: i16) -> Self { - RedisValue::Integer(d as i64) - } -} - -impl From for RedisValue { - fn from(d: i32) -> Self { - RedisValue::Integer(d as i64) - } -} - -impl From for RedisValue { - fn from(d: i64) -> Self { - RedisValue::Integer(d) - } -} - -impl From for RedisValue { - fn from(f: f32) -> Self { - RedisValue::Double(f as f64) - } -} - -impl From for RedisValue { - fn from(f: f64) -> Self { - RedisValue::Double(f) - } -} - -impl TryFrom for RedisValue { - type Error = RedisError; - - fn try_from(d: u64) -> Result { - if d >= (i64::MAX as u64) { - return Err(RedisError::new(RedisErrorKind::Unknown, "Unsigned integer too large.")); - } - - Ok((d as i64).into()) - } -} - -impl TryFrom for RedisValue { - type Error = RedisError; - - fn try_from(d: u128) -> Result { - if d >= (i64::MAX as u128) { - return Err(RedisError::new(RedisErrorKind::Unknown, "Unsigned integer too large.")); - } - - Ok((d as i64).into()) - } -} - -impl TryFrom for RedisValue { - type Error = RedisError; - - fn try_from(d: i128) -> Result { - if d >= (i64::MAX as i128) { - return Err(RedisError::new(RedisErrorKind::Unknown, "Signed integer too large.")); - } - - Ok((d as i64).into()) - } -} - -impl TryFrom for RedisValue { - type Error = RedisError; - - fn try_from(d: usize) -> Result { - if d >= (i64::MAX as usize) { - return Err(RedisError::new(RedisErrorKind::Unknown, "Unsigned integer too large.")); - } - - Ok((d as i64).into()) - } -} - -impl From for RedisValue { - fn from(s: Str) -> Self { - RedisValue::String(s) - } -} - -impl From for RedisValue { - fn from(b: Bytes) -> Self { - RedisValue::Bytes(b) - } -} - -impl From> for RedisValue { - fn from(b: Box<[u8]>) -> Self { - RedisValue::Bytes(b.into()) - } -} - -impl From for RedisValue { - fn from(d: String) -> Self { - RedisValue::String(Str::from(d)) - } -} - -impl<'a> From<&'a String> for RedisValue { - fn from(d: &'a String) -> Self { - RedisValue::String(Str::from(d)) - } -} - -impl<'a> From<&'a str> for RedisValue { - fn from(d: &'a str) -> Self { - RedisValue::String(Str::from(d)) - } -} - -impl<'a> From<&'a [u8]> for RedisValue { - fn from(b: &'a [u8]) -> Self { - RedisValue::Bytes(Bytes::from(b.to_vec())) - } -} - -impl From for RedisValue { - fn from(d: bool) -> Self { - RedisValue::Boolean(d) - } -} - -impl TryFrom> for RedisValue -where - T: TryInto, - T::Error: Into, -{ - type Error = RedisError; - - fn try_from(d: Option) -> Result { - match d { - Some(i) => to!(i), - None => Ok(RedisValue::Null), - } - } -} - -impl<'a, T, const N: usize> TryFrom<&'a [T; N]> for RedisValue -where - T: TryInto + Clone, - T::Error: Into, -{ - type Error = RedisError; - - fn try_from(value: &'a [T; N]) -> Result { - let values = value - .iter() - .map(|v| v.clone().try_into().map_err(|e| e.into())) - .collect::, RedisError>>()?; - - Ok(RedisValue::Array(values)) - } -} - -impl TryFrom<[T; N]> for RedisValue -where - T: TryInto + Clone, - T::Error: Into, -{ - type Error = RedisError; - - fn try_from(value: [T; N]) -> Result { - let values = value - .into_iter() - .map(|v| v.try_into().map_err(|e| e.into())) - .collect::, RedisError>>()?; - - Ok(RedisValue::Array(values)) - } -} - -impl TryFrom> for RedisValue -where - T: TryInto, - T::Error: Into, -{ - type Error = RedisError; - - fn try_from(value: Vec) -> Result { - let values = value - .into_iter() - .map(|v| v.try_into().map_err(|e| e.into())) - .collect::, RedisError>>()?; - - Ok(RedisValue::Array(values)) - } -} - -impl TryFrom> for RedisValue -where - T: TryInto, - T::Error: Into, -{ - type Error = RedisError; - - fn try_from(value: VecDeque) -> Result { - let values = value - .into_iter() - .map(|v| v.try_into().map_err(|e| e.into())) - .collect::, RedisError>>()?; - - Ok(RedisValue::Array(values)) - } -} - -impl FromIterator for RedisValue -where - V: Into, -{ - fn from_iter>(iter: I) -> Self { - RedisValue::Array(iter.into_iter().map(|v| v.into()).collect()) - } -} - -impl TryFrom> for RedisValue -where - K: TryInto, - K::Error: Into, - V: TryInto, - V::Error: Into, -{ - type Error = RedisError; - - fn try_from(d: HashMap) -> Result { - Ok(RedisValue::Map(RedisMap { - inner: utils::into_redis_map(d.into_iter())?, - })) - } -} - -impl TryFrom> for RedisValue -where - K: TryInto, - K::Error: Into, - V: TryInto, - V::Error: Into, -{ - type Error = RedisError; - - fn try_from(d: BTreeMap) -> Result { - Ok(RedisValue::Map(RedisMap { - inner: utils::into_redis_map(d.into_iter())?, - })) - } -} - -impl From for RedisValue { - fn from(d: RedisKey) -> Self { - RedisValue::Bytes(d.key) - } -} - -impl From for RedisValue { - fn from(m: RedisMap) -> Self { - RedisValue::Map(m) - } -} - -impl From<()> for RedisValue { - fn from(_: ()) -> Self { - RedisValue::Null - } -} - -impl TryFrom for RedisValue { - type Error = RedisError; - - fn try_from(value: Resp3Frame) -> Result { - protocol_utils::frame_to_results(value) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn redis_map_from_iter() { - let map = [("hello", "world")].into_iter().collect::(); - assert_eq!(map.inner[&RedisKey::from("hello")], RedisValue::from("world")); - } -} diff --git a/src/types/builder.rs b/src/types/builder.rs deleted file mode 100644 index 2f3d9ace..00000000 --- a/src/types/builder.rs +++ /dev/null @@ -1,305 +0,0 @@ -use crate::{ - clients::{RedisClient, RedisPool}, - error::{RedisError, RedisErrorKind}, - prelude::ReconnectPolicy, - types::{ConnectionConfig, PerformanceConfig, RedisConfig, ServerConfig}, -}; - -#[cfg(not(feature = "glommio"))] -use crate::clients::ExclusivePool; -#[cfg(feature = "subscriber-client")] -use crate::clients::SubscriberClient; -#[cfg(feature = "sentinel-client")] -use crate::{clients::SentinelClient, types::SentinelConfig}; - -/// A client and pool builder interface. -/// -/// ```rust -/// # use std::time::Duration; -/// # use redis_protocol::resp3::types::RespVersion; -/// # use fred::prelude::*; -/// fn example() -> Result<(), RedisError> { -/// // use default values -/// let client = Builder::default_centralized().build()?; -/// -/// // or initialize from a URL or config -/// let config = RedisConfig::from_url("redis://localhost:6379/1")?; -/// let mut builder = Builder::from_config(config); -/// // or modify values in place (creating defaults if needed) -/// builder -/// .with_performance_config(|config| { -/// config.auto_pipeline = true; -/// }) -/// .with_config(|config| { -/// config.version = RespVersion::RESP3; -/// config.fail_fast = true; -/// }) -/// .with_connection_config(|config| { -/// config.tcp = TcpConfig { -/// nodelay: Some(true), -/// ..Default::default() -/// }; -/// config.internal_command_timeout = Duration::from_secs(10); -/// }); -/// // or overwrite configuration structs in place -/// builder.set_policy(ReconnectPolicy::new_exponential(0, 100, 30_000, 2)); -/// builder.set_performance_config(PerformanceConfig::default()); -/// -/// // reuse the builder as needed to create any kind of client -/// let client = builder.build()?; -/// let pool = builder.build_pool(3)?; -/// let subscriber = builder.build_subscriber_client()?; -/// -/// // ... -/// -/// Ok(()) -/// } -/// ``` -#[derive(Clone, Debug)] -pub struct Builder { - config: Option, - performance: PerformanceConfig, - connection: ConnectionConfig, - policy: Option, - #[cfg(feature = "sentinel-client")] - sentinel: Option, -} - -impl Default for Builder { - fn default() -> Self { - Builder { - config: None, - performance: PerformanceConfig::default(), - connection: ConnectionConfig::default(), - policy: None, - #[cfg(feature = "sentinel-client")] - sentinel: None, - } - } -} - -impl Builder { - /// Create a new builder instance with default config values for a centralized deployment. - pub fn default_centralized() -> Self { - Builder { - config: Some(RedisConfig { - server: ServerConfig::default_centralized(), - ..Default::default() - }), - ..Default::default() - } - } - - /// Create a new builder instance with default config values for a clustered deployment. - pub fn default_clustered() -> Self { - Builder { - config: Some(RedisConfig { - server: ServerConfig::default_clustered(), - ..Default::default() - }), - ..Default::default() - } - } - - /// Create a new builder instance from the provided client config. - pub fn from_config(config: RedisConfig) -> Self { - Builder { - config: Some(config), - ..Default::default() - } - } - - /// Read the client config. - pub fn get_config(&self) -> Option<&RedisConfig> { - self.config.as_ref() - } - - /// Read the reconnection policy. - pub fn get_policy(&self) -> Option<&ReconnectPolicy> { - self.policy.as_ref() - } - - /// Read the performance config. - pub fn get_performance_config(&self) -> &PerformanceConfig { - &self.performance - } - - /// Read the connection config. - pub fn get_connection_config(&self) -> &ConnectionConfig { - &self.connection - } - - /// Read the sentinel client config. - #[cfg(feature = "sentinel-client")] - #[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))] - pub fn get_sentinel_config(&self) -> Option<&RedisConfig> { - self.config.as_ref() - } - - /// Overwrite the client config on the builder. - pub fn set_config(&mut self, config: RedisConfig) -> &mut Self { - self.config = Some(config); - self - } - - /// Overwrite the reconnection policy on the builder. - pub fn set_policy(&mut self, policy: ReconnectPolicy) -> &mut Self { - self.policy = Some(policy); - self - } - - /// Overwrite the performance config on the builder. - pub fn set_performance_config(&mut self, config: PerformanceConfig) -> &mut Self { - self.performance = config; - self - } - - /// Overwrite the connection config on the builder. - pub fn set_connection_config(&mut self, config: ConnectionConfig) -> &mut Self { - self.connection = config; - self - } - - /// Overwrite the sentinel config on the builder. - #[cfg(feature = "sentinel-client")] - #[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))] - pub fn set_sentinel_config(&mut self, config: SentinelConfig) -> &mut Self { - self.sentinel = Some(config); - self - } - - /// Modify the client config in place, creating a new one with default centralized values first if needed. - pub fn with_config(&mut self, func: F) -> &mut Self - where - F: FnOnce(&mut RedisConfig), - { - if let Some(config) = self.config.as_mut() { - func(config); - } else { - let mut config = RedisConfig::default(); - func(&mut config); - self.config = Some(config); - } - - self - } - - /// Modify the performance config in place, creating a new one with default values first if needed. - pub fn with_performance_config(&mut self, func: F) -> &mut Self - where - F: FnOnce(&mut PerformanceConfig), - { - func(&mut self.performance); - self - } - - /// Modify the connection config in place, creating a new one with default values first if needed. - pub fn with_connection_config(&mut self, func: F) -> &mut Self - where - F: FnOnce(&mut ConnectionConfig), - { - func(&mut self.connection); - self - } - - /// Modify the sentinel config in place, creating a new one with default values first if needed. - #[cfg(feature = "sentinel-client")] - #[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))] - pub fn with_sentinel_config(&mut self, func: F) -> &mut Self - where - F: FnOnce(&mut SentinelConfig), - { - if let Some(config) = self.sentinel.as_mut() { - func(config); - } else { - let mut config = SentinelConfig::default(); - func(&mut config); - self.sentinel = Some(config); - } - - self - } - - /// Create a new client. - pub fn build(&self) -> Result { - if let Some(config) = self.config.as_ref() { - Ok(RedisClient::new( - config.clone(), - Some(self.performance.clone()), - Some(self.connection.clone()), - self.policy.clone(), - )) - } else { - Err(RedisError::new(RedisErrorKind::Config, "Missing client configuration.")) - } - } - - /// Create a new client pool. - pub fn build_pool(&self, size: usize) -> Result { - if let Some(config) = self.config.as_ref() { - RedisPool::new( - config.clone(), - Some(self.performance.clone()), - Some(self.connection.clone()), - self.policy.clone(), - size, - ) - } else { - Err(RedisError::new(RedisErrorKind::Config, "Missing client configuration.")) - } - } - - /// Create a new exclusive client pool. - #[cfg(not(feature = "glommio"))] - pub fn build_exclusive_pool(&self, size: usize) -> Result { - if let Some(config) = self.config.as_ref() { - ExclusivePool::new( - config.clone(), - Some(self.performance.clone()), - Some(self.connection.clone()), - self.policy.clone(), - size, - ) - } else { - Err(RedisError::new(RedisErrorKind::Config, "Missing client configuration.")) - } - } - - /// Create a new subscriber client. - #[cfg(feature = "subscriber-client")] - #[cfg_attr(docsrs, doc(cfg(feature = "subscriber-client")))] - pub fn build_subscriber_client(&self) -> Result { - if let Some(config) = self.config.as_ref() { - Ok(SubscriberClient::new( - config.clone(), - Some(self.performance.clone()), - Some(self.connection.clone()), - self.policy.clone(), - )) - } else { - Err(RedisError::new(RedisErrorKind::Config, "Missing client configuration.")) - } - } - - /// Create a new sentinel client. - /// - /// This is only necessary if callers need to communicate directly with sentinel nodes. Use a - /// `ServerConfig::Sentinel` to interact with Redis servers behind a sentinel layer. - #[cfg(feature = "sentinel-client")] - #[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))] - pub fn build_sentinel_client(&self) -> Result { - if let Some(config) = self.sentinel.as_ref() { - Ok(SentinelClient::new( - config.clone(), - Some(self.performance.clone()), - Some(self.connection.clone()), - self.policy.clone(), - )) - } else { - Err(RedisError::new( - RedisErrorKind::Config, - "Missing sentinel client configuration.", - )) - } - } -} diff --git a/src/types/client.rs b/src/types/client.rs deleted file mode 100644 index 578de695..00000000 --- a/src/types/client.rs +++ /dev/null @@ -1,200 +0,0 @@ -use crate::utils; -use bytes_utils::Str; - -#[cfg(feature = "i-tracking")] -use crate::{ - error::{RedisError, RedisErrorKind}, - types::{Message, RedisKey, RedisValue, Server}, -}; - -/// The type of clients to close. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClientKillType { - Normal, - Master, - Replica, - Pubsub, -} - -impl ClientKillType { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - ClientKillType::Normal => "normal", - ClientKillType::Master => "master", - ClientKillType::Replica => "replica", - ClientKillType::Pubsub => "pubsub", - }) - } -} - -/// Filters provided to the CLIENT KILL command. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClientKillFilter { - ID(String), - Type(ClientKillType), - User(String), - Addr(String), - LAddr(String), - SkipMe(bool), -} - -impl ClientKillFilter { - pub(crate) fn to_str(&self) -> (Str, Str) { - let (prefix, value) = match *self { - ClientKillFilter::ID(ref id) => ("ID", id.into()), - ClientKillFilter::Type(ref kind) => ("TYPE", kind.to_str()), - ClientKillFilter::User(ref user) => ("USER", user.into()), - ClientKillFilter::Addr(ref addr) => ("ADDR", addr.into()), - ClientKillFilter::LAddr(ref addr) => ("LADDR", addr.into()), - ClientKillFilter::SkipMe(ref b) => ("SKIPME", match *b { - true => utils::static_str("yes"), - false => utils::static_str("no"), - }), - }; - - (utils::static_str(prefix), value) - } -} - -/// Filters for the CLIENT PAUSE command. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClientPauseKind { - Write, - All, -} - -impl ClientPauseKind { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - ClientPauseKind::Write => "WRITE", - ClientPauseKind::All => "ALL", - }) - } -} - -/// Arguments for the CLIENT REPLY command. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClientReplyFlag { - On, - Off, - Skip, -} - -impl ClientReplyFlag { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - ClientReplyFlag::On => "ON", - ClientReplyFlag::Off => "OFF", - ClientReplyFlag::Skip => "SKIP", - }) - } -} - -/// An `ON|OFF` flag used with client tracking commands. -#[cfg(feature = "i-tracking")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Toggle { - On, - Off, -} - -#[cfg(feature = "i-tracking")] -impl Toggle { - pub(crate) fn to_str(&self) -> &'static str { - match self { - Toggle::On => "ON", - Toggle::Off => "OFF", - } - } - - pub(crate) fn from_str(s: &str) -> Option { - Some(match s { - "ON" | "on" => Toggle::On, - "OFF" | "off" => Toggle::Off, - _ => return None, - }) - } -} - -#[cfg(feature = "i-tracking")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] -impl TryFrom<&str> for Toggle { - type Error = RedisError; - - fn try_from(value: &str) -> Result { - Toggle::from_str(value).ok_or(RedisError::new(RedisErrorKind::Parse, "Invalid toggle value.")) - } -} - -#[cfg(feature = "i-tracking")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] -impl TryFrom for Toggle { - type Error = RedisError; - - fn try_from(value: String) -> Result { - Toggle::from_str(&value).ok_or(RedisError::new(RedisErrorKind::Parse, "Invalid toggle value.")) - } -} - -#[cfg(feature = "i-tracking")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] -impl TryFrom<&String> for Toggle { - type Error = RedisError; - - fn try_from(value: &String) -> Result { - Toggle::from_str(value).ok_or(RedisError::new(RedisErrorKind::Parse, "Invalid toggle value.")) - } -} - -#[cfg(feature = "i-tracking")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] -impl From for Toggle { - fn from(value: bool) -> Self { - if value { - Toggle::On - } else { - Toggle::Off - } - } -} - -/// A [client tracking](https://redis.io/docs/manual/client-side-caching/) invalidation message from the provided server. -#[cfg(feature = "i-tracking")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Invalidation { - pub keys: Vec, - pub server: Server, -} - -#[cfg(feature = "i-tracking")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] -impl Invalidation { - pub(crate) fn from_message(message: Message, server: &Server) -> Option { - Some(Invalidation { - keys: match message.value { - RedisValue::Array(values) => values.into_iter().filter_map(|v| v.try_into().ok()).collect(), - RedisValue::String(s) => vec![s.into()], - RedisValue::Bytes(b) => vec![b.into()], - RedisValue::Double(f) => vec![f.into()], - RedisValue::Integer(i) => vec![i.into()], - RedisValue::Boolean(b) => vec![b.into()], - RedisValue::Null => vec![], - _ => { - trace!("Dropping invalid invalidation message."); - return None; - }, - }, - server: server.clone(), - }) - } -} diff --git a/src/types/cluster.rs b/src/types/cluster.rs deleted file mode 100644 index d21f2aad..00000000 --- a/src/types/cluster.rs +++ /dev/null @@ -1,153 +0,0 @@ -pub use crate::protocol::types::{ClusterRouting, SlotRange}; -use crate::{ - error::{RedisError, RedisErrorKind}, - types::RedisValue, - utils, -}; -use bytes_utils::Str; - -macro_rules! parse_or_zero( - ($data:ident, $t:ty) => { - $data.parse::<$t>().ok().unwrap_or(0) - } -); - -fn parse_cluster_info_line(info: &mut ClusterInfo, line: &str) -> Result<(), RedisError> { - let parts: Vec<&str> = line.split(':').collect(); - if parts.len() != 2 { - return Err(RedisError::new(RedisErrorKind::Protocol, "Expected key:value pair.")); - } - let (field, val) = (parts[0], parts[1]); - - match field { - "cluster_state" => match val { - "ok" => info.cluster_state = ClusterState::Ok, - "fail" => info.cluster_state = ClusterState::Fail, - _ => return Err(RedisError::new(RedisErrorKind::Protocol, "Invalid cluster state.")), - }, - "cluster_slots_assigned" => info.cluster_slots_assigned = parse_or_zero!(val, u16), - "cluster_slots_ok" => info.cluster_slots_ok = parse_or_zero!(val, u16), - "cluster_slots_pfail" => info.cluster_slots_pfail = parse_or_zero!(val, u16), - "cluster_slots_fail" => info.cluster_slots_fail = parse_or_zero!(val, u16), - "cluster_known_nodes" => info.cluster_known_nodes = parse_or_zero!(val, u16), - "cluster_size" => info.cluster_size = parse_or_zero!(val, u32), - "cluster_current_epoch" => info.cluster_current_epoch = parse_or_zero!(val, u64), - "cluster_my_epoch" => info.cluster_my_epoch = parse_or_zero!(val, u64), - "cluster_stats_messages_sent" => info.cluster_stats_messages_sent = parse_or_zero!(val, u64), - "cluster_stats_messages_received" => info.cluster_stats_messages_received = parse_or_zero!(val, u64), - _ => { - warn!("Invalid cluster info field: {}", line); - }, - }; - - Ok(()) -} - -/// The state of the cluster from the `CLUSTER INFO` command. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClusterState { - Ok, - Fail, -} - -impl Default for ClusterState { - fn default() -> Self { - ClusterState::Ok - } -} - -/// A parsed response from the `CLUSTER INFO` command. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq, Default)] -pub struct ClusterInfo { - pub cluster_state: ClusterState, - pub cluster_slots_assigned: u16, - pub cluster_slots_ok: u16, - pub cluster_slots_pfail: u16, - pub cluster_slots_fail: u16, - pub cluster_known_nodes: u16, - pub cluster_size: u32, - pub cluster_current_epoch: u64, - pub cluster_my_epoch: u64, - pub cluster_stats_messages_sent: u64, - pub cluster_stats_messages_received: u64, -} - -impl TryFrom for ClusterInfo { - type Error = RedisError; - - fn try_from(value: RedisValue) -> Result { - if let Some(data) = value.as_bytes_str() { - let mut out = ClusterInfo::default(); - - for line in data.lines() { - let trimmed = line.trim(); - if !trimmed.is_empty() { - parse_cluster_info_line(&mut out, trimmed)?; - } - } - Ok(out) - } else { - Err(RedisError::new(RedisErrorKind::Protocol, "Expected string response.")) - } - } -} - -/// Options for the CLUSTER FAILOVER command. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClusterFailoverFlag { - Force, - Takeover, -} - -impl ClusterFailoverFlag { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - ClusterFailoverFlag::Force => "FORCE", - ClusterFailoverFlag::Takeover => "TAKEOVER", - }) - } -} - -/// Flags for the CLUSTER RESET command. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClusterResetFlag { - Hard, - Soft, -} - -impl ClusterResetFlag { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - ClusterResetFlag::Hard => "HARD", - ClusterResetFlag::Soft => "SOFT", - }) - } -} - -/// Flags for the CLUSTER SETSLOT command. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClusterSetSlotState { - Importing, - Migrating, - Stable, - Node(String), -} - -impl ClusterSetSlotState { - pub(crate) fn to_str(&self) -> (Str, Option) { - let (prefix, value) = match *self { - ClusterSetSlotState::Importing => ("IMPORTING", None), - ClusterSetSlotState::Migrating => ("MIGRATING", None), - ClusterSetSlotState::Stable => ("STABLE", None), - ClusterSetSlotState::Node(ref n) => ("NODE", Some(n.into())), - }; - - (utils::static_str(prefix), value) - } -} diff --git a/src/types/config.rs b/src/types/config.rs deleted file mode 100644 index 0425a260..00000000 --- a/src/types/config.rs +++ /dev/null @@ -1,1757 +0,0 @@ -pub use crate::protocol::types::Server; -use crate::{error::RedisError, protocol::command::RedisCommand, types::RespVersion, utils}; -use socket2::TcpKeepalive; -use std::{cmp, time::Duration}; -use url::Url; - -use crate::error::RedisErrorKind; -#[cfg(feature = "mocks")] -use crate::mocks::Mocks; -#[cfg(feature = "unix-sockets")] -use std::path::PathBuf; -#[cfg(feature = "mocks")] -use std::sync::Arc; - -#[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" -))] -#[cfg_attr( - docsrs, - doc(cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" - ))) -)] -pub use crate::protocol::tls::{HostMapping, TlsConfig, TlsConnector, TlsHostMapping}; - -#[cfg(feature = "replicas")] -#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] -pub use crate::router::replicas::{ReplicaConfig, ReplicaFilter}; -use crate::types::ClusterHash; - -/// The default amount of jitter when waiting to reconnect. -pub const DEFAULT_JITTER_MS: u32 = 100; - -/// Special errors that can trigger reconnection logic, which can also retry the failing command if possible. -/// -/// `MOVED`, `ASK`, and `NOAUTH` errors are handled separately by the client. -#[derive(Clone, Debug, Eq, PartialEq)] -#[cfg(feature = "custom-reconnect-errors")] -#[cfg_attr(docsrs, doc(cfg(feature = "custom-reconnect-errors")))] -pub enum ReconnectError { - /// The CLUSTERDOWN prefix. - ClusterDown, - /// The LOADING prefix. - Loading, - /// The MASTERDOWN prefix. - MasterDown, - /// The READONLY prefix, which can happen if a primary node is switched to a replica without any connection - /// interruption. - ReadOnly, - /// The MISCONF prefix. - Misconf, - /// The BUSY prefix. - Busy, - /// The NOREPLICAS prefix. - NoReplicas, - /// A case-sensitive prefix on an error message. - /// - /// See [the source](https://github.com/redis/redis/blob/fe37e4fc874a92dcf61b3b0de899ec6f674d2442/src/server.c#L1845) for examples. - Custom(&'static str), -} - -#[cfg(feature = "custom-reconnect-errors")] -impl ReconnectError { - pub(crate) fn to_str(&self) -> &'static str { - use ReconnectError::*; - - match self { - ClusterDown => "CLUSTERDOWN", - Loading => "LOADING", - MasterDown => "MASTERDOWN", - ReadOnly => "READONLY", - Misconf => "MISCONF", - Busy => "BUSY", - NoReplicas => "NOREPLICAS", - Custom(prefix) => prefix, - } - } -} - -/// The type of reconnection policy to use. This will apply to every connection used by the client. -/// -/// Use a `max_attempts` value of `0` to retry forever. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ReconnectPolicy { - /// Wait a constant amount of time between reconnect attempts, in ms. - Constant { - attempts: u32, - max_attempts: u32, - delay: u32, - jitter: u32, - }, - /// Backoff reconnection attempts linearly, adding `delay` each time. - Linear { - attempts: u32, - max_attempts: u32, - max_delay: u32, - delay: u32, - jitter: u32, - }, - /// Backoff reconnection attempts exponentially, multiplying the last delay by `mult` each time. - Exponential { - attempts: u32, - max_attempts: u32, - min_delay: u32, - max_delay: u32, - mult: u32, - jitter: u32, - }, -} - -impl Default for ReconnectPolicy { - fn default() -> Self { - ReconnectPolicy::Constant { - attempts: 0, - max_attempts: 0, - delay: 1000, - jitter: DEFAULT_JITTER_MS, - } - } -} - -impl ReconnectPolicy { - /// Create a new reconnect policy with a constant backoff. - pub fn new_constant(max_attempts: u32, delay: u32) -> ReconnectPolicy { - ReconnectPolicy::Constant { - max_attempts, - delay, - attempts: 0, - jitter: DEFAULT_JITTER_MS, - } - } - - /// Create a new reconnect policy with a linear backoff. - pub fn new_linear(max_attempts: u32, max_delay: u32, delay: u32) -> ReconnectPolicy { - ReconnectPolicy::Linear { - max_attempts, - max_delay, - delay, - attempts: 0, - jitter: DEFAULT_JITTER_MS, - } - } - - /// Create a new reconnect policy with an exponential backoff. - pub fn new_exponential(max_attempts: u32, min_delay: u32, max_delay: u32, mult: u32) -> ReconnectPolicy { - ReconnectPolicy::Exponential { - max_delay, - max_attempts, - min_delay, - mult, - attempts: 0, - jitter: DEFAULT_JITTER_MS, - } - } - - /// Set the amount of jitter to add to each reconnect delay. - /// - /// Default: 50 ms - pub fn set_jitter(&mut self, jitter_ms: u32) { - match self { - ReconnectPolicy::Constant { ref mut jitter, .. } => { - *jitter = jitter_ms; - }, - ReconnectPolicy::Linear { ref mut jitter, .. } => { - *jitter = jitter_ms; - }, - ReconnectPolicy::Exponential { ref mut jitter, .. } => { - *jitter = jitter_ms; - }, - } - } - - /// Reset the number of reconnection attempts. - pub(crate) fn reset_attempts(&mut self) { - match *self { - ReconnectPolicy::Constant { ref mut attempts, .. } => { - *attempts = 0; - }, - ReconnectPolicy::Linear { ref mut attempts, .. } => { - *attempts = 0; - }, - ReconnectPolicy::Exponential { ref mut attempts, .. } => { - *attempts = 0; - }, - } - } - - /// Read the number of reconnection attempts. - pub fn attempts(&self) -> u32 { - match *self { - ReconnectPolicy::Constant { ref attempts, .. } => *attempts, - ReconnectPolicy::Linear { ref attempts, .. } => *attempts, - ReconnectPolicy::Exponential { ref attempts, .. } => *attempts, - } - } - - /// Whether the client should initiate a reconnect. - pub(crate) fn should_reconnect(&self) -> bool { - match *self { - ReconnectPolicy::Constant { - ref attempts, - ref max_attempts, - .. - } => *max_attempts == 0 || *attempts < *max_attempts, - ReconnectPolicy::Linear { - ref attempts, - ref max_attempts, - .. - } => *max_attempts == 0 || *attempts < *max_attempts, - ReconnectPolicy::Exponential { - ref attempts, - ref max_attempts, - .. - } => *max_attempts == 0 || *attempts < *max_attempts, - } - } - - /// Calculate the next delay, incrementing `attempts` in the process. - pub fn next_delay(&mut self) -> Option { - match *self { - ReconnectPolicy::Constant { - ref mut attempts, - delay, - max_attempts, - jitter, - } => { - *attempts = match utils::incr_with_max(*attempts, max_attempts) { - Some(a) => a, - None => return None, - }; - - Some(utils::add_jitter(delay as u64, jitter)) - }, - ReconnectPolicy::Linear { - ref mut attempts, - max_delay, - max_attempts, - delay, - jitter, - } => { - *attempts = match utils::incr_with_max(*attempts, max_attempts) { - Some(a) => a, - None => return None, - }; - let delay = (delay as u64).saturating_mul(*attempts as u64); - - Some(cmp::min(max_delay as u64, utils::add_jitter(delay, jitter))) - }, - ReconnectPolicy::Exponential { - ref mut attempts, - min_delay, - max_delay, - max_attempts, - mult, - jitter, - } => { - *attempts = match utils::incr_with_max(*attempts, max_attempts) { - Some(a) => a, - None => return None, - }; - let delay = (mult as u64) - .saturating_pow(*attempts - 1) - .saturating_mul(min_delay as u64); - - Some(cmp::min(max_delay as u64, utils::add_jitter(delay, jitter))) - }, - } - } -} - -/// Describes how the client should respond when a command is sent while the client is in a blocked state from a -/// blocking command. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Blocking { - /// Wait to send the command until the blocked command finishes. (Default) - Block, - /// Return an error to the caller. - Error, - /// Interrupt the blocked command by automatically sending `CLIENT UNBLOCK` for the blocked connection. - Interrupt, -} - -impl Default for Blocking { - fn default() -> Self { - Blocking::Block - } -} - -/// Backpressure policies to apply when the max number of in-flight commands is reached on a connection. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum BackpressurePolicy { - /// Sleep for some amount of time before sending the next command. - Sleep { - /// Disable the backpressure scaling logic used to calculate the `sleep` duration when throttling commands. - /// - /// If `true` the client will always wait a constant amount of time defined by `min_sleep_duration_ms` when - /// throttling commands. Otherwise the sleep duration will scale based on the number of in-flight commands. - /// - /// Default: `false` - disable_backpressure_scaling: bool, - /// The minimum amount of time to wait when applying backpressure to a command. - /// - /// If `0` then no backpressure will be applied, but backpressure errors will not be surfaced to callers unless - /// `disable_auto_backpressure` is `true`. - /// - /// Default: 10 ms - min_sleep_duration: Duration, - }, - /// Wait for all in-flight commands to finish before sending the next command. - Drain, -} - -impl Default for BackpressurePolicy { - fn default() -> Self { - BackpressurePolicy::Drain - } -} - -impl BackpressurePolicy { - /// Create a new `Sleep` policy with the legacy default values. - pub fn default_sleep() -> Self { - BackpressurePolicy::Sleep { - disable_backpressure_scaling: false, - min_sleep_duration: Duration::from_millis(10), - } - } -} - -/// Configuration options for backpressure features in the client. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct BackpressureConfig { - /// Whether to disable the automatic backpressure features when pipelining is enabled. - /// - /// If `true` then `RedisErrorKind::Backpressure` errors may be surfaced to callers. Callers can set this to `true` - /// and `max_in_flight_commands` to `0` to effectively disable the backpressure logic. - /// - /// Default: `false` - pub disable_auto_backpressure: bool, - /// The maximum number of in-flight commands (per connection) before backpressure will be applied. - /// - /// Default: 10_000 - pub max_in_flight_commands: u64, - /// The backpressure policy to apply when the max number of in-flight commands is reached. - /// - /// Default: [Drain](crate::types::BackpressurePolicy::Drain). - pub policy: BackpressurePolicy, -} - -impl Default for BackpressureConfig { - fn default() -> Self { - BackpressureConfig { - disable_auto_backpressure: false, - max_in_flight_commands: 10_000, - policy: BackpressurePolicy::default(), - } - } -} - -/// TCP configuration options. -#[derive(Clone, Debug, Default)] -pub struct TcpConfig { - /// Set the [TCP_NODELAY](https://docs.rs/tokio/latest/tokio/net/struct.TcpStream.html#method.set_nodelay) value. - pub nodelay: Option, - /// Set the [SO_LINGER](https://docs.rs/tokio/latest/tokio/net/struct.TcpStream.html#method.set_linger) value. - pub linger: Option, - /// Set the [IP_TTL](https://docs.rs/tokio/latest/tokio/net/struct.TcpStream.html#method.set_ttl) value. - pub ttl: Option, - /// Set the [TCP keepalive values](https://docs.rs/socket2/latest/socket2/struct.Socket.html#method.set_tcp_keepalive). - pub keepalive: Option, -} - -impl PartialEq for TcpConfig { - fn eq(&self, other: &Self) -> bool { - self.nodelay == other.nodelay && self.linger == other.linger && self.ttl == other.ttl - } -} - -impl Eq for TcpConfig {} - -/// Configuration options used to detect potentially unresponsive connections. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct UnresponsiveConfig { - /// If provided, the amount of time a frame can wait without a response before the associated connection is - /// considered unresponsive. - /// - /// If a connection is considered unresponsive it will be forcefully closed and the client will reconnect based on - /// the [ReconnectPolicy](crate::types::ReconnectPolicy). This heuristic can be useful in environments where - /// connections may close or change in subtle or unexpected ways. - /// - /// Unlike the [timeout](crate::types::Options) and [default_command_timeout](crate::types::PerformanceConfig) - /// interfaces, any in-flight commands waiting on a response when the connection is closed this way will be - /// retried based on the associated [ReconnectPolicy](crate::types::ReconnectPolicy) and - /// [Options](crate::types::Options). - /// - /// Default: `None` - pub max_timeout: Option, - /// The frequency at which the client checks for unresponsive connections. - /// - /// This value should usually be less than half of `max_timeout` and always more than 1 ms. - /// - /// Default: 2 sec - pub interval: Duration, -} - -impl Default for UnresponsiveConfig { - fn default() -> Self { - UnresponsiveConfig { - max_timeout: None, - interval: Duration::from_secs(2), - } - } -} - -/// A policy that determines how clustered clients initially connect to and discover other cluster nodes. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClusterDiscoveryPolicy { - /// Always use the endpoint(s) provided in the client's [ServerConfig](ServerConfig). - /// - /// This is generally recommended with managed services, Kubernetes, or other systems that provide client routing - /// or cluster discovery interfaces. - /// - /// Default. - ConfigEndpoint, - /// Try connecting to nodes specified in both the client's [ServerConfig](ServerConfig) and the most recently - /// cached routing table. - UseCache, -} - -impl Default for ClusterDiscoveryPolicy { - fn default() -> Self { - ClusterDiscoveryPolicy::ConfigEndpoint - } -} - -/// Configuration options related to the creation or management of TCP connection. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ConnectionConfig { - /// The timeout to apply when attempting to create a new TCP connection. - /// - /// This also includes the TLS handshake if using any of the TLS features. - /// - /// Default: 10 sec - pub connection_timeout: Duration, - /// The timeout to apply when sending internal commands such as `AUTH`, `SELECT`, `CLUSTER SLOTS`, `READONLY`, etc. - /// - /// Default: 10 sec - pub internal_command_timeout: Duration, - /// The amount of time to wait after a `MOVED` error is received before the client will update the cached cluster - /// state. - /// - /// Default: `0` - pub cluster_cache_update_delay: Duration, - /// The maximum number of times the client will attempt to send a command. - /// - /// This value be incremented whenever the connection closes while the command is in-flight. - /// - /// Default: `3` - pub max_command_attempts: u32, - /// The maximum number of times the client will attempt to follow a `MOVED` or `ASK` redirection per command. - /// - /// Default: `5` - pub max_redirections: u32, - /// Unresponsive connection configuration options. - pub unresponsive: UnresponsiveConfig, - /// An unexpected `NOAUTH` error is treated the same as a general connection failure, causing the client to - /// reconnect based on the [ReconnectPolicy](crate::types::ReconnectPolicy). This is [recommended](https://github.com/StackExchange/StackExchange.Redis/issues/1273#issuecomment-651823824) if callers are using ElastiCache. - /// - /// Default: `false` - pub reconnect_on_auth_error: bool, - /// Automatically send `CLIENT SETNAME` on each connection associated with a client instance. - /// - /// Default: `false` - pub auto_client_setname: bool, - /// Limit the size of the internal in-memory command queue. - /// - /// Commands that exceed this limit will receive a `RedisErrorKind::Backpressure` error. - /// - /// See [command_queue_len](crate::interfaces::MetricsInterface::command_queue_len) for more information. - /// - /// Default: `0` (unlimited) - pub max_command_buffer_len: usize, - /// Disable the `CLUSTER INFO` health check when initializing cluster connections. - /// - /// Default: `false` - pub disable_cluster_health_check: bool, - /// Configuration options for replica nodes. - /// - /// Default: `None` - #[cfg(feature = "replicas")] - #[cfg_attr(docsrs, doc(cfg(feature = "replicas")))] - pub replica: ReplicaConfig, - /// TCP connection options. - pub tcp: TcpConfig, - /// Errors that should trigger reconnection logic. - #[cfg(feature = "custom-reconnect-errors")] - #[cfg_attr(docsrs, doc(cfg(feature = "custom-reconnect-errors")))] - pub reconnect_errors: Vec, - - /// The task queue onto which routing tasks will be spawned. - #[cfg(feature = "glommio")] - #[cfg_attr(docsrs, doc(cfg(feature = "glommio")))] - pub router_task_queue: Option, - - /// The task queue onto which connection reader tasks will be spawned. - #[cfg(feature = "glommio")] - #[cfg_attr(docsrs, doc(cfg(feature = "glommio")))] - pub connection_task_queue: Option, -} - -impl Default for ConnectionConfig { - fn default() -> Self { - ConnectionConfig { - connection_timeout: Duration::from_millis(10_000), - internal_command_timeout: Duration::from_millis(10_000), - max_redirections: 5, - max_command_attempts: 3, - max_command_buffer_len: 0, - auto_client_setname: false, - cluster_cache_update_delay: Duration::from_millis(0), - reconnect_on_auth_error: false, - disable_cluster_health_check: false, - tcp: TcpConfig::default(), - unresponsive: UnresponsiveConfig::default(), - #[cfg(feature = "replicas")] - replica: ReplicaConfig::default(), - #[cfg(feature = "custom-reconnect-errors")] - reconnect_errors: vec![ - ReconnectError::ClusterDown, - ReconnectError::Loading, - ReconnectError::ReadOnly, - ], - #[cfg(feature = "glommio")] - router_task_queue: None, - #[cfg(feature = "glommio")] - connection_task_queue: None, - } - } -} - -/// Configuration options that can affect the performance of the client. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct PerformanceConfig { - /// Whether the client should automatically pipeline commands across tasks when possible. - /// - /// The [Pipeline](crate::clients::Pipeline) interface can be used to pipeline commands within one task, - /// whereas this flag can automatically pipeline commands across tasks. - /// - /// Default: `true` - pub auto_pipeline: bool, - /// Configuration options for backpressure features in the client. - pub backpressure: BackpressureConfig, - /// An optional timeout to apply to all commands. - /// - /// If `0` this will disable any timeout being applied to commands. Callers can also set timeouts on individual - /// commands via the [with_options](crate::interfaces::ClientLike::with_options) interface. - /// - /// Default: `0` - pub default_command_timeout: Duration, - /// The maximum number of frames that will be fed to a socket before flushing. - /// - /// Note: in some circumstances the client with always flush the socket (`QUIT`, `EXEC`, etc). - /// - /// Default: 200 - pub max_feed_count: u64, - /// The default capacity used when creating [broadcast channels](https://docs.rs/tokio/latest/tokio/sync/broadcast/fn.channel.html) in the [EventInterface](crate::interfaces::EventInterface). - /// - /// Default: 32 - pub broadcast_channel_capacity: usize, - /// The minimum size, in bytes, of frames that should be encoded or decoded with a blocking task. - /// - /// See [block_in_place](https://docs.rs/tokio/latest/tokio/task/fn.block_in_place.html) for more information. - /// - /// Default: 50_000_000 - #[cfg(feature = "blocking-encoding")] - #[cfg_attr(docsrs, doc(cfg(feature = "blocking-encoding")))] - pub blocking_encode_threshold: usize, -} - -impl Default for PerformanceConfig { - fn default() -> Self { - PerformanceConfig { - auto_pipeline: true, - backpressure: BackpressureConfig::default(), - default_command_timeout: Duration::from_millis(0), - max_feed_count: 200, - broadcast_channel_capacity: 32, - #[cfg(feature = "blocking-encoding")] - blocking_encode_threshold: 50_000_000, - } - } -} - -/// Configuration options for a `RedisClient`. -#[derive(Clone, Debug)] -pub struct RedisConfig { - /// Whether the client should return an error if it cannot connect to the server the first time when being - /// initialized. If `false` the client will run the reconnect logic if it cannot connect to the server the first - /// time, but if `true` the client will return initial connection errors to the caller immediately. - /// - /// Normally the reconnection logic only applies to connections that close unexpectedly, but this flag can apply - /// the same logic to the first connection as it is being created. - /// - /// Callers should use caution setting this to `false` since it can make debugging configuration issues more - /// difficult. - /// - /// Default: `true` - pub fail_fast: bool, - /// The default behavior of the client when a command is sent while the connection is blocked on a blocking - /// command. - /// - /// Setting this to anything other than `Blocking::Block` incurs a small performance penalty. - /// - /// Default: `Blocking::Block` - pub blocking: Blocking, - /// An optional ACL username for the client to use when authenticating. If ACL rules are not configured this should - /// be `None`. - /// - /// Default: `None` - pub username: Option, - /// An optional password for the client to use when authenticating. - /// - /// Default: `None` - pub password: Option, - /// Connection configuration for the server(s). - /// - /// Default: `Centralized(localhost, 6379)` - pub server: ServerConfig, - /// The protocol version to use when communicating with the server(s). - /// - /// If RESP3 is specified the client will automatically use `HELLO` when authenticating. **This requires Redis - /// >=6.0.0.** If the `HELLO` command fails this will prevent the client from connecting. Callers should set this - /// to RESP2 and use `HELLO` manually to fall back to RESP2 if needed. - /// - /// Note: upgrading an existing codebase from RESP2 to RESP3 may require changing certain type signatures. RESP3 - /// has a slightly different type system than RESP2. - /// - /// Default: `RESP2` - pub version: RespVersion, - /// An optional database number that the client will automatically `SELECT` after connecting or reconnecting. - /// - /// It is recommended that callers use this field instead of putting a `select()` call inside the `on_reconnect` - /// block, if possible. Commands that were in-flight when the connection closed will retry before anything inside - /// the `on_reconnect` block. - /// - /// Default: `None` - pub database: Option, - /// TLS configuration options. - /// - /// Default: `None` - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - #[cfg_attr( - docsrs, - doc(cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))) - )] - pub tls: Option, - /// Tracing configuration options. - #[cfg(feature = "partial-tracing")] - #[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))] - pub tracing: TracingConfig, - /// An optional [mocking layer](crate::mocks) to intercept and process commands. - /// - /// Default: `None` - #[cfg(feature = "mocks")] - #[cfg_attr(docsrs, doc(cfg(feature = "mocks")))] - pub mocks: Option>, -} - -impl PartialEq for RedisConfig { - fn eq(&self, other: &Self) -> bool { - self.server == other.server - && self.database == other.database - && self.fail_fast == other.fail_fast - && self.version == other.version - && self.username == other.username - && self.password == other.password - && self.blocking == other.blocking - } -} - -impl Eq for RedisConfig {} - -impl Default for RedisConfig { - fn default() -> Self { - RedisConfig { - fail_fast: true, - blocking: Blocking::default(), - username: None, - password: None, - server: ServerConfig::default(), - version: RespVersion::RESP2, - database: None, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls: None, - #[cfg(feature = "partial-tracing")] - tracing: TracingConfig::default(), - #[cfg(feature = "mocks")] - mocks: None, - } - } -} - -#[cfg_attr(docsrs, allow(rustdoc::broken_intra_doc_links))] -impl RedisConfig { - /// Whether the client uses TLS. - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - pub fn uses_tls(&self) -> bool { - self.tls.is_some() - } - - /// Whether the client uses TLS. - #[cfg(not(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - )))] - pub fn uses_tls(&self) -> bool { - false - } - - /// Whether the client uses a `native-tls` connector. - #[cfg(feature = "enable-native-tls")] - pub fn uses_native_tls(&self) -> bool { - self - .tls - .as_ref() - .map(|config| matches!(config.connector, TlsConnector::Native(_))) - .unwrap_or(false) - } - - /// Whether the client uses a `native-tls` connector. - #[cfg(not(feature = "enable-native-tls"))] - pub fn uses_native_tls(&self) -> bool { - false - } - - /// Whether the client uses a `rustls` connector. - #[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] - pub fn uses_rustls(&self) -> bool { - self - .tls - .as_ref() - .map(|config| matches!(config.connector, TlsConnector::Rustls(_))) - .unwrap_or(false) - } - - /// Whether the client uses a `rustls` connector. - #[cfg(not(any(feature = "enable-rustls", feature = "enable-rustls-ring")))] - pub fn uses_rustls(&self) -> bool { - false - } - - /// Parse a URL string into a `RedisConfig`. - /// - /// # URL Syntax - /// - /// **Centralized** - /// - /// ```text - /// redis|rediss :// [[username:]password@] host [:port][/database] - /// ``` - /// - /// **Clustered** - /// - /// ```text - /// redis|rediss[-cluster] :// [[username:]password@] host [:port][?[node=host1:port1][&node=host2:port2][&node=hostN:portN]] - /// ``` - /// - /// **Sentinel** - /// - /// ```text - /// redis|rediss[-sentinel] :// [[username1:]password1@] host [:port][/database][?[node=host1:port1][&node=host2:port2][&node=hostN:portN] - /// [&sentinelServiceName=myservice][&sentinelUsername=username2][&sentinelPassword=password2]] - /// ``` - /// - /// **Unix Socket** - /// - /// ```text - /// redis+unix:// [[username:]password@] /path/to/redis.sock - /// ``` - /// - /// # Schemes - /// - /// This function will use the URL scheme to determine which server type the caller is using. Valid schemes include: - /// - /// * `redis` - TCP connected to a centralized server. - /// * `rediss` - TLS connected to a centralized server. - /// * `redis-cluster` - TCP connected to a cluster. - /// * `rediss-cluster` - TLS connected to a cluster. - /// * `redis-sentinel` - TCP connected to a centralized server behind a sentinel layer. - /// * `rediss-sentinel` - TLS connected to a centralized server behind a sentinel layer. - /// * `redis+unix` - Unix domain socket followed by a path. - /// - /// **The `rediss` scheme prefix requires one of the TLS feature flags.** - /// - /// # Query Parameters - /// - /// In some cases it's necessary to specify multiple node hostname/port tuples (with a cluster or sentinel layer for - /// example). The following query parameters may also be used in their respective contexts: - /// - /// * `node` - Specify another node in the topology. In a cluster this would refer to any other known cluster node. - /// In the context of a Redis sentinel layer this refers to a known **sentinel** node. Multiple `node` parameters - /// may be used in a URL. - /// * `sentinelServiceName` - Specify the name of the sentinel service. This is required when using the - /// `redis-sentinel` scheme. - /// * `sentinelUsername` - Specify the username to use when connecting to a **sentinel** node. This requires the - /// `sentinel-auth` feature and allows the caller to use different credentials for sentinel nodes vs the actual - /// Redis server. The `username` part of the URL immediately following the scheme will refer to the username used - /// when connecting to the backing Redis server. - /// * `sentinelPassword` - Specify the password to use when connecting to a **sentinel** node. This requires the - /// `sentinel-auth` feature and allows the caller to use different credentials for sentinel nodes vs the actual - /// Redis server. The `password` part of the URL immediately following the scheme will refer to the password used - /// when connecting to the backing Redis server. - /// - /// See the [from_url_centralized](Self::from_url_centralized), [from_url_clustered](Self::from_url_clustered), - /// [from_url_sentinel](Self::from_url_sentinel), and [from_url_unix](Self::from_url_unix) for more information. Or - /// see the [RedisConfig](Self) unit tests for examples. - pub fn from_url(url: &str) -> Result { - let parsed_url = Url::parse(url)?; - if utils::url_is_clustered(&parsed_url) { - RedisConfig::from_url_clustered(url) - } else if utils::url_is_sentinel(&parsed_url) { - RedisConfig::from_url_sentinel(url) - } else if utils::url_is_unix_socket(&parsed_url) { - #[cfg(feature = "unix-sockets")] - return RedisConfig::from_url_unix(url); - #[allow(unreachable_code)] - Err(RedisError::new(RedisErrorKind::Config, "Missing unix-socket feature.")) - } else { - RedisConfig::from_url_centralized(url) - } - } - - /// Create a centralized `RedisConfig` struct from a URL. - /// - /// ```text - /// redis://username:password@foo.com:6379/1 - /// rediss://username:password@foo.com:6379/1 - /// redis://foo.com:6379/1 - /// redis://foo.com - /// // ... etc - /// ``` - /// - /// This function is very similar to [from_url](Self::from_url), but it adds a layer of validation for configuration - /// parameters that are only relevant to a centralized server. - /// - /// For example: - /// - /// * A database can be defined in the `path` section. - /// * The `port` field is optional in this context. If it is not specified then `6379` will be used. - /// * Any `node` or sentinel query parameters will be ignored. - pub fn from_url_centralized(url: &str) -> Result { - let (url, host, port, _tls) = utils::parse_url(url, Some(6379))?; - let server = ServerConfig::new_centralized(host, port); - let database = utils::parse_url_db(&url)?; - let (username, password) = utils::parse_url_credentials(&url)?; - - Ok(RedisConfig { - server, - username, - password, - database, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls: utils::tls_config_from_url(_tls)?, - ..RedisConfig::default() - }) - } - - /// Create a clustered `RedisConfig` struct from a URL. - /// - /// ```text - /// redis-cluster://username:password@foo.com:30001?node=bar.com:30002&node=baz.com:30003 - /// rediss-cluster://username:password@foo.com:30001?node=bar.com:30002&node=baz.com:30003 - /// rediss://foo.com:30001?node=bar.com:30002&node=baz.com:30003 - /// redis://foo.com:30001 - /// // ... etc - /// ``` - /// - /// This function is very similar to [from_url](Self::from_url), but it adds a layer of validation for configuration - /// parameters that are only relevant to a clustered deployment. - /// - /// For example: - /// - /// * The `-cluster` suffix in the scheme is optional when using this function directly. - /// * Any database defined in the `path` section will be ignored. - /// * The `port` field is required in this context alongside any hostname. - /// * Any `node` query parameters will be used to find other known cluster nodes. - /// * Any sentinel query parameters will be ignored. - pub fn from_url_clustered(url: &str) -> Result { - let (url, host, port, _tls) = utils::parse_url(url, Some(6379))?; - let mut cluster_nodes = utils::parse_url_other_nodes(&url)?; - cluster_nodes.push(Server::new(host, port)); - let server = ServerConfig::Clustered { - hosts: cluster_nodes, - policy: ClusterDiscoveryPolicy::default(), - }; - let (username, password) = utils::parse_url_credentials(&url)?; - - Ok(RedisConfig { - server, - username, - password, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls: utils::tls_config_from_url(_tls)?, - ..RedisConfig::default() - }) - } - - /// Create a sentinel `RedisConfig` struct from a URL. - /// - /// ```text - /// redis-sentinel://username:password@foo.com:6379/1?sentinelServiceName=fakename&node=foo.com:30001&node=bar.com:30002 - /// rediss-sentinel://username:password@foo.com:6379/0?sentinelServiceName=fakename&node=foo.com:30001&node=bar.com:30002 - /// redis://foo.com:6379?sentinelServiceName=fakename - /// rediss://foo.com:6379/1?sentinelServiceName=fakename - /// // ... etc - /// ``` - /// - /// This function is very similar to [from_url](Self::from_url), but it adds a layer of validation for configuration - /// parameters that are only relevant to a sentinel deployment. - /// - /// For example: - /// - /// * The `-sentinel` suffix in the scheme is optional when using this function directly. - /// * A database can be defined in the `path` section. - /// * The `port` field is optional following the first hostname (`26379` will be used if undefined), but required - /// within any `node` query parameters. - /// * Any `node` query parameters will be used to find other known sentinel nodes. - /// * The `sentinelServiceName` query parameter is required. - /// * Depending on the cargo features used other sentinel query parameters may be used. - /// - /// This particular function is more complex than the others when the `sentinel-auth` feature is used. For example, - /// to declare a config that uses different credentials for the sentinel nodes vs the backing Redis servers: - /// - /// ```text - /// redis-sentinel://username1:password1@foo.com:26379/1?sentinelServiceName=fakename&sentinelUsername=username2&sentinelPassword=password2&node=bar.com:26379&node=baz.com:26380 - /// ``` - /// - /// The above example will use `("username1", "password1")` when authenticating to the backing Redis servers, and - /// `("username2", "password2")` when initially connecting to the sentinel nodes. Additionally, all 3 addresses - /// (`foo.com:26379`, `bar.com:26379`, `baz.com:26380`) specify known **sentinel** nodes. - pub fn from_url_sentinel(url: &str) -> Result { - let (url, host, port, _tls) = utils::parse_url(url, Some(26379))?; - let mut other_nodes = utils::parse_url_other_nodes(&url)?; - other_nodes.push(Server::new(host, port)); - let service_name = utils::parse_url_sentinel_service_name(&url)?; - let (username, password) = utils::parse_url_credentials(&url)?; - let database = utils::parse_url_db(&url)?; - let server = ServerConfig::Sentinel { - hosts: other_nodes, - service_name, - #[cfg(feature = "sentinel-auth")] - username: utils::parse_url_sentinel_username(&url), - #[cfg(feature = "sentinel-auth")] - password: utils::parse_url_sentinel_password(&url), - }; - - Ok(RedisConfig { - server, - username, - password, - database, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls: utils::tls_config_from_url(_tls)?, - ..RedisConfig::default() - }) - } - - /// Create a `RedisConfig` from a URL that connects via a Unix domain socket. - /// - /// ```text - /// redis+unix:///path/to/redis.sock - /// redis+unix://username:password@nonemptyhost/path/to/redis.sock - /// ``` - /// - /// **Important** - /// - /// * In the other URL parsing functions the path section indicates the database that the client should `SELECT` - /// after connecting. However, Unix sockets are also specified by a path rather than a hostname:port, which - /// creates some ambiguity in this case. Callers should manually set the database field on the returned - /// `RedisConfig` if needed. - /// * If credentials are provided the caller must also specify a hostname in order to pass to the [URL - /// validation](Url::parse) process. This function will ignore the value, but some non-empty string must be - /// provided. - #[cfg(feature = "unix-sockets")] - #[cfg_attr(docsrs, doc(cfg(feature = "unix-sockets")))] - pub fn from_url_unix(url: &str) -> Result { - let (url, path) = utils::parse_unix_url(url)?; - let (username, password) = utils::parse_url_credentials(&url)?; - - Ok(RedisConfig { - server: ServerConfig::Unix { path }, - username, - password, - ..Default::default() - }) - } -} - -/// Connection configuration for the Redis server. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ServerConfig { - Centralized { - /// The `Server` identifier. - server: Server, - }, - Clustered { - /// The known cluster node `Server` identifiers. - /// - /// Only one node in the cluster needs to be provided here, the rest will be discovered via the `CLUSTER SLOTS` - /// command. - hosts: Vec, - /// The cluster discovery policy to use when connecting or following redirections. - policy: ClusterDiscoveryPolicy, - }, - #[cfg(feature = "unix-sockets")] - #[cfg_attr(docsrs, doc(cfg(feature = "unix-sockets")))] - Unix { - /// The path to the Unix socket. - /// - /// Any associated [Server](crate::types::Server) identifiers will use this value as the `host`. - path: PathBuf, - }, - Sentinel { - /// An array of `Server` identifiers for each known sentinel instance. - hosts: Vec, - /// The service name for primary/main instances. - service_name: String, - - /// An optional ACL username for the client to use when authenticating. - #[cfg(feature = "sentinel-auth")] - #[cfg_attr(docsrs, doc(cfg(feature = "sentinel-auth")))] - username: Option, - /// An optional password for the client to use when authenticating. - #[cfg(feature = "sentinel-auth")] - #[cfg_attr(docsrs, doc(cfg(feature = "sentinel-auth")))] - password: Option, - }, -} - -impl Default for ServerConfig { - fn default() -> Self { - ServerConfig::default_centralized() - } -} - -impl ServerConfig { - /// Create a new centralized config with the provided host and port. - pub fn new_centralized(host: S, port: u16) -> ServerConfig - where - S: Into, - { - ServerConfig::Centralized { - server: Server::new(host.into(), port), - } - } - - /// Create a new clustered config with the provided set of hosts and ports. - /// - /// Only one valid host in the cluster needs to be provided here. The client will use `CLUSTER NODES` to discover - /// the other nodes. - pub fn new_clustered(mut hosts: Vec<(S, u16)>) -> ServerConfig - where - S: Into, - { - ServerConfig::Clustered { - hosts: hosts.drain(..).map(|(s, p)| Server::new(s.into(), p)).collect(), - policy: ClusterDiscoveryPolicy::default(), - } - } - - /// Create a new sentinel config with the provided set of hosts and the name of the service. - /// - /// This library will connect using the details from the [Redis documentation](https://redis.io/topics/sentinel-clients). - pub fn new_sentinel(hosts: Vec<(H, u16)>, service_name: N) -> ServerConfig - where - H: Into, - N: Into, - { - ServerConfig::Sentinel { - hosts: hosts.into_iter().map(|(h, p)| Server::new(h.into(), p)).collect(), - service_name: service_name.into(), - #[cfg(feature = "sentinel-auth")] - username: None, - #[cfg(feature = "sentinel-auth")] - password: None, - } - } - - /// Create a new server config for a connected Unix socket. - #[cfg(feature = "unix-sockets")] - #[cfg_attr(docsrs, doc(cfg(feature = "unix-sockets")))] - pub fn new_unix_socket

(path: P) -> ServerConfig - where - P: Into, - { - ServerConfig::Unix { path: path.into() } - } - - /// Create a centralized config with default settings for a local deployment. - pub fn default_centralized() -> ServerConfig { - ServerConfig::Centralized { - server: Server::new("127.0.0.1", 6379), - } - } - - /// Create a clustered config with the same defaults as specified in the `create-cluster` script provided by Redis. - pub fn default_clustered() -> ServerConfig { - ServerConfig::Clustered { - hosts: vec![ - Server::new("127.0.0.1", 30001), - Server::new("127.0.0.1", 30002), - Server::new("127.0.0.1", 30003), - ], - policy: ClusterDiscoveryPolicy::default(), - } - } - - /// Whether the config uses a clustered deployment. - pub fn is_clustered(&self) -> bool { - matches!(*self, ServerConfig::Clustered { .. }) - } - - /// Whether the config is for a centralized server behind a sentinel node(s). - pub fn is_sentinel(&self) -> bool { - matches!(*self, ServerConfig::Sentinel { .. }) - } - - /// Whether the config is for a centralized server. - pub fn is_centralized(&self) -> bool { - matches!(*self, ServerConfig::Centralized { .. }) - } - - /// Whether the config uses a Unix socket. - pub fn is_unix_socket(&self) -> bool { - match *self { - #[cfg(feature = "unix-sockets")] - ServerConfig::Unix { .. } => true, - _ => false, - } - } - - /// Read the server hosts or sentinel hosts if using the sentinel interface. - pub fn hosts(&self) -> Vec { - match *self { - ServerConfig::Centralized { ref server } => vec![server.clone()], - ServerConfig::Clustered { ref hosts, .. } => hosts.to_vec(), - ServerConfig::Sentinel { ref hosts, .. } => hosts.to_vec(), - #[cfg(feature = "unix-sockets")] - ServerConfig::Unix { ref path } => vec![Server::new(utils::path_to_string(path), 0)], - } - } - - /// Set the [ClusterDiscoveryPolicy], if possible. - pub fn set_cluster_discovery_policy(&mut self, new_policy: ClusterDiscoveryPolicy) -> Result<(), RedisError> { - if let ServerConfig::Clustered { ref mut policy, .. } = self { - *policy = new_policy; - Ok(()) - } else { - Err(RedisError::new(RedisErrorKind::Config, "Expected clustered config.")) - } - } -} - -/// Configuration options for tracing. -#[cfg(feature = "partial-tracing")] -#[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))] -#[derive(Clone, Debug)] -pub struct TracingConfig { - /// Whether to enable tracing for this client. - /// - /// Default: `false` - pub enabled: bool, - - /// Set the `tracing::Level` of spans under `partial-tracing` feature. - /// - /// Default: `INFO` - pub default_tracing_level: tracing::Level, - - /// Set the `tracing::Level` of spans under `full-tracing` feature. - /// - /// Default: `DEBUG` - #[cfg(feature = "full-tracing")] - #[cfg_attr(docsrs, doc(cfg(feature = "full-tracing")))] - pub full_tracing_level: tracing::Level, -} - -#[cfg(feature = "partial-tracing")] -#[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))] -impl TracingConfig { - pub fn new(enabled: bool) -> Self { - Self { - enabled, - ..Self::default() - } - } -} - -#[cfg(feature = "partial-tracing")] -#[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))] -impl Default for TracingConfig { - fn default() -> Self { - Self { - enabled: false, - default_tracing_level: tracing::Level::INFO, - #[cfg(feature = "full-tracing")] - full_tracing_level: tracing::Level::DEBUG, - } - } -} - -/// Configuration options for sentinel clients. -#[derive(Clone, Debug)] -#[cfg(feature = "sentinel-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))] -pub struct SentinelConfig { - /// The hostname for the sentinel node. - /// - /// Default: `127.0.0.1` - pub host: String, - /// The port on which the sentinel node is listening. - /// - /// Default: `26379` - pub port: u16, - /// An optional ACL username for the client to use when authenticating. If ACL rules are not configured this should - /// be `None`. - /// - /// Default: `None` - pub username: Option, - /// An optional password for the client to use when authenticating. - /// - /// Default: `None` - pub password: Option, - /// TLS configuration fields. If `None` the connection will not use TLS. - /// - /// See the `tls` examples on Github for more information. - /// - /// Default: `None` - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - #[cfg_attr( - docsrs, - doc(cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))) - )] - pub tls: Option, - /// Whether to enable tracing for this client. - /// - /// Default: `false` - #[cfg(feature = "partial-tracing")] - #[cfg_attr(docsrs, doc(cfg(feature = "partial-tracing")))] - pub tracing: TracingConfig, -} - -#[cfg(feature = "sentinel-client")] -impl Default for SentinelConfig { - fn default() -> Self { - SentinelConfig { - host: "127.0.0.1".into(), - port: 26379, - username: None, - password: None, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls: None, - #[cfg(feature = "partial-tracing")] - tracing: TracingConfig::default(), - } - } -} - -#[doc(hidden)] -#[cfg(feature = "sentinel-client")] -impl From for RedisConfig { - fn from(config: SentinelConfig) -> Self { - RedisConfig { - server: ServerConfig::Centralized { - server: Server::new(config.host, config.port), - }, - fail_fast: true, - database: None, - blocking: Blocking::Block, - username: config.username, - password: config.password, - version: RespVersion::RESP2, - #[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" - ))] - tls: config.tls, - #[cfg(feature = "partial-tracing")] - tracing: config.tracing, - #[cfg(feature = "mocks")] - mocks: None, - } - } -} - -/// Options to configure or overwrite for individual commands. -/// -/// Fields left as `None` will use the value from the corresponding client or global config option. -/// -/// ```rust -/// # use fred::prelude::*; -/// async fn example() -> Result<(), RedisError> { -/// let options = Options { -/// max_attempts: Some(10), -/// max_redirections: Some(2), -/// ..Default::default() -/// }; -/// -/// let client = RedisClient::default(); -/// client.init().await?; -/// let _: () = client.with_options(&options).get("foo").await?; -/// -/// Ok(()) -/// } -/// ``` -/// -/// See [WithOptions](crate::clients::WithOptions) for more information. -#[derive(Clone, Debug, Eq, PartialEq, Default)] -pub struct Options { - /// Set the max number of write attempts for a command. - pub max_attempts: Option, - /// Set the max number of cluster redirections to follow for a command. - pub max_redirections: Option, - /// Set the timeout duration for a command. - /// - /// This interface is more* cancellation-safe than a simple [timeout](https://docs.rs/tokio/latest/tokio/time/fn.timeout.html) call. - /// - /// * But it's not perfect. There's no reliable mechanism to cancel a command once it has been written - /// to the connection. - pub timeout: Option, - /// The cluster node that should receive the command. - /// - /// The caller will receive a `RedisErrorKind::Cluster` error if the provided server does not exist. - /// - /// The client will still follow redirection errors via this interface. Callers may not notice this, but incorrect - /// server arguments here could result in unnecessary calls to refresh the cached cluster routing table. - pub cluster_node: Option, - /// The cluster hashing policy to use, if applicable. - /// - /// If `cluster_node` is also provided it will take precedence over this value. - pub cluster_hash: Option, - /// Whether to skip backpressure checks for a command. - pub no_backpressure: bool, - /// Whether the command should fail quickly if the connection is not healthy or available for writes. This always - /// takes precedence over `max_attempts` if `true`. - /// - /// This can be useful for caching use cases where it's preferable to fail fast with a fallback query to another - /// storage layer rather than wait for a reconnection delay. - /// - /// Default: `false` - pub fail_fast: bool, - /// Whether to send `CLIENT CACHING yes|no` before the command. - #[cfg(feature = "i-tracking")] - #[cfg_attr(docsrs, doc(cfg(feature = "i-tracking")))] - pub caching: Option, -} - -impl Options { - /// Set the non-null values from `other` onto `self`. - pub fn extend(&mut self, other: &Self) -> &mut Self { - if let Some(val) = other.max_attempts { - self.max_attempts = Some(val); - } - if let Some(val) = other.max_redirections { - self.max_redirections = Some(val); - } - if let Some(val) = other.timeout { - self.timeout = Some(val); - } - if let Some(ref val) = other.cluster_node { - self.cluster_node = Some(val.clone()); - } - if let Some(ref cluster_hash) = other.cluster_hash { - self.cluster_hash = Some(cluster_hash.clone()); - } - self.no_backpressure |= other.no_backpressure; - self.fail_fast |= other.fail_fast; - - #[cfg(feature = "i-tracking")] - if let Some(val) = other.caching { - self.caching = Some(val); - } - - self - } - - /// Create options from a command - #[cfg(feature = "transactions")] - pub(crate) fn from_command(cmd: &RedisCommand) -> Self { - Options { - max_attempts: Some(cmd.attempts_remaining), - max_redirections: Some(cmd.redirections_remaining), - timeout: cmd.timeout_dur, - no_backpressure: cmd.skip_backpressure, - cluster_node: cmd.cluster_node.clone(), - cluster_hash: Some(cmd.hasher.clone()), - fail_fast: cmd.fail_fast, - #[cfg(feature = "i-tracking")] - caching: cmd.caching, - } - } - - /// Overwrite the configuration options on the provided command. - pub(crate) fn apply(&self, command: &mut RedisCommand) { - command.skip_backpressure = self.no_backpressure; - command.timeout_dur = self.timeout; - command.cluster_node = self.cluster_node.clone(); - command.fail_fast = self.fail_fast; - - #[cfg(feature = "i-tracking")] - { - command.caching = self.caching; - } - - if let Some(attempts) = self.max_attempts { - command.attempts_remaining = attempts; - } - if let Some(redirections) = self.max_redirections { - command.redirections_remaining = redirections; - } - if let Some(ref cluster_hash) = self.cluster_hash { - command.hasher = cluster_hash.clone(); - } - } -} - -#[cfg(test)] -mod tests { - #[cfg(feature = "sentinel-auth")] - use crate::types::Server; - #[allow(unused_imports)] - use crate::{prelude::ServerConfig, types::RedisConfig, utils}; - - #[test] - fn should_parse_centralized_url() { - let url = "redis://username:password@foo.com:6379/1"; - let expected = RedisConfig { - server: ServerConfig::new_centralized("foo.com", 6379), - database: Some(1), - username: Some("username".into()), - password: Some("password".into()), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_centralized(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_centralized_url_without_port() { - let url = "redis://foo.com"; - let expected = RedisConfig { - server: ServerConfig::new_centralized("foo.com", 6379), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_centralized(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_centralized_url_without_creds() { - let url = "redis://foo.com:6379/1"; - let expected = RedisConfig { - server: ServerConfig::new_centralized("foo.com", 6379), - database: Some(1), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_centralized(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_centralized_url_without_db() { - let url = "redis://username:password@foo.com:6379"; - let expected = RedisConfig { - server: ServerConfig::new_centralized("foo.com", 6379), - username: Some("username".into()), - password: Some("password".into()), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_centralized(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - #[cfg(feature = "enable-native-tls")] - fn should_parse_centralized_url_with_tls() { - let url = "rediss://username:password@foo.com:6379/1"; - let expected = RedisConfig { - server: ServerConfig::new_centralized("foo.com", 6379), - database: Some(1), - username: Some("username".into()), - password: Some("password".into()), - tls: utils::tls_config_from_url(true).unwrap(), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_centralized(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_clustered_url() { - let url = "redis-cluster://username:password@foo.com:30000"; - let expected = RedisConfig { - server: ServerConfig::new_clustered(vec![("foo.com", 30000)]), - username: Some("username".into()), - password: Some("password".into()), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_clustered(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_clustered_url_without_port() { - let url = "redis-cluster://foo.com"; - let expected = RedisConfig { - server: ServerConfig::new_clustered(vec![("foo.com", 6379)]), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_clustered(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_clustered_url_without_creds() { - let url = "redis-cluster://foo.com:30000"; - let expected = RedisConfig { - server: ServerConfig::new_clustered(vec![("foo.com", 30000)]), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_clustered(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_clustered_url_with_other_nodes() { - let url = "redis-cluster://username:password@foo.com:30000?node=bar.com:30001&node=baz.com:30002"; - let expected = RedisConfig { - // need to be careful with the array ordering here - server: ServerConfig::new_clustered(vec![("bar.com", 30001), ("baz.com", 30002), ("foo.com", 30000)]), - username: Some("username".into()), - password: Some("password".into()), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_clustered(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - #[cfg(feature = "enable-native-tls")] - fn should_parse_clustered_url_with_tls() { - let url = "rediss-cluster://username:password@foo.com:30000"; - let expected = RedisConfig { - server: ServerConfig::new_clustered(vec![("foo.com", 30000)]), - username: Some("username".into()), - password: Some("password".into()), - tls: utils::tls_config_from_url(true).unwrap(), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_clustered(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_sentinel_url() { - let url = "redis-sentinel://username:password@foo.com:26379/1?sentinelServiceName=fakename"; - let expected = RedisConfig { - server: ServerConfig::new_sentinel(vec![("foo.com", 26379)], "fakename"), - username: Some("username".into()), - password: Some("password".into()), - database: Some(1), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_sentinel(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn should_parse_sentinel_url_with_other_nodes() { - let url = "redis-sentinel://username:password@foo.com:26379/1?sentinelServiceName=fakename&node=bar.com:26380&\ - node=baz.com:26381"; - let expected = RedisConfig { - // also need to be careful with array ordering here - server: ServerConfig::new_sentinel( - vec![("bar.com", 26380), ("baz.com", 26381), ("foo.com", 26379)], - "fakename", - ), - username: Some("username".into()), - password: Some("password".into()), - database: Some(1), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_sentinel(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - #[cfg(feature = "unix-sockets")] - fn should_parse_unix_socket_url_no_auth() { - let url = "redis+unix:///path/to/redis.sock"; - let expected = RedisConfig { - server: ServerConfig::Unix { - path: "/path/to/redis.sock".into(), - }, - username: None, - password: None, - ..Default::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_unix(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - #[cfg(feature = "unix-sockets")] - fn should_parse_unix_socket_url_with_auth() { - let url = "redis+unix://username:password@foo/path/to/redis.sock"; - let expected = RedisConfig { - server: ServerConfig::Unix { - path: "/path/to/redis.sock".into(), - }, - username: Some("username".into()), - password: Some("password".into()), - ..Default::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_unix(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - #[cfg(feature = "enable-native-tls")] - fn should_parse_sentinel_url_with_tls() { - let url = "rediss-sentinel://username:password@foo.com:26379/1?sentinelServiceName=fakename"; - let expected = RedisConfig { - server: ServerConfig::new_sentinel(vec![("foo.com", 26379)], "fakename"), - username: Some("username".into()), - password: Some("password".into()), - database: Some(1), - tls: utils::tls_config_from_url(true).unwrap(), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_sentinel(url).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - #[cfg(feature = "sentinel-auth")] - fn should_parse_sentinel_url_with_sentinel_auth() { - let url = "redis-sentinel://username1:password1@foo.com:26379/1?sentinelServiceName=fakename&\ - sentinelUsername=username2&sentinelPassword=password2"; - let expected = RedisConfig { - server: ServerConfig::Sentinel { - hosts: vec![Server::new("foo.com", 26379)], - service_name: "fakename".into(), - username: Some("username2".into()), - password: Some("password2".into()), - }, - username: Some("username1".into()), - password: Some("password1".into()), - database: Some(1), - ..RedisConfig::default() - }; - - let actual = RedisConfig::from_url(url).unwrap(); - assert_eq!(actual, expected); - let actual = RedisConfig::from_url_sentinel(url).unwrap(); - assert_eq!(actual, expected); - } -} diff --git a/src/types/from_tuple.rs b/src/types/from_tuple.rs deleted file mode 100644 index eaf203c7..00000000 --- a/src/types/from_tuple.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::types::{MultipleKeys, RedisKey, RedisValue}; - -macro_rules! tuple2val { - ($($id:tt $ty:ident);+) => { -impl<$($ty: Into),+ > From<($($ty),+)> for RedisValue { - fn from(value: ($($ty),+)) -> Self { - RedisValue::Array(vec![$(value.$id.into()),+]) - } -} - -impl<$($ty: Into),+ > From<($($ty),+)> for MultipleKeys { - fn from(value: ($($ty),+)) -> Self { - Self{keys:vec![$(value.$id.into()),+]} - } -} - }; -} - -tuple2val!(0 A0; 1 A1); -tuple2val!(0 A0; 1 A1; 2 A2); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10; 11 A11); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10; 11 A11; 12 A12); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10; 11 A11; 12 A12; 13 A13); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10; 11 A11; 12 A12; 13 A13; 14 A14); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10; 11 A11; 12 A12; 13 A13; 14 A14; 15 -A15); -tuple2val!(0 A0; 1 A1; 2 A2; 3 A3; 4 A4; 5 A5; 6 A6; 7 A7; 8 A8; 9 A9; 10 A10; 11 A11; 12 A12; 13 A13; 14 -A14; 15 A15; 16 A16); diff --git a/src/types/geo.rs b/src/types/geo.rs deleted file mode 100644 index 02ac4378..00000000 --- a/src/types/geo.rs +++ /dev/null @@ -1,222 +0,0 @@ -use crate::{error::RedisError, protocol::utils as protocol_utils, types::RedisValue, utils}; -use bytes_utils::Str; -use std::{ - collections::VecDeque, - convert::{TryFrom, TryInto}, -}; - -/// A struct describing the longitude and latitude coordinates of a GEO command. -#[derive(Clone, Debug)] -pub struct GeoPosition { - pub longitude: f64, - pub latitude: f64, -} - -impl PartialEq for GeoPosition { - fn eq(&self, other: &Self) -> bool { - utils::f64_eq(self.longitude, other.longitude) && utils::f64_eq(self.latitude, other.latitude) - } -} - -impl Eq for GeoPosition {} - -impl From<(f64, f64)> for GeoPosition { - fn from(d: (f64, f64)) -> Self { - GeoPosition { - longitude: d.0, - latitude: d.1, - } - } -} - -impl TryFrom for GeoPosition { - type Error = RedisError; - - fn try_from(value: RedisValue) -> Result { - let (longitude, latitude): (f64, f64) = value.convert()?; - Ok(GeoPosition { longitude, latitude }) - } -} - -/// Units for the GEO DIST command. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum GeoUnit { - Meters, - Kilometers, - Miles, - Feet, -} - -impl GeoUnit { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - GeoUnit::Meters => "m", - GeoUnit::Kilometers => "km", - GeoUnit::Feet => "ft", - GeoUnit::Miles => "mi", - }) - } -} - -/// A struct describing the value inside a GEO data structure. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct GeoValue { - pub coordinates: GeoPosition, - pub member: RedisValue, -} - -impl TryFrom<(f64, f64, T)> for GeoValue -where - T: TryInto, - T::Error: Into, -{ - type Error = RedisError; - - fn try_from(v: (f64, f64, T)) -> Result { - Ok(GeoValue { - coordinates: GeoPosition { - longitude: v.0, - latitude: v.1, - }, - member: utils::try_into(v.2)?, - }) - } -} - -/// A convenience struct for commands that take one or more GEO values. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct MultipleGeoValues { - inner: Vec, -} - -impl MultipleGeoValues { - pub fn len(&self) -> usize { - self.inner.len() - } - - pub fn inner(self) -> Vec { - self.inner - } -} - -impl From for MultipleGeoValues { - fn from(d: GeoValue) -> Self { - MultipleGeoValues { inner: vec![d] } - } -} - -impl From> for MultipleGeoValues { - fn from(d: Vec) -> Self { - MultipleGeoValues { inner: d } - } -} - -impl From> for MultipleGeoValues { - fn from(d: VecDeque) -> Self { - MultipleGeoValues { - inner: d.into_iter().collect(), - } - } -} - -/// A typed struct representing the full output of the GEORADIUS (or similar) command. -#[derive(Clone, Debug)] -pub struct GeoRadiusInfo { - pub member: RedisValue, - pub position: Option, - pub distance: Option, - pub hash: Option, -} - -impl Default for GeoRadiusInfo { - fn default() -> Self { - GeoRadiusInfo { - member: RedisValue::Null, - position: None, - distance: None, - hash: None, - } - } -} - -impl PartialEq for GeoRadiusInfo { - fn eq(&self, other: &Self) -> bool { - self.member == other.member - && self.position == other.position - && self.hash == other.hash - && utils::f64_opt_eq(&self.distance, &other.distance) - } -} - -impl Eq for GeoRadiusInfo {} - -impl GeoRadiusInfo { - /// Parse the value with context from the calling command. - pub fn from_redis_value( - value: RedisValue, - withcoord: bool, - withdist: bool, - withhash: bool, - ) -> Result { - if let RedisValue::Array(mut data) = value { - let mut out = GeoRadiusInfo::default(); - data.reverse(); - - if withcoord && withdist && withhash { - // 4 elements: member, dist, hash, position - protocol_utils::assert_array_len(&data, 4)?; - - out.member = data.pop().unwrap(); - out.distance = data.pop().unwrap().convert()?; - out.hash = data.pop().unwrap().convert()?; - out.position = data.pop().unwrap().convert()?; - } else if withcoord && withdist { - // 3 elements: member, dist, position - protocol_utils::assert_array_len(&data, 3)?; - - out.member = data.pop().unwrap(); - out.distance = data.pop().unwrap().convert()?; - out.position = data.pop().unwrap().convert()?; - } else if withcoord && withhash { - // 3 elements: member, hash, position - protocol_utils::assert_array_len(&data, 3)?; - - out.member = data.pop().unwrap(); - out.hash = data.pop().unwrap().convert()?; - out.position = data.pop().unwrap().convert()?; - } else if withdist && withhash { - // 3 elements: member, dist, hash - protocol_utils::assert_array_len(&data, 3)?; - - out.member = data.pop().unwrap(); - out.distance = data.pop().unwrap().convert()?; - out.hash = data.pop().unwrap().convert()?; - } else if withcoord { - // 2 elements: member, position - protocol_utils::assert_array_len(&data, 2)?; - - out.member = data.pop().unwrap(); - out.position = data.pop().unwrap().convert()?; - } else if withdist { - // 2 elements: member, dist - protocol_utils::assert_array_len(&data, 2)?; - - out.member = data.pop().unwrap(); - out.distance = data.pop().unwrap().convert()?; - } else if withhash { - // 2 elements: member, hash - protocol_utils::assert_array_len(&data, 2)?; - - out.member = data.pop().unwrap(); - out.hash = data.pop().unwrap().convert()?; - } - - Ok(out) - } else { - Ok(GeoRadiusInfo { - member: value, - ..Default::default() - }) - } - } -} diff --git a/src/types/lists.rs b/src/types/lists.rs deleted file mode 100644 index b444d762..00000000 --- a/src/types/lists.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::utils; -use bytes_utils::Str; - -/// The direction to move elements in a *LMOVE command. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum LMoveDirection { - Left, - Right, -} - -impl LMoveDirection { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - LMoveDirection::Left => "LEFT", - LMoveDirection::Right => "RIGHT", - }) - } -} - -/// Location flag for the `LINSERT` command. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ListLocation { - Before, - After, -} - -impl ListLocation { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - ListLocation::Before => "BEFORE", - ListLocation::After => "AFTER", - }) - } -} diff --git a/src/types/misc.rs b/src/types/misc.rs deleted file mode 100644 index f0979cbb..00000000 --- a/src/types/misc.rs +++ /dev/null @@ -1,684 +0,0 @@ -pub use crate::protocol::{ - hashers::ClusterHash, - types::{Message, MessageKind}, -}; -use crate::{ - error::{RedisError, RedisErrorKind}, - types::{RedisKey, RedisValue, Server}, - utils, -}; -use bytes_utils::Str; -use std::{convert::TryFrom, fmt, time::Duration}; - -#[cfg(feature = "i-memory")] -use crate::utils::convert_or_default; -#[cfg(feature = "i-memory")] -use std::collections::HashMap; - -/// Arguments passed to the SHUTDOWN command. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ShutdownFlags { - Save, - NoSave, -} - -impl ShutdownFlags { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - ShutdownFlags::Save => "SAVE", - ShutdownFlags::NoSave => "NOSAVE", - }) - } -} - -/// An event on the publish-subscribe interface describing a keyspace notification. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct KeyspaceEvent { - pub db: u8, - pub operation: String, - pub key: RedisKey, -} - -/// Aggregate options for the [zinterstore](https://redis.io/commands/zinterstore) (and related) commands. -pub enum AggregateOptions { - Sum, - Min, - Max, -} - -impl AggregateOptions { - #[cfg(feature = "i-sorted-sets")] - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - AggregateOptions::Sum => "SUM", - AggregateOptions::Min => "MIN", - AggregateOptions::Max => "MAX", - }) - } -} - -/// Options for the [info](https://redis.io/commands/info) command. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum InfoKind { - Default, - All, - Keyspace, - Cluster, - CommandStats, - Cpu, - Replication, - Stats, - Persistence, - Memory, - Clients, - Server, -} - -impl InfoKind { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - InfoKind::Default => "default", - InfoKind::All => "all", - InfoKind::Keyspace => "keyspace", - InfoKind::Cluster => "cluster", - InfoKind::CommandStats => "commandstats", - InfoKind::Cpu => "cpu", - InfoKind::Replication => "replication", - InfoKind::Stats => "stats", - InfoKind::Persistence => "persistence", - InfoKind::Memory => "memory", - InfoKind::Clients => "clients", - InfoKind::Server => "server", - }) - } -} - -/// Configuration for custom redis commands, primarily used for interacting with third party modules or extensions. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct CustomCommand { - /// The command name, sent directly to the server. - pub cmd: Str, - /// The cluster hashing policy to use, if any. - /// - /// Cluster clients will use the default policy if not provided. - pub cluster_hash: ClusterHash, - /// Whether the command should block the connection while waiting on a response. - pub blocking: bool, -} - -impl CustomCommand { - /// Create a new custom command. - /// - /// See the [custom](crate::interfaces::ClientLike::custom) command for more information. - pub fn new(cmd: C, cluster_hash: H, blocking: bool) -> Self - where - C: Into, - H: Into, - { - CustomCommand { - cmd: cmd.into(), - cluster_hash: cluster_hash.into(), - blocking, - } - } - - /// Create a new custom command specified by a `&'static str`. - pub fn new_static(cmd: &'static str, cluster_hash: H, blocking: bool) -> Self - where - H: Into, - { - CustomCommand { - cmd: utils::static_str(cmd), - cluster_hash: cluster_hash.into(), - blocking, - } - } -} - -/// An enum describing the possible ways in which a Redis cluster can change state. -/// -/// See [on_cluster_change](crate::interfaces::EventInterface::on_cluster_change) for more information. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClusterStateChange { - /// A node was added to the cluster. - /// - /// This implies that hash slots were also probably rebalanced. - Add(Server), - /// A node was removed from the cluster. - /// - /// This implies that hash slots were also probably rebalanced. - Remove(Server), - /// Hash slots were rebalanced across the cluster and/or local routing state was updated. - Rebalance, -} - -/// Options for the [set](https://redis.io/commands/set) command. -/// -/// -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum SetOptions { - NX, - XX, -} - -impl SetOptions { - #[allow(dead_code)] - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - SetOptions::NX => "NX", - SetOptions::XX => "XX", - }) - } -} - -/// Options for certain expiration commands (`PEXPIRE`, etc). -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum ExpireOptions { - NX, - XX, - GT, - LT, -} - -impl ExpireOptions { - #[allow(dead_code)] - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - ExpireOptions::NX => "NX", - ExpireOptions::XX => "XX", - ExpireOptions::GT => "GT", - ExpireOptions::LT => "LT", - }) - } -} - -/// Expiration options for the [set](https://redis.io/commands/set) command. -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum Expiration { - /// Expiration in seconds. - EX(i64), - /// Expiration in milliseconds. - PX(i64), - /// Expiration time, in seconds. - EXAT(i64), - /// Expiration time, in milliseconds. - PXAT(i64), - /// Do not reset the TTL. - KEEPTTL, -} - -impl Expiration { - #[allow(dead_code)] - pub(crate) fn into_args(self) -> (Str, Option) { - let (prefix, value) = match self { - Expiration::EX(i) => ("EX", Some(i)), - Expiration::PX(i) => ("PX", Some(i)), - Expiration::EXAT(i) => ("EXAT", Some(i)), - Expiration::PXAT(i) => ("PXAT", Some(i)), - Expiration::KEEPTTL => ("KEEPTTL", None), - }; - - (utils::static_str(prefix), value) - } -} - -/// The state of the underlying connection to the Redis server. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClientState { - Disconnected, - Disconnecting, - Connected, - Connecting, -} - -impl ClientState { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - ClientState::Connecting => "Connecting", - ClientState::Connected => "Connected", - ClientState::Disconnecting => "Disconnecting", - ClientState::Disconnected => "Disconnected", - }) - } -} - -impl fmt::Display for ClientState { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_str()) - } -} - -/// The parsed result of the MEMORY STATS command for a specific database. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq)] -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -pub struct DatabaseMemoryStats { - pub overhead_hashtable_main: u64, - pub overhead_hashtable_expires: u64, - pub overhead_hashtable_slot_to_keys: u64, -} - -#[cfg(feature = "i-memory")] -impl Default for DatabaseMemoryStats { - fn default() -> Self { - DatabaseMemoryStats { - overhead_hashtable_expires: 0, - overhead_hashtable_main: 0, - overhead_hashtable_slot_to_keys: 0, - } - } -} - -#[cfg(feature = "i-memory")] -fn parse_database_memory_stat(stats: &mut DatabaseMemoryStats, key: &str, value: RedisValue) { - match key { - "overhead.hashtable.main" => stats.overhead_hashtable_main = convert_or_default(value), - "overhead.hashtable.expires" => stats.overhead_hashtable_expires = convert_or_default(value), - "overhead.hashtable.slot-to-keys" => stats.overhead_hashtable_slot_to_keys = convert_or_default(value), - _ => {}, - }; -} - -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -impl TryFrom for DatabaseMemoryStats { - type Error = RedisError; - - fn try_from(value: RedisValue) -> Result { - let values: HashMap = value.convert()?; - let mut out = DatabaseMemoryStats::default(); - - for (key, value) in values.into_iter() { - parse_database_memory_stat(&mut out, &key, value); - } - Ok(out) - } -} - -/// The parsed result of the MEMORY STATS command. -/// -/// -#[derive(Clone, Debug)] -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -pub struct MemoryStats { - pub peak_allocated: u64, - pub total_allocated: u64, - pub startup_allocated: u64, - pub replication_backlog: u64, - pub clients_slaves: u64, - pub clients_normal: u64, - pub aof_buffer: u64, - pub lua_caches: u64, - pub overhead_total: u64, - pub keys_count: u64, - pub keys_bytes_per_key: u64, - pub dataset_bytes: u64, - pub dataset_percentage: f64, - pub peak_percentage: f64, - pub fragmentation: f64, - pub fragmentation_bytes: u64, - pub rss_overhead_ratio: f64, - pub rss_overhead_bytes: u64, - pub allocator_allocated: u64, - pub allocator_active: u64, - pub allocator_resident: u64, - pub allocator_fragmentation_ratio: f64, - pub allocator_fragmentation_bytes: u64, - pub allocator_rss_ratio: f64, - pub allocator_rss_bytes: u64, - pub db: HashMap, -} - -#[cfg(feature = "i-memory")] -impl Default for MemoryStats { - fn default() -> Self { - MemoryStats { - peak_allocated: 0, - total_allocated: 0, - startup_allocated: 0, - replication_backlog: 0, - clients_normal: 0, - clients_slaves: 0, - aof_buffer: 0, - lua_caches: 0, - overhead_total: 0, - keys_count: 0, - keys_bytes_per_key: 0, - dataset_bytes: 0, - dataset_percentage: 0.0, - peak_percentage: 0.0, - fragmentation: 0.0, - fragmentation_bytes: 0, - rss_overhead_ratio: 0.0, - rss_overhead_bytes: 0, - allocator_allocated: 0, - allocator_active: 0, - allocator_resident: 0, - allocator_fragmentation_ratio: 0.0, - allocator_fragmentation_bytes: 0, - allocator_rss_bytes: 0, - allocator_rss_ratio: 0.0, - db: HashMap::new(), - } - } -} -#[cfg(feature = "i-memory")] -impl PartialEq for MemoryStats { - fn eq(&self, other: &Self) -> bool { - self.peak_allocated == other.peak_allocated - && self.total_allocated == other.total_allocated - && self.startup_allocated == other.startup_allocated - && self.replication_backlog == other.replication_backlog - && self.clients_normal == other.clients_normal - && self.clients_slaves == other.clients_slaves - && self.aof_buffer == other.aof_buffer - && self.lua_caches == other.lua_caches - && self.overhead_total == other.overhead_total - && self.keys_count == other.keys_count - && self.keys_bytes_per_key == other.keys_bytes_per_key - && self.dataset_bytes == other.dataset_bytes - && utils::f64_eq(self.dataset_percentage, other.dataset_percentage) - && utils::f64_eq(self.peak_percentage, other.peak_percentage) - && utils::f64_eq(self.fragmentation, other.fragmentation) - && self.fragmentation_bytes == other.fragmentation_bytes - && utils::f64_eq(self.rss_overhead_ratio, other.rss_overhead_ratio) - && self.rss_overhead_bytes == other.rss_overhead_bytes - && self.allocator_allocated == other.allocator_allocated - && self.allocator_active == other.allocator_active - && self.allocator_resident == other.allocator_resident - && utils::f64_eq(self.allocator_fragmentation_ratio, other.allocator_fragmentation_ratio) - && self.allocator_fragmentation_bytes == other.allocator_fragmentation_bytes - && self.allocator_rss_bytes == other.allocator_rss_bytes - && utils::f64_eq(self.allocator_rss_ratio, other.allocator_rss_ratio) - && self.db == other.db - } -} - -#[cfg(feature = "i-memory")] -impl Eq for MemoryStats {} - -#[cfg(feature = "i-memory")] -fn parse_memory_stat_field(stats: &mut MemoryStats, key: &str, value: RedisValue) { - match key { - "peak.allocated" => stats.peak_allocated = convert_or_default(value), - "total.allocated" => stats.total_allocated = convert_or_default(value), - "startup.allocated" => stats.startup_allocated = convert_or_default(value), - "replication.backlog" => stats.replication_backlog = convert_or_default(value), - "clients.slaves" => stats.clients_slaves = convert_or_default(value), - "clients.normal" => stats.clients_normal = convert_or_default(value), - "aof.buffer" => stats.aof_buffer = convert_or_default(value), - "lua.caches" => stats.lua_caches = convert_or_default(value), - "overhead.total" => stats.overhead_total = convert_or_default(value), - "keys.count" => stats.keys_count = convert_or_default(value), - "keys.bytes-per-key" => stats.keys_bytes_per_key = convert_or_default(value), - "dataset.bytes" => stats.dataset_bytes = convert_or_default(value), - "dataset.percentage" => stats.dataset_percentage = convert_or_default(value), - "peak.percentage" => stats.peak_percentage = convert_or_default(value), - "allocator.allocated" => stats.allocator_allocated = convert_or_default(value), - "allocator.active" => stats.allocator_active = convert_or_default(value), - "allocator.resident" => stats.allocator_resident = convert_or_default(value), - "allocator-fragmentation.ratio" => stats.allocator_fragmentation_ratio = convert_or_default(value), - "allocator-fragmentation.bytes" => stats.allocator_fragmentation_bytes = convert_or_default(value), - "allocator-rss.ratio" => stats.allocator_rss_ratio = convert_or_default(value), - "allocator-rss.bytes" => stats.allocator_rss_bytes = convert_or_default(value), - "rss-overhead.ratio" => stats.rss_overhead_ratio = convert_or_default(value), - "rss-overhead.bytes" => stats.rss_overhead_bytes = convert_or_default(value), - "fragmentation" => stats.fragmentation = convert_or_default(value), - "fragmentation.bytes" => stats.fragmentation_bytes = convert_or_default(value), - _ => { - if key.starts_with("db.") { - let db = match key.split('.').last().and_then(|v| v.parse::().ok()) { - Some(db) => db, - None => return, - }; - let parsed: DatabaseMemoryStats = match value.convert().ok() { - Some(db) => db, - None => return, - }; - - stats.db.insert(db, parsed); - } - }, - } -} - -#[cfg(feature = "i-memory")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-memory")))] -impl TryFrom for MemoryStats { - type Error = RedisError; - - fn try_from(value: RedisValue) -> Result { - let values: HashMap = value.convert()?; - let mut out = MemoryStats::default(); - - for (key, value) in values.into_iter() { - parse_memory_stat_field(&mut out, &key, value); - } - Ok(out) - } -} - -/// The output of an entry in the slow queries log. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SlowlogEntry { - pub id: i64, - pub timestamp: i64, - pub duration: Duration, - pub args: Vec, - pub ip: Option, - pub name: Option, -} - -impl TryFrom for SlowlogEntry { - type Error = RedisError; - - fn try_from(value: RedisValue) -> Result { - if let RedisValue::Array(values) = value { - if values.len() < 4 { - return Err(RedisError::new( - RedisErrorKind::Protocol, - "Expected at least 4 response values.", - )); - } - - let id = values[0] - .as_i64() - .ok_or(RedisError::new(RedisErrorKind::Protocol, "Expected integer ID."))?; - let timestamp = values[1] - .as_i64() - .ok_or(RedisError::new(RedisErrorKind::Protocol, "Expected integer timestamp."))?; - let duration = values[2] - .as_u64() - .map(Duration::from_micros) - .ok_or(RedisError::new(RedisErrorKind::Protocol, "Expected integer duration."))?; - let args = values[3].clone().into_multiple_values(); - - let (ip, name) = if values.len() == 6 { - let ip = values[4] - .as_bytes_str() - .ok_or(RedisError::new(RedisErrorKind::Protocol, "Expected IP address string."))?; - let name = values[5].as_bytes_str().ok_or(RedisError::new( - RedisErrorKind::Protocol, - "Expected client name string.", - ))?; - - (Some(ip), Some(name)) - } else { - (None, None) - }; - - Ok(SlowlogEntry { - id, - timestamp, - duration, - args, - ip, - name, - }) - } else { - Err(RedisError::new_parse("Expected array.")) - } - } -} - -/// Flags for the SCRIPT DEBUG command. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ScriptDebugFlag { - Yes, - No, - Sync, -} - -impl ScriptDebugFlag { - #[cfg(feature = "i-scripts")] - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - ScriptDebugFlag::Yes => "YES", - ScriptDebugFlag::No => "NO", - ScriptDebugFlag::Sync => "SYNC", - }) - } -} - -/// Arguments for the `SENTINEL SIMULATE-FAILURE` command. -#[derive(Clone, Debug, Eq, PartialEq)] -#[cfg(feature = "sentinel-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "sentinel-client")))] -pub enum SentinelFailureKind { - CrashAfterElection, - CrashAfterPromotion, - Help, -} - -#[cfg(feature = "sentinel-client")] -impl SentinelFailureKind { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match self { - SentinelFailureKind::CrashAfterElection => "crash-after-election", - SentinelFailureKind::CrashAfterPromotion => "crash-after-promotion", - SentinelFailureKind::Help => "help", - }) - } -} - -/// The sort order for redis commands that take or return a sorted list. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum SortOrder { - Asc, - Desc, -} - -impl SortOrder { - #[allow(dead_code)] - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - SortOrder::Asc => "ASC", - SortOrder::Desc => "DESC", - }) - } -} - -/// The policy type for the [FUNCTION RESTORE](https://redis.io/commands/function-restore/) command. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum FnPolicy { - Flush, - Append, - Replace, -} - -impl Default for FnPolicy { - fn default() -> Self { - FnPolicy::Append - } -} - -impl FnPolicy { - #[cfg(feature = "i-scripts")] - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - FnPolicy::Flush => "FLUSH", - FnPolicy::Append => "APPEND", - FnPolicy::Replace => "REPLACE", - }) - } - - pub(crate) fn from_str(s: &str) -> Result { - Ok(match s { - "flush" | "FLUSH" => FnPolicy::Flush, - "append" | "APPEND" => FnPolicy::Append, - "replace" | "REPLACE" => FnPolicy::Replace, - _ => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Invalid function restore policy.", - )) - }, - }) - } -} - -// have to implement these for specific types to avoid conflicting with the core Into implementation -impl TryFrom<&str> for FnPolicy { - type Error = RedisError; - - fn try_from(value: &str) -> Result { - FnPolicy::from_str(value) - } -} - -impl TryFrom<&String> for FnPolicy { - type Error = RedisError; - - fn try_from(value: &String) -> Result { - FnPolicy::from_str(value.as_str()) - } -} - -impl TryFrom for FnPolicy { - type Error = RedisError; - - fn try_from(value: String) -> Result { - FnPolicy::from_str(value.as_str()) - } -} - -impl TryFrom for FnPolicy { - type Error = RedisError; - - fn try_from(value: Str) -> Result { - FnPolicy::from_str(&value) - } -} - -impl TryFrom<&Str> for FnPolicy { - type Error = RedisError; - - fn try_from(value: &Str) -> Result { - FnPolicy::from_str(value) - } -} - -/// Arguments to the CLIENT UNBLOCK command. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClientUnblockFlag { - Timeout, - Error, -} - -impl ClientUnblockFlag { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - ClientUnblockFlag::Timeout => "TIMEOUT", - ClientUnblockFlag::Error => "ERROR", - }) - } -} diff --git a/src/types/mod.rs b/src/types/mod.rs deleted file mode 100644 index 1148edea..00000000 --- a/src/types/mod.rs +++ /dev/null @@ -1,82 +0,0 @@ -pub use crate::modules::response::{FromRedis, FromRedisKey}; -use crate::{error::RedisError, runtime::JoinHandle}; -pub use redis_protocol::resp3::types::{BytesFrame as Resp3Frame, RespVersion}; - -mod args; -mod builder; -#[cfg(feature = "i-client")] -mod client; -#[cfg(feature = "i-cluster")] -mod cluster; -mod config; -mod from_tuple; -#[cfg(feature = "i-geo")] -mod geo; -#[cfg(feature = "i-lists")] -mod lists; -mod misc; -mod multiple; -#[cfg(feature = "i-redisearch")] -mod redisearch; -mod scan; -#[cfg(feature = "i-scripts")] -mod scripts; -#[cfg(feature = "i-sorted-sets")] -mod sorted_sets; -#[cfg(feature = "i-streams")] -mod streams; -#[cfg(feature = "i-time-series")] -mod timeseries; - -#[cfg(feature = "metrics")] -#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] -pub use crate::modules::metrics::Stats; -pub use args::*; -pub use builder::*; -#[cfg(feature = "i-client")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-client")))] -pub use client::*; -#[cfg(feature = "i-cluster")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-cluster")))] -pub use cluster::*; -pub use config::*; -#[cfg(feature = "i-geo")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-geo")))] -pub use geo::*; -#[cfg(feature = "i-lists")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-lists")))] -pub use lists::*; -pub use misc::*; -pub use multiple::*; -#[cfg(feature = "i-redisearch")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-redisearch")))] -pub use redisearch::*; -pub use scan::*; -#[cfg(feature = "i-scripts")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-scripts")))] -pub use scripts::*; -pub use semver::Version; -#[cfg(feature = "i-sorted-sets")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-sorted-sets")))] -pub use sorted_sets::*; -#[cfg(feature = "i-streams")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-streams")))] -pub use streams::*; -#[cfg(feature = "i-time-series")] -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -pub use timeseries::*; - -#[cfg(feature = "dns")] -#[cfg_attr(docsrs, doc(cfg(feature = "dns")))] -pub use crate::protocol::types::Resolve; - -pub(crate) static QUEUED: &str = "QUEUED"; - -/// The ANY flag used on certain GEO commands. -pub type Any = bool; -/// The result from any of the `connect` functions showing the error that closed the connection, if any. -pub type ConnectHandle = JoinHandle>; -/// A tuple of `(offset, count)` values for commands that allow paging through results. -pub type Limit = (i64, i64); -/// An argument type equivalent to "[LIMIT count]". -pub type LimitCount = Option; diff --git a/src/types/multiple.rs b/src/types/multiple.rs deleted file mode 100644 index 16708c8b..00000000 --- a/src/types/multiple.rs +++ /dev/null @@ -1,150 +0,0 @@ -use crate::types::{RedisKey, RedisValue}; -use std::{collections::VecDeque, iter::FromIterator}; - -/// Convenience struct for commands that take 1 or more keys. -/// -/// **Note: this can also be used to represent an empty array of keys by passing `None` to any function that takes -/// `Into`.** This is mostly useful for `EVAL` and `EVALSHA`. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct MultipleKeys { - pub(crate) keys: Vec, -} - -impl MultipleKeys { - pub fn new() -> MultipleKeys { - MultipleKeys { keys: Vec::new() } - } - - pub fn inner(self) -> Vec { - self.keys - } - - pub fn into_values(self) -> Vec { - self.keys.into_iter().map(|k| k.into()).collect() - } - - pub fn len(&self) -> usize { - self.keys.len() - } -} - -impl From> for MultipleKeys { - fn from(key: Option) -> Self { - let keys = if let Some(key) = key { vec![key] } else { vec![] }; - MultipleKeys { keys } - } -} - -impl From for MultipleKeys -where - T: Into, -{ - fn from(d: T) -> Self { - MultipleKeys { keys: vec![d.into()] } - } -} - -impl FromIterator for MultipleKeys -where - T: Into, -{ - fn from_iter>(iter: I) -> Self { - MultipleKeys { - keys: iter.into_iter().map(|k| k.into()).collect(), - } - } -} - -impl<'a, K, const N: usize> From<&'a [K; N]> for MultipleKeys -where - K: Into + Clone, -{ - fn from(value: &'a [K; N]) -> Self { - MultipleKeys { - keys: value.iter().map(|k| k.clone().into()).collect(), - } - } -} - -impl From> for MultipleKeys -where - T: Into, -{ - fn from(d: Vec) -> Self { - MultipleKeys { - keys: d.into_iter().map(|k| k.into()).collect(), - } - } -} - -impl From> for MultipleKeys -where - T: Into, -{ - fn from(d: VecDeque) -> Self { - MultipleKeys { - keys: d.into_iter().map(|k| k.into()).collect(), - } - } -} - -impl From<()> for MultipleKeys { - fn from(_: ()) -> Self { - MultipleKeys { keys: Vec::new() } - } -} - -/// Convenience interface for commands that take 1 or more strings. -pub type MultipleStrings = MultipleKeys; - -/// Convenience interface for commands that take 1 or more values. -pub type MultipleValues = RedisValue; - -/// A convenience struct for functions that take one or more hash slot values. -pub struct MultipleHashSlots { - inner: Vec, -} - -impl MultipleHashSlots { - pub fn inner(self) -> Vec { - self.inner - } - - pub fn len(&self) -> usize { - self.inner.len() - } -} - -impl From for MultipleHashSlots { - fn from(d: u16) -> Self { - MultipleHashSlots { inner: vec![d] } - } -} - -impl From> for MultipleHashSlots { - fn from(d: Vec) -> Self { - MultipleHashSlots { inner: d } - } -} - -impl<'a> From<&'a [u16]> for MultipleHashSlots { - fn from(d: &'a [u16]) -> Self { - MultipleHashSlots { inner: d.to_vec() } - } -} - -impl From> for MultipleHashSlots { - fn from(d: VecDeque) -> Self { - MultipleHashSlots { - inner: d.into_iter().collect(), - } - } -} - -impl FromIterator for MultipleHashSlots { - fn from_iter>(iter: I) -> Self { - MultipleHashSlots { - inner: iter.into_iter().collect(), - } - } -} diff --git a/src/types/redisearch.rs b/src/types/redisearch.rs deleted file mode 100644 index 081080b4..00000000 --- a/src/types/redisearch.rs +++ /dev/null @@ -1,525 +0,0 @@ -use crate::{ - types::{GeoPosition, GeoUnit, Limit, RedisKey, RedisValue, SortOrder, ZRange}, - utils, -}; -use bytes::Bytes; -use bytes_utils::Str; - -fn bool_args(b: bool) -> usize { - if b { - 1 - } else { - 0 - } -} - -fn named_opt_args(opt: &Option) -> usize { - opt.as_ref().map(|_| 2).unwrap_or(0) -} - -/// `GROUPBY` reducer functions. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ReducerFunc { - Count, - CountDistinct, - CountDistinctIsh, - Sum, - Min, - Max, - Avg, - StdDev, - Quantile, - ToList, - FirstValue, - RandomSample, - Custom(&'static str), -} - -impl ReducerFunc { - pub(crate) fn to_str(&self) -> &'static str { - use ReducerFunc::*; - - match self { - Count => "COUNT", - CountDistinct => "COUNT_DISTINCT", - CountDistinctIsh => "COUNT_DISTINCTISH", - Sum => "SUM", - Min => "MIN", - Max => "MAX", - Avg => "AVG", - StdDev => "STDDEV", - Quantile => "QUANTILE", - ToList => "TOLIST", - FirstValue => "FIRST_VALUE", - RandomSample => "RANDOM_SAMPLE", - Custom(v) => v, - } - } -} - -/// `REDUCE` arguments in `FT.AGGREGATE`. -/// -/// Equivalent to `function nargs arg [arg ...] [AS name]` -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SearchReducer { - pub func: ReducerFunc, - pub args: Vec, - pub name: Option, -} - -impl SearchReducer { - pub(crate) fn num_args(&self) -> usize { - 3 + self.args.len() + named_opt_args(&self.name) - } -} - -/// A search field with an optional property. -/// -/// Typically equivalent to `identifier [AS property]` in `FT.AGGREGATE`, `FT.SEARCH`, etc. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SearchField { - pub identifier: Str, - pub property: Option, -} - -impl SearchField { - pub(crate) fn num_args(&self) -> usize { - 1 + named_opt_args(&self.property) - } -} - -/// Arguments to `LOAD` in `FT.AGGREGATE`. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Load { - All, - Some(Vec), -} - -/// Arguments for `WITHCURSOR` in `FT.AGGREGATE`. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct WithCursor { - pub count: Option, - pub max_idle: Option, -} - -/// Arguments for `PARAMS` in `FT.AGGREGATE`. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SearchParameter { - pub name: Str, - pub value: Str, -} - -/// An aggregation operation used in `FT.AGGREGATE`. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum AggregateOperation { - Filter { - expression: Str, - }, - GroupBy { - /// An empty array is equivalent to `GROUPBY 0` - fields: Vec, - reducers: Vec, - }, - Apply { - expression: Str, - name: Str, - }, - SortBy { - properties: Vec<(Str, SortOrder)>, - max: Option, - }, - Limit { - offset: u64, - num: u64, - }, -} - -impl AggregateOperation { - pub(crate) fn num_args(&self) -> usize { - match self { - AggregateOperation::Filter { .. } => 2, - AggregateOperation::Limit { .. } => 3, - AggregateOperation::Apply { .. } => 4, - AggregateOperation::SortBy { max, properties, .. } => 2 + (properties.len() * 2) + named_opt_args(max), - AggregateOperation::GroupBy { fields, reducers } => { - 2 + fields.len() + reducers.iter().fold(0, |m, r| m + r.num_args()) - }, - } - } -} - -/// Arguments to the `FT.AGGREGATE` command. -/// -/// -#[derive(Clone, Debug, Default)] -pub struct FtAggregateOptions { - pub verbatim: bool, - pub load: Option, - pub timeout: Option, - pub pipeline: Vec, - pub cursor: Option, - pub params: Vec, - pub dialect: Option, -} - -impl FtAggregateOptions { - pub(crate) fn num_args(&self) -> usize { - let mut count = 0; - count += bool_args(self.verbatim); - if let Some(ref load) = self.load { - count += 1 - + match load { - Load::All => 1, - Load::Some(ref v) => 1 + v.iter().fold(0, |m, f| m + f.num_args()), - }; - } - count += named_opt_args(&self.timeout); - count += self.pipeline.iter().fold(0, |m, op| m + op.num_args()); - if let Some(ref cursor) = self.cursor { - count += 1 + named_opt_args(&cursor.count) + named_opt_args(&cursor.max_idle); - } - if !self.params.is_empty() { - count += 2 + self.params.len() * 2; - } - count += named_opt_args(&self.dialect); - - count - } -} - -/// Arguments for `FILTER` in `FT.SEARCH`. -/// -/// Callers should use the `*Score*` variants on any provided [ZRange](crate::types::ZRange) values. -#[derive(Clone, Debug)] -pub struct SearchFilter { - pub attribute: Str, - pub min: ZRange, - pub max: ZRange, -} - -/// Arguments for `GEOFILTER` in `FT.SEARCH`. -#[derive(Clone, Debug)] -pub struct SearchGeoFilter { - pub attribute: Str, - pub position: GeoPosition, - pub radius: RedisValue, - pub units: GeoUnit, -} - -/// Arguments used in `SUMMARIZE` values. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq, Default)] -pub struct SearchSummarize { - pub fields: Vec, - pub frags: Option, - pub len: Option, - pub separator: Option, -} - -/// Arguments used in `HIGHLIGHT` values. -/// -/// -#[derive(Clone, Debug, Eq, PartialEq, Default)] -pub struct SearchHighlight { - pub fields: Vec, - pub tags: Option<(Str, Str)>, -} - -/// Arguments for `SORTBY` in `FT.SEARCH`. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SearchSortBy { - pub attribute: Str, - pub order: Option, - pub withcount: bool, -} - -/// Arguments to `FT.SEARCH`. -#[derive(Clone, Debug, Default)] -pub struct FtSearchOptions { - pub nocontent: bool, - pub verbatim: bool, - pub nostopwords: bool, - pub withscores: bool, - pub withpayloads: bool, - pub withsortkeys: bool, - pub filters: Vec, - pub geofilters: Vec, - pub inkeys: Vec, - pub infields: Vec, - pub r#return: Vec, - pub summarize: Option, - pub highlight: Option, - pub slop: Option, - pub timeout: Option, - pub inorder: bool, - pub language: Option, - pub expander: Option, - pub scorer: Option, - pub explainscore: bool, - pub payload: Option, - pub sortby: Option, - pub limit: Option, - pub params: Vec, - pub dialect: Option, -} - -impl FtSearchOptions { - pub(crate) fn num_args(&self) -> usize { - let mut count = 0; - count += bool_args(self.nocontent); - count += bool_args(self.verbatim); - count += bool_args(self.nostopwords); - count += bool_args(self.withscores); - count += bool_args(self.withpayloads); - count += bool_args(self.withsortkeys); - count += self.filters.len() * 4; - count += self.geofilters.len() * 6; - if !self.inkeys.is_empty() { - count += 2 + self.inkeys.len(); - } - if !self.infields.is_empty() { - count += 2 + self.infields.len(); - } - if !self.r#return.is_empty() { - count += 2; - for val in self.r#return.iter() { - count += if val.property.is_some() { 3 } else { 1 }; - } - } - if let Some(ref summarize) = self.summarize { - count += 1; - if !summarize.fields.is_empty() { - count += 2 + summarize.fields.len(); - } - count += named_opt_args(&summarize.frags); - count += named_opt_args(&summarize.len); - count += named_opt_args(&summarize.separator); - } - if let Some(ref highlight) = self.highlight { - count += 1; - if !highlight.fields.is_empty() { - count += 2 + highlight.fields.len(); - } - if highlight.tags.is_some() { - count += 3; - } - } - count += named_opt_args(&self.slop); - count += named_opt_args(&self.timeout); - count += bool_args(self.inorder); - count += named_opt_args(&self.language); - count += named_opt_args(&self.expander); - count += named_opt_args(&self.scorer); - count += bool_args(self.explainscore); - count += named_opt_args(&self.payload); - if let Some(ref sort) = self.sortby { - count += 2 + if sort.order.is_some() { 1 } else { 0 } + bool_args(sort.withcount) - } - if self.limit.is_some() { - count += 3; - } - if !self.params.is_empty() { - count += 2 + self.params.len() * 2; - } - count += named_opt_args(&self.dialect); - count - } -} - -/// Index arguments for `FT.CREATE`. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum IndexKind { - Hash, - JSON, -} - -impl IndexKind { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match self { - IndexKind::JSON => "JSON", - IndexKind::Hash => "HASH", - }) - } -} - -/// Arguments for `FT.CREATE`. -#[derive(Clone, Debug, Default)] -pub struct FtCreateOptions { - pub on: Option, - pub prefixes: Vec, - pub filter: Option, - pub language: Option, - pub language_field: Option, - pub score: Option, - pub score_field: Option, - pub payload_field: Option, - pub maxtextfields: bool, - pub temporary: Option, - pub nooffsets: bool, - pub nohl: bool, - pub nofields: bool, - pub nofreqs: bool, - pub stopwords: Vec, - pub skipinitialscan: bool, -} - -impl FtCreateOptions { - pub(crate) fn num_args(&self) -> usize { - let mut count = 0; - count += named_opt_args(&self.on); - if !self.prefixes.is_empty() { - count += 2 + self.prefixes.len(); - } - count += named_opt_args(&self.filter); - count += named_opt_args(&self.language); - count += named_opt_args(&self.language_field); - count += named_opt_args(&self.score); - count += named_opt_args(&self.score_field); - count += named_opt_args(&self.payload_field); - count += bool_args(self.maxtextfields); - count += named_opt_args(&self.temporary); - count += bool_args(self.nooffsets); - count += bool_args(self.nohl); - count += bool_args(self.nofields); - count += bool_args(self.nofreqs); - if !self.stopwords.is_empty() { - count += 2 + self.stopwords.len(); - } - count += bool_args(self.skipinitialscan); - - count - } -} - -/// One of the available schema types used with `FT.CREATE` or `FT.ALTER`. -#[derive(Clone, Debug)] -pub enum SearchSchemaKind { - Text { - sortable: bool, - unf: bool, - nostem: bool, - phonetic: Option, - weight: Option, - withsuffixtrie: bool, - noindex: bool, - }, - Tag { - sortable: bool, - unf: bool, - separator: Option, - casesensitive: bool, - withsuffixtrie: bool, - noindex: bool, - }, - Numeric { - sortable: bool, - unf: bool, - noindex: bool, - }, - Geo { - sortable: bool, - unf: bool, - noindex: bool, - }, - Vector { - noindex: bool, - }, - GeoShape { - noindex: bool, - }, - Custom { - name: Str, - arguments: Vec, - }, -} - -impl SearchSchemaKind { - pub(crate) fn num_args(&self) -> usize { - match self { - SearchSchemaKind::Custom { arguments, .. } => 1 + arguments.len(), - SearchSchemaKind::GeoShape { noindex } | SearchSchemaKind::Vector { noindex } => 1 + bool_args(*noindex), - SearchSchemaKind::Geo { sortable, unf, noindex } | SearchSchemaKind::Numeric { sortable, unf, noindex } => { - 1 + bool_args(*sortable) + bool_args(*unf) + bool_args(*noindex) - }, - SearchSchemaKind::Tag { - sortable, - unf, - separator, - casesensitive, - withsuffixtrie, - noindex, - } => { - 1 + bool_args(*sortable) - + bool_args(*unf) - + named_opt_args(separator) - + bool_args(*casesensitive) - + bool_args(*withsuffixtrie) - + bool_args(*noindex) - }, - SearchSchemaKind::Text { - sortable, - unf, - nostem, - phonetic, - weight, - withsuffixtrie, - noindex, - } => { - 1 + bool_args(*sortable) - + bool_args(*unf) - + bool_args(*nostem) - + named_opt_args(phonetic) - + named_opt_args(weight) - + bool_args(*withsuffixtrie) - + bool_args(*noindex) - }, - } - } -} - -/// Arguments for `SCHEMA` in `FT.CREATE`. -#[derive(Clone, Debug)] -pub struct SearchSchema { - pub field_name: Str, - pub alias: Option, - pub kind: SearchSchemaKind, -} - -impl SearchSchema { - pub(crate) fn num_args(&self) -> usize { - 2 + named_opt_args(&self.alias) + self.kind.num_args() - } -} - -/// Arguments to `FT.ALTER`. -#[derive(Clone, Debug)] -pub struct FtAlterOptions { - pub skipinitialscan: bool, - pub attribute: Str, - pub options: SearchSchemaKind, -} - -impl FtAlterOptions { - pub(crate) fn num_args(&self) -> usize { - 3 + bool_args(self.skipinitialscan) + self.options.num_args() - } -} - -/// Arguments to `TERMS` in `FT.SPELLCHECK`, -#[derive(Clone, Debug)] -pub enum SpellcheckTerms { - Include { dictionary: Str, terms: Vec }, - Exclude { dictionary: Str, terms: Vec }, -} - -impl SpellcheckTerms { - pub(crate) fn num_args(&self) -> usize { - 3 + match self { - SpellcheckTerms::Include { terms, .. } => terms.len(), - SpellcheckTerms::Exclude { terms, .. } => terms.len(), - } - } -} diff --git a/src/types/scan.rs b/src/types/scan.rs deleted file mode 100644 index f5d4f62f..00000000 --- a/src/types/scan.rs +++ /dev/null @@ -1,260 +0,0 @@ -use crate::{ - clients::RedisClient, - error::RedisError, - interfaces, - modules::inner::RedisClientInner, - protocol::{ - command::{RedisCommand, RedisCommandKind}, - responders::ResponseKind, - types::{KeyScanInner, ValueScanInner}, - }, - runtime::RefCount, - types::{RedisKey, RedisMap, RedisValue}, - utils, -}; -use bytes_utils::Str; -use std::borrow::Cow; - -/// The types of values supported by the [type](https://redis.io/commands/type) command. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ScanType { - Set, - String, - ZSet, - List, - Hash, - Stream, -} - -impl ScanType { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - ScanType::Set => "set", - ScanType::String => "string", - ScanType::List => "list", - ScanType::ZSet => "zset", - ScanType::Hash => "hash", - ScanType::Stream => "stream", - }) - } -} - -/// An interface for interacting with the results of a scan operation. -pub trait Scanner { - /// The type of results from the scan operation. - type Page; - - /// Read the cursor returned from the last scan operation. - fn cursor(&self) -> Option>; - - /// Whether the scan call will continue returning results. If `false` this will be the last result set - /// returned on the stream. - /// - /// Calling `next` when this returns `false` will return `Ok(())`, so this does not need to be checked on each - /// result. - fn has_more(&self) -> bool; - - /// Return a reference to the last page of results. - fn results(&self) -> &Option; - - /// Take ownership over the results of the SCAN operation. Calls to `results` or `take_results` will return `None` - /// afterwards. - fn take_results(&mut self) -> Option; - - /// A lightweight function to create a Redis client from the SCAN result. - /// - /// To continue scanning the caller should call `next` on this struct. Calling `scan` again on the client will - /// initiate a new SCAN call starting with a cursor of 0. - fn create_client(&self) -> RedisClient; - - /// Move on to the next page of results from the SCAN operation. If no more results are available this may close the - /// stream. - /// - /// **This must be called to continue scanning the keyspace.** Results are not automatically scanned in the - /// background since this could cause the buffer backing the stream to grow too large very quickly. This - /// interface provides a mechanism for throttling the throughput of the SCAN call. If this struct is dropped - /// without calling this function the stream will close without an error. - /// - /// If this function returns an error the scan call cannot continue as the client has been closed, or some other - /// fatal error has occurred. If this happens the error will appear in the stream from the original SCAN call. - fn next(self) -> Result<(), RedisError>; -} - -/// The result of a SCAN operation. -pub struct ScanResult { - pub(crate) results: Option>, - pub(crate) inner: RefCount, - pub(crate) scan_state: KeyScanInner, - pub(crate) can_continue: bool, -} - -impl Scanner for ScanResult { - type Page = Vec; - - fn cursor(&self) -> Option> { - self.scan_state.args[self.scan_state.cursor_idx].as_str() - } - - fn has_more(&self) -> bool { - self.can_continue - } - - fn results(&self) -> &Option { - &self.results - } - - fn take_results(&mut self) -> Option { - self.results.take() - } - - fn create_client(&self) -> RedisClient { - RedisClient { - inner: self.inner.clone(), - } - } - - fn next(self) -> Result<(), RedisError> { - if !self.can_continue { - return Ok(()); - } - - let cluster_node = self.scan_state.server.clone(); - let response = ResponseKind::KeyScan(self.scan_state); - let mut cmd: RedisCommand = (RedisCommandKind::Scan, Vec::new(), response).into(); - cmd.cluster_node = cluster_node; - - interfaces::default_send_command(&self.inner, cmd) - } -} - -/// The result of a HSCAN operation. -pub struct HScanResult { - pub(crate) results: Option, - pub(crate) inner: RefCount, - pub(crate) scan_state: ValueScanInner, - pub(crate) can_continue: bool, -} - -impl Scanner for HScanResult { - type Page = RedisMap; - - fn cursor(&self) -> Option> { - self.scan_state.args[self.scan_state.cursor_idx].as_str() - } - - fn has_more(&self) -> bool { - self.can_continue - } - - fn results(&self) -> &Option { - &self.results - } - - fn take_results(&mut self) -> Option { - self.results.take() - } - - fn create_client(&self) -> RedisClient { - RedisClient { - inner: self.inner.clone(), - } - } - - fn next(self) -> Result<(), RedisError> { - if !self.can_continue { - return Ok(()); - } - - let response = ResponseKind::ValueScan(self.scan_state); - let cmd: RedisCommand = (RedisCommandKind::Hscan, Vec::new(), response).into(); - interfaces::default_send_command(&self.inner, cmd) - } -} - -/// The result of a SSCAN operation. -pub struct SScanResult { - pub(crate) results: Option>, - pub(crate) inner: RefCount, - pub(crate) scan_state: ValueScanInner, - pub(crate) can_continue: bool, -} - -impl Scanner for SScanResult { - type Page = Vec; - - fn cursor(&self) -> Option> { - self.scan_state.args[self.scan_state.cursor_idx].as_str() - } - - fn results(&self) -> &Option { - &self.results - } - - fn take_results(&mut self) -> Option { - self.results.take() - } - - fn has_more(&self) -> bool { - self.can_continue - } - - fn create_client(&self) -> RedisClient { - RedisClient { - inner: self.inner.clone(), - } - } - - fn next(self) -> Result<(), RedisError> { - if !self.can_continue { - return Ok(()); - } - - let response = ResponseKind::ValueScan(self.scan_state); - let cmd: RedisCommand = (RedisCommandKind::Sscan, Vec::new(), response).into(); - interfaces::default_send_command(&self.inner, cmd) - } -} - -/// The result of a ZSCAN operation. -pub struct ZScanResult { - pub(crate) results: Option>, - pub(crate) inner: RefCount, - pub(crate) scan_state: ValueScanInner, - pub(crate) can_continue: bool, -} - -impl Scanner for ZScanResult { - type Page = Vec<(RedisValue, f64)>; - - fn cursor(&self) -> Option> { - self.scan_state.args[self.scan_state.cursor_idx].as_str() - } - - fn has_more(&self) -> bool { - self.can_continue - } - - fn results(&self) -> &Option { - &self.results - } - - fn take_results(&mut self) -> Option { - self.results.take() - } - - fn create_client(&self) -> RedisClient { - RedisClient { - inner: self.inner.clone(), - } - } - - fn next(self) -> Result<(), RedisError> { - if !self.can_continue { - return Ok(()); - } - - let response = ResponseKind::ValueScan(self.scan_state); - let cmd: RedisCommand = (RedisCommandKind::Zscan, Vec::new(), response).into(); - interfaces::default_send_command(&self.inner, cmd) - } -} diff --git a/src/types/scripts.rs b/src/types/scripts.rs deleted file mode 100644 index d85f881e..00000000 --- a/src/types/scripts.rs +++ /dev/null @@ -1,351 +0,0 @@ -use crate::{ - clients::RedisClient, - interfaces::{FunctionInterface, LuaInterface}, - prelude::{FromRedis, RedisError, RedisResult}, - types::{MultipleKeys, MultipleValues, RedisValue}, -}; -#[cfg(feature = "sha-1")] -use crate::{prelude::RedisErrorKind, util::sha1_hash}; -use bytes_utils::Str; -use std::{ - cmp::Ordering, - collections::HashMap, - convert::TryInto, - fmt, - fmt::Formatter, - hash::{Hash, Hasher}, - ops::Deref, -}; - -/// An interface for caching and running lua scripts. -/// -/// ```rust no_run -/// # use fred::types::Script; -/// # use fred::prelude::*; -/// async fn example(client: &RedisClient) -> Result<(), RedisError> { -/// let script = Script::from_lua("return ARGV[1]"); -/// assert_eq!(script.sha1(), "098e0f0d1448c0a81dafe820f66d460eb09263da"); -/// -/// let _ = script.load(client).await?; -/// let result: String = script.evalsha(client, "key", "arg").await?; -/// assert_eq!(result, "arg"); -/// Ok(()) -/// } -/// ``` -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Script { - lua: Option, - hash: Str, -} - -impl fmt::Display for Script { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.hash) - } -} - -impl Hash for Script { - fn hash(&self, state: &mut H) { - self.hash.hash(state); - } -} - -impl Ord for Script { - fn cmp(&self, other: &Self) -> Ordering { - self.hash.cmp(&other.hash) - } -} - -impl PartialOrd for Script { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Script { - /// Create a new `Script` from a lua script. - #[cfg(feature = "sha-1")] - #[cfg_attr(docsrs, doc(cfg(feature = "sha-1")))] - pub fn from_lua>(lua: S) -> Self { - let lua: Str = lua.into(); - let hash = Str::from(sha1_hash(&lua)); - - Script { lua: Some(lua), hash } - } - - /// Create a new `Script` from a lua hash. - pub fn from_hash>(hash: S) -> Self { - Script { - lua: None, - hash: hash.into(), - } - } - - /// Read the lua script contents. - pub fn lua(&self) -> Option<&Str> { - self.lua.as_ref() - } - - /// Read the SHA-1 hash for the script. - pub fn sha1(&self) -> &Str { - &self.hash - } - - /// Call `SCRIPT LOAD` on all the associated servers. This must be - /// called once before calling [evalsha](Self::evalsha). - #[cfg(feature = "sha-1")] - #[cfg_attr(docsrs, doc(cfg(feature = "sha-1")))] - pub async fn load(&self, client: &RedisClient) -> RedisResult<()> { - if let Some(ref lua) = self.lua { - client.script_load_cluster::<(), _>(lua.clone()).await - } else { - Err(RedisError::new(RedisErrorKind::Unknown, "Missing lua script contents.")) - } - } - - /// Send `EVALSHA` to the server with the provided arguments. - pub async fn evalsha(&self, client: &C, keys: K, args: V) -> RedisResult - where - R: FromRedis, - C: LuaInterface + Send + Sync, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - client.evalsha(self.hash.clone(), keys, args).await - } - - /// Send `EVALSHA` to the server with the provided arguments. Automatically `SCRIPT LOAD` in case - /// of `NOSCRIPT` error and try `EVALSHA` again. - #[cfg(feature = "sha-1")] - #[cfg_attr(docsrs, doc(cfg(feature = "sha-1")))] - pub async fn evalsha_with_reload(&self, client: &RedisClient, keys: K, args: V) -> RedisResult - where - R: FromRedis, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - into!(keys); - try_into!(args); - - match client.evalsha(self.hash.clone(), keys.clone(), args.clone()).await { - Err(error) if error.details().starts_with("NOSCRIPT") => { - self.load(client).await?; - client.evalsha(self.hash.clone(), keys, args).await - }, - result => result, - } - } -} - -/// Possible [flags](https://redis.io/docs/manual/programmability/lua-api/) associated with a [Function](crate::types::Function). -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub enum FunctionFlag { - NoWrites, - AllowOOM, - NoCluster, - AllowCrossSlotKeys, - AllowStale, -} - -impl FunctionFlag { - /// Parse the string representation of the flag. - #[allow(clippy::should_implement_trait)] - pub fn from_str(s: &str) -> Option { - Some(match s { - "allow-oom" => FunctionFlag::AllowOOM, - "allow-stale" => FunctionFlag::AllowStale, - "allow-cross-slot-keys" => FunctionFlag::AllowCrossSlotKeys, - "no-writes" => FunctionFlag::NoWrites, - "no-cluster" => FunctionFlag::NoCluster, - _ => return None, - }) - } - - /// Convert to the string representation of the flag. - pub fn to_str(&self) -> &'static str { - match self { - FunctionFlag::AllowCrossSlotKeys => "allow-cross-slot-keys", - FunctionFlag::AllowOOM => "allow-oom", - FunctionFlag::NoCluster => "no-cluster", - FunctionFlag::NoWrites => "no-writes", - FunctionFlag::AllowStale => "allow-stale", - } - } -} - -/// An individual function within a [Library](crate::types::Library). -/// -/// See the [library documentation](crate::types::Library) for more information. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Function { - pub(crate) name: Str, - pub(crate) flags: Vec, -} - -impl fmt::Display for Function { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.name) - } -} - -impl Hash for Function { - fn hash(&self, state: &mut H) { - self.name.hash(state); - } -} - -impl Ord for Function { - fn cmp(&self, other: &Self) -> Ordering { - self.name.cmp(&other.name) - } -} - -impl PartialOrd for Function { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Function { - /// Create a new `Function`. - pub fn new>(name: S, flags: Vec) -> Self { - Function { - name: name.into(), - flags, - } - } - - /// Read the name of the function. - pub fn name(&self) -> &Str { - &self.name - } - - /// Read the flags associated with the function. - pub fn flags(&self) -> &[FunctionFlag] { - &self.flags - } - - /// Send the [fcall](crate::interfaces::FunctionInterface::fcall) command via the provided client. - pub async fn fcall(&self, client: &C, keys: K, args: V) -> RedisResult - where - R: FromRedis, - C: FunctionInterface + Send + Sync, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - client.fcall(self.name.clone(), keys, args).await - } - - /// Send the [fcall_ro](crate::interfaces::FunctionInterface::fcall_ro) command via the provided client. - pub async fn fcall_ro(&self, client: &C, keys: K, args: V) -> RedisResult - where - R: FromRedis, - C: FunctionInterface + Send + Sync, - K: Into + Send, - V: TryInto + Send, - V::Error: Into + Send, - { - client.fcall_ro(self.name.clone(), keys, args).await - } -} - -/// A helper struct for interacting with [libraries and functions](https://redis.io/docs/manual/programmability/functions-intro/). -/// -/// ```rust no_run -/// # use fred::types::{FunctionFlag, Library}; -/// let code = "#!lua name=mylib \n redis.register_function('myfunc', function(keys, args) return \ -/// args[1] end)"; -/// let library = Library::from_code(client, code).await?; -/// assert_eq!(library.name(), "mylib"); -/// -/// if let Some(func) = library.functions().get("myfunc") { -/// if func.flags().contains(&FunctionFlag::NoWrites) { -/// let _: () = func.fcall_ro(client, "key", "arg").await?; -/// } else { -/// let _: () = func.fcall(client, "key", "arg").await?; -/// } -/// } -/// ``` -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Library { - name: Str, - functions: HashMap, -} - -impl fmt::Display for Library { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.name) - } -} - -impl Hash for Library { - fn hash(&self, state: &mut H) { - self.name.hash(state); - } -} - -impl Ord for Library { - fn cmp(&self, other: &Self) -> Ordering { - self.name.cmp(&other.name) - } -} - -impl PartialOrd for Library { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Library { - /// Create a new `Library` with the provided code, loading it on all the servers and inspecting the contents via the [FUNCTION LIST](https://redis.io/commands/function-list/) command. - /// - /// This interface will load the library on the server. - pub async fn from_code(client: &RedisClient, code: S) -> Result - where - S: Into, - { - let code = code.into(); - let name: Str = client.function_load_cluster(true, code).await?; - let functions = client - .function_list::(Some(name.deref()), false) - .await? - .as_functions(&name)?; - - Ok(Library { - name, - functions: functions.into_iter().map(|f| (f.name.clone(), f)).collect(), - }) - } - - /// Create a new `Library` with the associated name, inspecting the library contents via the [FUNCTION LIST](https://redis.io/commands/function-list/) command. - /// - /// This interface assumes the library is already loaded on the server. - pub async fn from_name(client: &RedisClient, name: S) -> Result - where - S: Into, - { - let name = name.into(); - let functions = client - .function_list::(Some(name.deref()), false) - .await? - .as_functions(&name)?; - - Ok(Library { - name, - functions: functions.into_iter().map(|f| (f.name.clone(), f)).collect(), - }) - } - - /// Read the name of the library. - pub fn name(&self) -> &Str { - &self.name - } - - /// Read the functions contained within this library. - pub fn functions(&self) -> &HashMap { - &self.functions - } -} diff --git a/src/types/sorted_sets.rs b/src/types/sorted_sets.rs deleted file mode 100644 index 0bc20320..00000000 --- a/src/types/sorted_sets.rs +++ /dev/null @@ -1,370 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - types::RedisValue, - utils, -}; -use bytes_utils::Str; -use std::{ - collections::VecDeque, - convert::{TryFrom, TryInto}, - iter::FromIterator, -}; - -/// `MIN|MAX` arguments for `BZMPOP`, etc. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ZCmp { - Min, - Max, -} - -impl ZCmp { - pub(crate) fn to_str(&self) -> &'static str { - match self { - ZCmp::Min => "MIN", - ZCmp::Max => "MAX", - } - } -} - -/// Convenience struct for `ZINTERSTORE` and `ZUNIONSTORE` when accepting 1 or more `weights` arguments. -pub struct MultipleWeights { - values: Vec, -} - -impl MultipleWeights { - pub fn new() -> MultipleWeights { - MultipleWeights { values: Vec::new() } - } - - pub fn inner(self) -> Vec { - self.values - } - - pub fn len(&self) -> usize { - self.values.len() - } -} - -impl From> for MultipleWeights { - fn from(d: Option) -> Self { - match d { - Some(w) => w.into(), - None => MultipleWeights::new(), - } - } -} - -impl From for MultipleWeights { - fn from(d: f64) -> Self { - MultipleWeights { values: vec![d] } - } -} - -impl FromIterator for MultipleWeights { - fn from_iter>(iter: I) -> Self { - MultipleWeights { - values: iter.into_iter().collect(), - } - } -} - -impl From> for MultipleWeights { - fn from(d: Vec) -> Self { - MultipleWeights { values: d } - } -} - -impl From> for MultipleWeights { - fn from(d: VecDeque) -> Self { - MultipleWeights { - values: d.into_iter().collect(), - } - } -} - -/// Convenience struct for the `ZADD` command to accept 1 or more `(score, value)` arguments. -pub struct MultipleZaddValues { - values: Vec<(f64, RedisValue)>, -} - -impl MultipleZaddValues { - pub fn new() -> MultipleZaddValues { - MultipleZaddValues { values: Vec::new() } - } - - pub fn inner(self) -> Vec<(f64, RedisValue)> { - self.values - } - - pub fn len(&self) -> usize { - self.values.len() - } -} - -impl TryFrom<(f64, T)> for MultipleZaddValues -where - T: TryInto, - T::Error: Into, -{ - type Error = RedisError; - - fn try_from((f, d): (f64, T)) -> Result { - Ok(MultipleZaddValues { - values: vec![(f, to!(d)?)], - }) - } -} - -impl FromIterator<(f64, T)> for MultipleZaddValues -where - T: Into, -{ - fn from_iter>(iter: I) -> Self { - MultipleZaddValues { - values: iter.into_iter().map(|(f, d)| (f, d.into())).collect(), - } - } -} - -impl TryFrom> for MultipleZaddValues -where - T: TryInto, - T::Error: Into, -{ - type Error = RedisError; - - fn try_from(d: Vec<(f64, T)>) -> Result { - let mut values = Vec::with_capacity(d.len()); - for (f, v) in d.into_iter() { - values.push((f, to!(v)?)); - } - - Ok(MultipleZaddValues { values }) - } -} - -impl TryFrom> for MultipleZaddValues -where - T: TryInto, - T::Error: Into, -{ - type Error = RedisError; - - fn try_from(d: VecDeque<(f64, T)>) -> Result { - let mut values = Vec::with_capacity(d.len()); - for (f, v) in d.into_iter() { - values.push((f, to!(v)?)); - } - - Ok(MultipleZaddValues { values }) - } -} - -/// Ordering options for the ZADD (and related) commands. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Ordering { - GreaterThan, - LessThan, -} - -impl Ordering { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - Ordering::GreaterThan => "GT", - Ordering::LessThan => "LT", - }) - } -} - -/// Options for the ZRANGE (and related) commands. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ZSort { - ByScore, - ByLex, -} - -impl ZSort { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - ZSort::ByScore => "BYSCORE", - ZSort::ByLex => "BYLEX", - }) - } -} - -/// An index, score, lexicographical, or +|-|+inf|-inf range bound for the ZRANGE command. -#[derive(Clone, Debug)] -pub enum ZRangeBound { - /// Index ranges () - Index(i64), - /// Score ranges () - Score(f64), - /// Lexicographical ranges () - Lex(String), - /// Shortcut for the `+` character. - InfiniteLex, - /// Shortcut for the `-` character. - NegInfinityLex, - /// Shortcut for the `+inf` range bound. - InfiniteScore, - /// Shortcut for the `-inf` range bound. - NegInfiniteScore, -} - -impl From for ZRangeBound { - fn from(i: i64) -> Self { - ZRangeBound::Index(i) - } -} - -impl<'a> From<&'a str> for ZRangeBound { - fn from(s: &'a str) -> Self { - if s == "+inf" { - ZRangeBound::InfiniteScore - } else if s == "-inf" { - ZRangeBound::NegInfiniteScore - } else { - ZRangeBound::Lex(s.to_owned()) - } - } -} - -impl From for ZRangeBound { - fn from(s: String) -> Self { - if s == "+inf" { - ZRangeBound::InfiniteScore - } else if s == "-inf" { - ZRangeBound::NegInfiniteScore - } else { - ZRangeBound::Lex(s) - } - } -} - -impl<'a> From<&'a String> for ZRangeBound { - fn from(s: &'a String) -> Self { - s.as_str().into() - } -} - -impl TryFrom for ZRangeBound { - type Error = RedisError; - - fn try_from(f: f64) -> Result { - let value = if f.is_infinite() && f.is_sign_negative() { - ZRangeBound::NegInfiniteScore - } else if f.is_infinite() { - ZRangeBound::InfiniteScore - } else if f.is_nan() { - return Err(RedisError::new( - RedisErrorKind::Unknown, - "Cannot use NaN as zrange field.", - )); - } else { - ZRangeBound::Score(f) - }; - - Ok(value) - } -} - -/// The type of range interval bound. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ZRangeKind { - Inclusive, - Exclusive, -} - -impl Default for ZRangeKind { - fn default() -> Self { - ZRangeKind::Inclusive - } -} - -/// A wrapper struct for a range bound in a sorted set command. -#[derive(Clone, Debug)] -pub struct ZRange { - pub kind: ZRangeKind, - pub range: ZRangeBound, -} - -impl ZRange { - pub(crate) fn into_value(self) -> Result { - let value = if self.kind == ZRangeKind::Exclusive { - match self.range { - ZRangeBound::Index(i) => format!("({}", i).into(), - ZRangeBound::Score(f) => utils::f64_to_zrange_bound(f, &self.kind)?.into(), - ZRangeBound::Lex(s) => utils::check_lex_str(s, &self.kind).into(), - ZRangeBound::InfiniteLex => RedisValue::from_static_str("+"), - ZRangeBound::NegInfinityLex => RedisValue::from_static_str("-"), - ZRangeBound::InfiniteScore => RedisValue::from_static_str("+inf"), - ZRangeBound::NegInfiniteScore => RedisValue::from_static_str("-inf"), - } - } else { - match self.range { - ZRangeBound::Index(i) => i.into(), - ZRangeBound::Score(f) => f.try_into()?, - ZRangeBound::Lex(s) => utils::check_lex_str(s, &self.kind).into(), - ZRangeBound::InfiniteLex => RedisValue::from_static_str("+"), - ZRangeBound::NegInfinityLex => RedisValue::from_static_str("-"), - ZRangeBound::InfiniteScore => RedisValue::from_static_str("+inf"), - ZRangeBound::NegInfiniteScore => RedisValue::from_static_str("-inf"), - } - }; - - Ok(value) - } -} - -impl From for ZRange { - fn from(i: i64) -> Self { - ZRange { - kind: ZRangeKind::default(), - range: i.into(), - } - } -} - -impl<'a> From<&'a str> for ZRange { - fn from(s: &'a str) -> Self { - ZRange { - kind: ZRangeKind::default(), - range: s.into(), - } - } -} - -impl From for ZRange { - fn from(s: String) -> Self { - ZRange { - kind: ZRangeKind::default(), - range: s.into(), - } - } -} - -impl<'a> From<&'a String> for ZRange { - fn from(s: &'a String) -> Self { - ZRange { - kind: ZRangeKind::default(), - range: s.as_str().into(), - } - } -} - -impl TryFrom for ZRange { - type Error = RedisError; - - fn try_from(f: f64) -> Result { - Ok(ZRange { - kind: ZRangeKind::default(), - range: f.try_into()?, - }) - } -} - -impl<'a> From<&'a ZRange> for ZRange { - fn from(range: &'a ZRange) -> Self { - range.clone() - } -} diff --git a/src/types/streams.rs b/src/types/streams.rs deleted file mode 100644 index f464495e..00000000 --- a/src/types/streams.rs +++ /dev/null @@ -1,510 +0,0 @@ -use crate::{ - commands::{MAXLEN, MINID}, - error::{RedisError, RedisErrorKind}, - types::{LimitCount, RedisKey, RedisValue, StringOrNumber}, - utils, -}; -use bytes_utils::Str; -use std::{ - collections::{HashMap, VecDeque}, - convert::{TryFrom, TryInto}, -}; - -/// Representation for the "=" or "~" operator in `XADD`, etc. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum XCapTrim { - Exact, - AlmostExact, -} - -impl XCapTrim { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - XCapTrim::Exact => "=", - XCapTrim::AlmostExact => "~", - }) - } -} - -impl<'a> TryFrom<&'a str> for XCapTrim { - type Error = RedisError; - - fn try_from(s: &'a str) -> Result { - Ok(match s { - "=" => XCapTrim::Exact, - "~" => XCapTrim::AlmostExact, - _ => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Invalid XADD trim value.", - )) - }, - }) - } -} - -/// One or more ordered key-value pairs, typically used as an argument for `XADD`. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct MultipleOrderedPairs { - values: Vec<(RedisKey, RedisValue)>, -} - -impl MultipleOrderedPairs { - pub fn len(&self) -> usize { - self.values.len() - } - - pub fn inner(self) -> Vec<(RedisKey, RedisValue)> { - self.values - } -} - -impl From<()> for MultipleOrderedPairs { - fn from(_: ()) -> Self { - MultipleOrderedPairs { values: Vec::new() } - } -} - -impl TryFrom<(K, V)> for MultipleOrderedPairs -where - K: Into, - V: TryInto, - V::Error: Into, -{ - type Error = RedisError; - - fn try_from((key, value): (K, V)) -> Result { - Ok(MultipleOrderedPairs { - values: vec![(key.into(), to!(value)?)], - }) - } -} - -impl TryFrom> for MultipleOrderedPairs -where - K: Into, - V: TryInto, - V::Error: Into, -{ - type Error = RedisError; - - fn try_from(values: Vec<(K, V)>) -> Result { - Ok(MultipleOrderedPairs { - values: values - .into_iter() - .map(|(key, value)| Ok((key.into(), to!(value)?))) - .collect::, RedisError>>()?, - }) - } -} - -impl TryFrom> for MultipleOrderedPairs -where - K: Into, - V: TryInto, - V::Error: Into, -{ - type Error = RedisError; - - fn try_from(values: VecDeque<(K, V)>) -> Result { - Ok(MultipleOrderedPairs { - values: values - .into_iter() - .map(|(key, value)| Ok((key.into(), to!(value)?))) - .collect::, RedisError>>()?, - }) - } -} - -impl TryFrom> for MultipleOrderedPairs -where - K: Into, - V: TryInto, - V::Error: Into, -{ - type Error = RedisError; - - fn try_from(values: HashMap) -> Result { - Ok(MultipleOrderedPairs { - values: values - .into_iter() - .map(|(key, value)| Ok((key.into(), to!(value)?))) - .collect::, RedisError>>()?, - }) - } -} - -/// One or more IDs for elements in a stream. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct MultipleIDs { - inner: Vec, -} - -impl MultipleIDs { - pub fn len(&self) -> usize { - self.inner.len() - } - - pub fn inner(self) -> Vec { - self.inner - } -} - -impl From for MultipleIDs -where - T: Into, -{ - fn from(value: T) -> Self { - MultipleIDs { - inner: vec![value.into()], - } - } -} - -impl From> for MultipleIDs -where - T: Into, -{ - fn from(value: Vec) -> Self { - MultipleIDs { - inner: value.into_iter().map(|value| value.into()).collect(), - } - } -} - -impl From> for MultipleIDs -where - T: Into, -{ - fn from(value: VecDeque) -> Self { - MultipleIDs { - inner: value.into_iter().map(|value| value.into()).collect(), - } - } -} - -/// The MAXLEN or MINID argument for a stream cap. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum XCapKind { - MaxLen, - MinID, -} - -impl XCapKind { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - XCapKind::MaxLen => MAXLEN, - XCapKind::MinID => MINID, - }) - } -} - -impl<'a> TryFrom<&'a str> for XCapKind { - type Error = RedisError; - - fn try_from(value: &'a str) -> Result { - Ok(match value { - "MAXLEN" => XCapKind::MaxLen, - "MINID" => XCapKind::MinID, - _ => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Expected MAXLEN or MINID,", - )) - }, - }) - } -} - -/// Stream cap arguments for `XADD`, `XTRIM`, etc. -/// -/// Equivalent to `[MAXLEN|MINID [=|~] threshold [LIMIT count]]`. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct XCap { - inner: Option<(XCapKind, XCapTrim, StringOrNumber, LimitCount)>, -} - -impl XCap { - pub(crate) fn into_parts(self) -> Option<(XCapKind, XCapTrim, StringOrNumber, LimitCount)> { - self.inner - } -} - -impl From> for XCap { - fn from(_: Option<()>) -> Self { - XCap { inner: None } - } -} - -impl TryFrom<(K, T, S, Option)> for XCap -where - K: TryInto, - K::Error: Into, - T: TryInto, - T::Error: Into, - S: Into, -{ - type Error = RedisError; - - fn try_from((kind, trim, threshold, limit): (K, T, S, Option)) -> Result { - let (kind, trim) = (to!(kind)?, to!(trim)?); - Ok(XCap { - inner: Some((kind, trim, threshold.into(), limit)), - }) - } -} - -impl TryFrom<(K, T, S)> for XCap -where - K: TryInto, - K::Error: Into, - T: TryInto, - T::Error: Into, - S: Into, -{ - type Error = RedisError; - - fn try_from((kind, trim, threshold): (K, T, S)) -> Result { - let (kind, trim) = (to!(kind)?, to!(trim)?); - Ok(XCap { - inner: Some((kind, trim, threshold.into(), None)), - }) - } -} - -impl TryFrom<(K, S)> for XCap -where - K: TryInto, - K::Error: Into, - S: Into, -{ - type Error = RedisError; - - fn try_from((kind, threshold): (K, S)) -> Result { - let kind = to!(kind)?; - Ok(XCap { - inner: Some((kind, XCapTrim::Exact, threshold.into(), None)), - }) - } -} - -/// Stream ID arguments for `XADD`, `XREAD`, etc. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum XID { - /// The auto-generated key symbol "*". - Auto, - /// An ID specified by the user such as "12345-0". - Manual(Str), - /// The highest ID in a stream ("$"). - Max, - /// For `XREADGROUP`, only return new IDs (">"). - NewInGroup, -} - -impl XID { - pub(crate) fn into_str(self) -> Str { - match self { - XID::Auto => utils::static_str("*"), - XID::Max => utils::static_str("$"), - XID::NewInGroup => utils::static_str(">"), - XID::Manual(s) => s, - } - } -} - -impl<'a> From<&'a str> for XID { - fn from(value: &'a str) -> Self { - match value { - "*" => XID::Auto, - "$" => XID::Max, - ">" => XID::NewInGroup, - _ => XID::Manual(value.into()), - } - } -} - -impl<'a> From<&'a String> for XID { - fn from(value: &'a String) -> Self { - match value.as_ref() { - "*" => XID::Auto, - "$" => XID::Max, - ">" => XID::NewInGroup, - _ => XID::Manual(value.into()), - } - } -} - -impl From for XID { - fn from(value: String) -> Self { - match value.as_ref() { - "*" => XID::Auto, - "$" => XID::Max, - ">" => XID::NewInGroup, - _ => XID::Manual(value.into()), - } - } -} - -impl From for XID { - fn from(value: Str) -> Self { - match &*value { - "*" => XID::Auto, - "$" => XID::Max, - ">" => XID::NewInGroup, - _ => XID::Manual(value), - } - } -} - -/// A struct representing the trailing optional arguments to [XPENDING](https://redis.io/commands/xpending). -/// -/// See the `From` implementations for various shorthand representations of these arguments. Callers should use `()` -/// to represent no arguments. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct XPendingArgs { - pub idle: Option, - pub start: Option, - pub end: Option, - pub count: Option, - pub consumer: Option, -} - -impl XPendingArgs { - pub(crate) fn into_parts(self) -> Result, XID, XID, u64, Option)>, RedisError> { - let is_empty = self.idle.is_none() - && self.start.is_none() - && self.end.is_none() - && self.count.is_none() - && self.consumer.is_none(); - - if is_empty { - Ok(None) - } else { - let start = match self.start { - Some(s) => s, - None => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "The `start` argument is required in this context.", - )) - }, - }; - let end = match self.end { - Some(s) => s, - None => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "The `end` argument is required in this context.", - )) - }, - }; - let count = match self.count { - Some(s) => s, - None => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "The `count` argument is required in this context.", - )) - }, - }; - - Ok(Some((self.idle, start, end, count, self.consumer))) - } - } -} - -impl From<()> for XPendingArgs { - fn from(_: ()) -> Self { - XPendingArgs { - idle: None, - start: None, - end: None, - count: None, - consumer: None, - } - } -} - -impl From<(S, E, u64)> for XPendingArgs -where - S: Into, - E: Into, -{ - fn from((start, end, count): (S, E, u64)) -> Self { - XPendingArgs { - idle: None, - start: Some(start.into()), - end: Some(end.into()), - count: Some(count), - consumer: None, - } - } -} - -impl From<(S, E, u64, C)> for XPendingArgs -where - S: Into, - E: Into, - C: Into, -{ - fn from((start, end, count, consumer): (S, E, u64, C)) -> Self { - XPendingArgs { - idle: None, - start: Some(start.into()), - end: Some(end.into()), - count: Some(count), - consumer: Some(consumer.into()), - } - } -} - -impl From<(u64, S, E, u64)> for XPendingArgs -where - S: Into, - E: Into, -{ - fn from((idle, start, end, count): (u64, S, E, u64)) -> Self { - XPendingArgs { - idle: Some(idle), - start: Some(start.into()), - end: Some(end.into()), - count: Some(count), - consumer: None, - } - } -} - -impl From<(u64, S, E, u64, C)> for XPendingArgs -where - S: Into, - E: Into, - C: Into, -{ - fn from((idle, start, end, count, consumer): (u64, S, E, u64, C)) -> Self { - XPendingArgs { - idle: Some(idle), - start: Some(start.into()), - end: Some(end.into()), - count: Some(count), - consumer: Some(consumer.into()), - } - } -} - -/// A generic helper type describing the ID and associated map for each record in a stream. -/// -/// See the [XReadResponse](crate::types::XReadResponse) type for more information. -pub type XReadValue = (I, HashMap); -/// A generic helper type describing the top level response from `XREAD` or `XREADGROUP`. -/// -/// See the [xread](crate::interfaces::StreamsInterface::xread) documentation for more information. -/// -/// The inner type declarations refer to the following: -/// * K1 - The type of the outer Redis key for the stream. Usually a `String` or `RedisKey`. -/// * I - The type of the ID for a stream record ("abc-123"). This is usually a `String`. -/// * K2 - The type of key in the map associated with each stream record. -/// * V - The type of value in the map associated with each stream record. -/// -/// To support heterogeneous values in the map describing each stream element it is recommended to declare the last -/// type as `RedisValue` and [convert](crate::types::RedisValue::convert) as needed. -pub type XReadResponse = HashMap>>; diff --git a/src/types/timeseries.rs b/src/types/timeseries.rs deleted file mode 100644 index 0029c063..00000000 --- a/src/types/timeseries.rs +++ /dev/null @@ -1,449 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - types::RedisValue, - utils, -}; -use bytes_utils::Str; -use std::collections::HashMap; - -/// Encoding arguments for certain timeseries commands. -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Encoding { - Compressed, - Uncompressed, -} - -impl Encoding { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - Encoding::Compressed => "COMPRESSED", - Encoding::Uncompressed => "UNCOMPRESSED", - }) - } -} - -/// The duplicate policy used with certain timeseries commands. -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum DuplicatePolicy { - Block, - First, - Last, - Min, - Max, - Sum, -} - -impl DuplicatePolicy { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - DuplicatePolicy::Block => "BLOCK", - DuplicatePolicy::First => "FIRST", - DuplicatePolicy::Last => "LAST", - DuplicatePolicy::Min => "MIN", - DuplicatePolicy::Max => "MAX", - DuplicatePolicy::Sum => "SUM", - }) - } -} - -/// A timestamp used in most timeseries commands. -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Timestamp { - /// Unix time (milliseconds since epoch). - Custom(i64), - /// The server's current time, equivalent to "*". - Now, -} - -impl Default for Timestamp { - fn default() -> Self { - Timestamp::Now - } -} - -impl Timestamp { - pub(crate) fn to_value(&self) -> RedisValue { - match *self { - Timestamp::Now => RedisValue::String(utils::static_str("*")), - Timestamp::Custom(v) => RedisValue::Integer(v), - } - } - - pub(crate) fn from_str(value: &str) -> Result { - match value { - "*" => Ok(Timestamp::Now), - _ => Ok(Timestamp::Custom(value.parse::()?)), - } - } -} - -impl From for Timestamp { - fn from(value: i64) -> Self { - Timestamp::Custom(value) - } -} - -impl TryFrom<&str> for Timestamp { - type Error = RedisError; - - fn try_from(value: &str) -> Result { - Self::from_str(value) - } -} - -impl TryFrom for Timestamp { - type Error = RedisError; - - fn try_from(value: Str) -> Result { - Self::from_str(&value) - } -} - -impl TryFrom for Timestamp { - type Error = RedisError; - - fn try_from(value: String) -> Result { - Self::from_str(&value) - } -} - -/// An aggregation policy to use with certain timeseries commands. -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Aggregator { - Avg, - Sum, - Min, - Max, - Range, - Count, - First, - Last, - StdP, - StdS, - VarP, - VarS, - TWA, -} - -impl Aggregator { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - Aggregator::Avg => "avg", - Aggregator::Sum => "sum", - Aggregator::Min => "min", - Aggregator::Max => "max", - Aggregator::Range => "range", - Aggregator::Count => "count", - Aggregator::First => "first", - Aggregator::Last => "last", - Aggregator::StdP => "std.p", - Aggregator::StdS => "std.s", - Aggregator::VarP => "var.p", - Aggregator::VarS => "var.s", - Aggregator::TWA => "twa", - }) - } -} - -/// Arguments equivalent to `WITHLABELS | SELECTED_LABELS label...` in various time series GET functions. -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum GetLabels { - WithLabels, - SelectedLabels(Vec), -} - -impl GetLabels { - pub(crate) fn args_len(&self) -> usize { - match *self { - GetLabels::WithLabels => 1, - GetLabels::SelectedLabels(ref s) => 1 + s.len(), - } - } -} - -impl FromIterator for GetLabels -where - S: Into, -{ - fn from_iter>(iter: I) -> Self { - GetLabels::SelectedLabels(iter.into_iter().map(|v| v.into()).collect()) - } -} - -impl From<[S; N]> for GetLabels -where - S: Into, -{ - fn from(value: [S; N]) -> Self { - GetLabels::SelectedLabels(value.into_iter().map(|v| v.into()).collect()) - } -} - -impl From> for GetLabels -where - S: Into, -{ - fn from(value: Vec) -> Self { - GetLabels::SelectedLabels(value.into_iter().map(|v| v.into()).collect()) - } -} - -/// A timestamp query used in commands such as `TS.MRANGE`. -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum GetTimestamp { - /// Equivalent to `-`. - Earliest, - /// Equivalent to `+` - Latest, - Custom(i64), -} - -impl GetTimestamp { - pub(crate) fn to_value(&self) -> RedisValue { - match *self { - GetTimestamp::Earliest => static_val!("-"), - GetTimestamp::Latest => static_val!("+"), - GetTimestamp::Custom(i) => i.into(), - } - } -} - -impl TryFrom<&str> for GetTimestamp { - type Error = RedisError; - - fn try_from(value: &str) -> Result { - Ok(match value { - "-" => GetTimestamp::Earliest, - "+" => GetTimestamp::Latest, - _ => GetTimestamp::Custom(value.parse::()?), - }) - } -} - -impl From for GetTimestamp { - fn from(value: i64) -> Self { - GetTimestamp::Custom(value) - } -} - -/// A struct representing `[ALIGN align] AGGREGATION aggregator bucketDuration [BUCKETTIMESTAMP bt] [EMPTY]` in -/// commands such as `TS.MRANGE`. -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct RangeAggregation { - pub align: Option, - pub aggregation: Aggregator, - pub bucket_duration: u64, - pub bucket_timestamp: Option, - pub empty: bool, -} - -impl From<(Aggregator, u64)> for RangeAggregation { - fn from((aggregation, duration): (Aggregator, u64)) -> Self { - RangeAggregation { - aggregation, - bucket_duration: duration, - align: None, - bucket_timestamp: None, - empty: false, - } - } -} - -/// A `REDUCER` argument in commands such as `TS.MRANGE`. -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Reducer { - Avg, - Sum, - Min, - Max, - Range, - Count, - StdP, - StdS, - VarP, - VarS, -} - -impl Reducer { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - Reducer::Avg => "avg", - Reducer::Sum => "sum", - Reducer::Min => "min", - Reducer::Max => "max", - Reducer::Range => "range", - Reducer::Count => "count", - Reducer::StdP => "std.p", - Reducer::StdS => "std.s", - Reducer::VarP => "var.p", - Reducer::VarS => "var.s", - }) - } -} - -/// A struct representing `GROUPBY label REDUCE reducer` in commands such as `TS.MRANGE`. -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct GroupBy { - pub groupby: Str, - pub reduce: Reducer, -} - -impl> From<(S, Reducer)> for GroupBy { - fn from((groupby, reduce): (S, Reducer)) -> Self { - GroupBy { - groupby: groupby.into(), - reduce, - } - } -} - -/// A `BUCKETTIMESTAMP` argument in commands such as `TS.MRANGE`. -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum BucketTimestamp { - Start, - End, - Mid, -} - -impl TryFrom<&str> for BucketTimestamp { - type Error = RedisError; - - fn try_from(value: &str) -> Result { - Ok(match value { - "-" | "start" => BucketTimestamp::Start, - "+" | "end" => BucketTimestamp::End, - "~" | "mid" => BucketTimestamp::Mid, - _ => { - return Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Invalid bucket timestamp.", - )) - }, - }) - } -} - -impl BucketTimestamp { - pub(crate) fn to_str(&self) -> Str { - utils::static_str(match *self { - BucketTimestamp::Start => "-", - BucketTimestamp::End => "+", - BucketTimestamp::Mid => "~", - }) - } -} - -/// Shorthand for the result of commands such as `MGET`, `MRANGE`, etc. -/// -/// * **K** - The key type, usually a `RedisKey`, `Str`, or `String`. -/// * **Lk** - The label key type, usually a `Str` or `String`. -/// * **Lv** - The label value type, often some kind of string type. -/// -/// The fastest/cheapest option is usually `TimeseriesValues`. -/// -/// ```rust -/// # use fred::prelude::*; -/// # use tokio::time::sleep; -/// # use std::time::Duration; -/// # use bytes_utils::Str; -/// # use fred::types::{RespVersion, GetLabels, Resp2TimeSeriesValues}; -/// async fn example(client: &RedisClient) -> Result<(), RedisError> { -/// assert_eq!(client.protocol_version(), RespVersion::RESP2); -/// -/// client -/// .ts_add("foo", "*", 1.1, None, None, None, None, ("a", "b")) -/// .await?; -/// sleep(Duration::from_millis(5)).await; -/// client -/// .ts_add("foo", "*", 2.2, None, None, None, None, ("a", "b")) -/// .await?; -/// sleep(Duration::from_millis(5)).await; -/// client -/// .ts_add("bar", "*", 3.3, None, None, None, None, ("a", "b")) -/// .await?; -/// sleep(Duration::from_millis(5)).await; -/// client -/// .ts_add("bar", "*", 4.4, None, None, None, None, ("a", "b")) -/// .await?; -/// -/// let ranges: Resp2TimeSeriesValues = client -/// .ts_mrange( -/// "-", -/// "+", -/// true, -/// [], -/// None, -/// Some(GetLabels::WithLabels), -/// None, -/// None, -/// ["a=b"], -/// None, -/// ) -/// .await?; -/// -/// for (key, labels, values) in ranges.into_iter() { -/// println!("{} [{:?}] {:?}", key.as_str_lossy(), labels, values); -/// } -/// // bar [[("a", "b")]] [(1705355605510, 3.3), (1705355605517, 4.4)] -/// // foo [[("a", "b")]] [(1705355605498, 1.1), (1705355605504, 2.2)] -/// Ok(()) -/// } -/// ``` -/// -/// See [Resp3TimeSeriesValues](crate::types::Resp3TimeSeriesValues) for the RESP3 equivalent. -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -pub type Resp2TimeSeriesValues = Vec<(K, Vec<(Lk, Lv)>, Vec<(i64, f64)>)>; - -/// The RESP3 equivalent of [Resp2TimeSeriesValues](crate::types::Resp2TimeSeriesValues). -/// -/// The timeseries interface uses slightly different type signatures in RESP3 mode. -/// -/// ```rust -/// # use fred::prelude::*; -/// # use tokio::time::sleep; -/// # use std::time::Duration; -/// # use bytes_utils::Str; -/// # use fred::types::{RespVersion, GetLabels, Resp3TimeSeriesValues}; -/// async fn example(client: &RedisClient) -> Result<(), RedisError> { -/// assert_eq!(client.protocol_version(), RespVersion::RESP3); -/// -/// client -/// .ts_add("foo", "*", 1.1, None, None, None, None, ("a", "b")) -/// .await?; -/// sleep(Duration::from_millis(5)).await; -/// client -/// .ts_add("foo", "*", 2.2, None, None, None, None, ("a", "b")) -/// .await?; -/// sleep(Duration::from_millis(5)).await; -/// client -/// .ts_add("bar", "*", 3.3, None, None, None, None, ("a", "b")) -/// .await?; -/// sleep(Duration::from_millis(5)).await; -/// client -/// .ts_add("bar", "*", 4.4, None, None, None, None, ("a", "b")) -/// .await?; -/// -/// let ranges: Resp3TimeSeriesValues = client -/// .ts_mget(false, Some(GetLabels::WithLabels), ["a=b"]) -/// .await?; -/// -/// for (key, (labels, values)) in ranges.into_iter() { -/// println!("{} [{:?}] {:?}", key.as_str_lossy(), labels, values); -/// } -/// // bar [[("a", "b")]] [(1705355605517, 4.4)] -/// // foo [[("a", "b")]] [(1705355605504, 2.2)] -/// Ok(()) -/// } -/// ``` -#[cfg_attr(docsrs, doc(cfg(feature = "i-time-series")))] -pub type Resp3TimeSeriesValues = HashMap, Vec<(i64, f64)>)>; diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 221e18dd..00000000 --- a/src/utils.rs +++ /dev/null @@ -1,971 +0,0 @@ -use crate::{ - error::{RedisError, RedisErrorKind}, - interfaces::ClientLike, - modules::inner::{CommandSender, RedisClientInner}, - protocol::{ - command::{RedisCommand, RedisCommandKind}, - responders::ResponseKind, - utils as protocol_utils, - }, - runtime::{ - broadcast_channel, - oneshot_channel, - sleep, - unbounded_channel, - AtomicBool, - AtomicUsize, - BroadcastSender, - RefCount, - RefSwap, - RwLock, - }, - types::*, -}; -use bytes::Bytes; -use bytes_utils::Str; -use float_cmp::approx_eq; -use futures::{ - future::{select, Either}, - pin_mut, - Future, - TryFutureExt, -}; -use rand::{self, distributions::Alphanumeric, Rng}; -use redis_protocol::resp3::types::BytesFrame as Resp3Frame; -use std::{collections::HashMap, convert::TryInto, f64, sync::atomic::Ordering, time::Duration}; -use url::Url; -use urlencoding::decode as percent_decode; - -#[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" -))] -use crate::protocol::tls::{TlsConfig, TlsConnector}; -#[cfg(feature = "transactions")] -use crate::runtime::Mutex; -#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))] -use crate::trace; -#[cfg(feature = "transactions")] -use std::mem; -#[cfg(feature = "unix-sockets")] -use std::path::{Path, PathBuf}; -#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))] -use tracing_futures::Instrument; - -const REDIS_TLS_SCHEME: &str = "rediss"; -const REDIS_CLUSTER_SCHEME_SUFFIX: &str = "-cluster"; -const REDIS_SENTINEL_SCHEME_SUFFIX: &str = "-sentinel"; -const SENTINEL_NAME_QUERY: &str = "sentinelServiceName"; -const CLUSTER_NODE_QUERY: &str = "node"; -#[cfg(feature = "sentinel-auth")] -const SENTINEL_USERNAME_QUERY: &str = "sentinelUsername"; -#[cfg(feature = "sentinel-auth")] -const SENTINEL_PASSWORD_QUERY: &str = "sentinelPassword"; - -/// Create a `Str` from a static str slice without copying. -pub const fn static_str(s: &'static str) -> Str { - // it's already parsed as a string - unsafe { Str::from_inner_unchecked(Bytes::from_static(s.as_bytes())) } -} - -/// Create a `Bytes` from static bytes without copying. -pub fn static_bytes(b: &'static [u8]) -> Bytes { - Bytes::from_static(b) -} - -pub fn f64_eq(lhs: f64, rhs: f64) -> bool { - approx_eq!(f64, lhs, rhs, ulps = 2) -} - -#[cfg(feature = "i-geo")] -pub fn f64_opt_eq(lhs: &Option, rhs: &Option) -> bool { - match *lhs { - Some(lhs) => match *rhs { - Some(rhs) => f64_eq(lhs, rhs), - None => false, - }, - None => rhs.is_none(), - } -} - -/// Convert a redis string to an `f64`, supporting "+inf" and "-inf". -pub fn redis_string_to_f64(s: &str) -> Result { - // this is changing in newer versions of redis to lose the "+" prefix - if s == "+inf" || s == "inf" { - Ok(f64::INFINITY) - } else if s == "-inf" { - Ok(f64::NEG_INFINITY) - } else { - s.parse::().map_err(|_| { - RedisError::new( - RedisErrorKind::Unknown, - format!("Could not convert {} to floating point value.", s), - ) - }) - } -} - -/// Convert an `f64` to a redis string, supporting "+inf" and "-inf". -pub fn f64_to_redis_string(d: f64) -> Result { - if d.is_infinite() && d.is_sign_negative() { - Ok(RedisValue::from_static_str("-inf")) - } else if d.is_infinite() { - Ok(RedisValue::from_static_str("+inf")) - } else if d.is_nan() { - Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Cannot convert NaN to redis value.", - )) - } else { - Ok(d.to_string().into()) - } -} - -#[cfg(feature = "i-sorted-sets")] -pub fn f64_to_zrange_bound(d: f64, kind: &ZRangeKind) -> Result { - if d.is_infinite() && d.is_sign_negative() { - Ok("-inf".into()) - } else if d.is_infinite() { - Ok("+inf".into()) - } else if d.is_nan() { - Err(RedisError::new( - RedisErrorKind::InvalidArgument, - "Cannot convert NaN to redis value.", - )) - } else { - Ok(match kind { - ZRangeKind::Inclusive => d.to_string(), - ZRangeKind::Exclusive => format!("({}", d), - }) - } -} - -pub fn incr_with_max(curr: u32, max: u32) -> Option { - if max != 0 && curr >= max { - None - } else { - Some(curr.saturating_add(1)) - } -} - -pub fn random_string(len: usize) -> String { - rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(len) - .map(char::from) - .collect() -} - -#[cfg(feature = "i-memory")] -pub fn convert_or_default(value: RedisValue) -> R -where - R: FromRedis + Default, -{ - value.convert().ok().unwrap_or_default() -} - -#[cfg(feature = "transactions")] -pub fn random_u64(max: u64) -> u64 { - rand::thread_rng().gen_range(0 .. max) -} - -pub fn set_client_state(state: &RwLock, new_state: ClientState) { - let mut state_guard = state.write(); - *state_guard = new_state; -} - -pub fn check_and_set_client_state( - state: &RwLock, - expected: ClientState, - new_state: ClientState, -) -> bool { - let mut state_guard = state.write(); - - if *state_guard != expected { - false - } else { - *state_guard = new_state; - true - } -} - -pub fn read_bool_atomic(val: &AtomicBool) -> bool { - val.load(Ordering::Acquire) -} - -pub fn set_bool_atomic(val: &AtomicBool, new: bool) -> bool { - val.swap(new, Ordering::SeqCst) -} - -pub fn decr_atomic(size: &AtomicUsize) -> usize { - size.fetch_sub(1, Ordering::AcqRel).saturating_sub(1) -} - -pub fn incr_atomic(size: &AtomicUsize) -> usize { - size.fetch_add(1, Ordering::AcqRel).saturating_add(1) -} - -pub fn read_atomic(size: &AtomicUsize) -> usize { - size.load(Ordering::Acquire) -} - -pub fn set_atomic(size: &AtomicUsize, val: usize) -> usize { - size.swap(val, Ordering::SeqCst) -} - -pub fn read_locked(locked: &RwLock) -> T { - locked.read().clone() -} - -#[cfg(feature = "transactions")] -pub fn read_mutex(locked: &Mutex) -> T { - locked.lock().clone() -} - -#[cfg(feature = "transactions")] -pub fn set_mutex(locked: &Mutex, value: T) -> T { - mem::replace(&mut *locked.lock(), value) -} - -#[cfg(feature = "unix-sockets")] -pub fn path_to_string(path: &Path) -> String { - path.as_os_str().to_string_lossy().to_string() -} - -#[cfg(feature = "i-sorted-sets")] -pub fn check_lex_str(val: String, kind: &ZRangeKind) -> String { - let formatted = val.starts_with('(') || val.starts_with('[') || val == "+" || val == "-"; - - if formatted { - val - } else if *kind == ZRangeKind::Exclusive { - format!("({}", val) - } else { - format!("[{}", val) - } -} - -/// Parse the response from `FUNCTION LIST`. -#[cfg(feature = "i-scripts")] -fn parse_functions(value: &RedisValue) -> Result, RedisError> { - if let RedisValue::Array(functions) = value { - let mut out = Vec::with_capacity(functions.len()); - for function_block in functions.iter() { - let functions: HashMap = function_block.clone().convert()?; - let name = match functions.get("name").and_then(|n| n.as_bytes_str()) { - Some(name) => name, - None => return Err(RedisError::new_parse("Missing function name.")), - }; - let flags: Vec = functions - .get("flags") - .and_then(|f| { - f.clone() - .into_array() - .into_iter() - .map(|v| FunctionFlag::from_str(v.as_str().unwrap_or_default().as_ref())) - .collect() - }) - .unwrap_or_default(); - - out.push(Function { name, flags }) - } - - Ok(out) - } else { - Err(RedisError::new_parse("Invalid functions block.")) - } -} - -/// Check and parse the response to `FUNCTION LIST`. -#[cfg(feature = "i-scripts")] -pub fn value_to_functions(value: &RedisValue, name: &str) -> Result, RedisError> { - if let RedisValue::Array(ref libraries) = value { - for library in libraries.iter() { - let properties: HashMap = library.clone().convert()?; - let should_parse = properties - .get("library_name") - .and_then(|v| v.as_str()) - .map(|s| s == name) - .unwrap_or(false); - - if should_parse { - if let Some(functions) = properties.get("functions") { - return parse_functions(functions); - } - } - } - - Err(RedisError::new_parse(format!("Missing library '{}'", name))) - } else { - Err(RedisError::new_parse("Expected array.")) - } -} - -pub async fn timeout(ft: Fut, timeout: Duration) -> Result -where - E: Into, - Fut: Future>, -{ - if !timeout.is_zero() { - let sleep_ft = sleep(timeout); - pin_mut!(sleep_ft); - pin_mut!(ft); - - trace!("Using timeout: {:?}", timeout); - match select(ft, sleep_ft).await { - Either::Left((lhs, _)) => lhs.map_err(|e| e.into()), - Either::Right((_, _)) => Err(RedisError::new(RedisErrorKind::Timeout, "Request timed out.")), - } - } else { - ft.await.map_err(|e| e.into()) - } -} - -/// Disconnect any state shared with the last router task spawned by the client. -pub fn reset_router_task(inner: &RefCount) { - let _guard = inner._lock.lock(); - - if !inner.has_command_rx() { - _trace!(inner, "Resetting command channel before connecting."); - // another connection task is running. this will let the command channel drain, then it'll drop everything on - // the old connection/router interface. - let (tx, rx) = unbounded_channel(); - #[cfg(feature = "glommio")] - let tx = tx.into(); - - let old_command_tx = inner.swap_command_tx(tx); - inner.store_command_rx(rx, true); - close_router_channel(inner, old_command_tx); - } -} - -/// Whether the router should check and interrupt the blocked command. -async fn should_enforce_blocking_policy(inner: &RefCount, command: &RedisCommand) -> bool { - if command.kind.closes_connection() { - return false; - } - if matches!(inner.config.blocking, Blocking::Error | Blocking::Interrupt) { - inner.backchannel.write().await.is_blocked() - } else { - false - } -} - -/// Interrupt the currently blocked connection (if found) with the provided flag. -pub async fn interrupt_blocked_connection( - inner: &RefCount, - flag: ClientUnblockFlag, -) -> Result<(), RedisError> { - let connection_id = { - let backchannel = inner.backchannel.write().await; - let server = match backchannel.blocked_server() { - Some(server) => server, - None => return Err(RedisError::new(RedisErrorKind::Unknown, "Connection is not blocked.")), - }; - let id = match backchannel.connection_id(&server) { - Some(id) => id, - None => { - return Err(RedisError::new( - RedisErrorKind::Unknown, - "Failed to read connection ID.", - )) - }, - }; - - _debug!(inner, "Sending CLIENT UNBLOCK to {}, ID: {}", server, id); - id - }; - - let command = RedisCommand::new(RedisCommandKind::ClientUnblock, vec![ - connection_id.into(), - flag.to_str().into(), - ]); - let frame = backchannel_request_response(inner, command, true).await?; - protocol_utils::frame_to_results(frame).map(|_| ()) -} - -/// Check the status of the connection (usually before sending a command) to determine whether the connection should -/// be unblocked automatically. -async fn check_blocking_policy(inner: &RefCount, command: &RedisCommand) -> Result<(), RedisError> { - if should_enforce_blocking_policy(inner, command).await { - _debug!( - inner, - "Checking to enforce blocking policy for {}", - command.kind.to_str_debug() - ); - - if inner.config.blocking == Blocking::Error { - return Err(RedisError::new( - RedisErrorKind::InvalidCommand, - "Error sending command while connection is blocked.", - )); - } else if inner.config.blocking == Blocking::Interrupt { - if let Err(e) = interrupt_blocked_connection(inner, ClientUnblockFlag::Error).await { - _error!(inner, "Failed to interrupt blocked connection: {:?}", e); - } - } - } - - Ok(()) -} - -/// Prepare the command options, returning the timeout duration to apply. -pub fn prepare_command(client: &C, command: &mut RedisCommand) -> Duration { - client.change_command(command); - command.inherit_options(client.inner()); - command - .timeout_dur - .unwrap_or_else(|| client.inner().default_command_timeout()) -} - -/// Send a command to the server using the default response handler. -pub async fn basic_request_response(client: &C, func: F) -> Result -where - C: ClientLike, - R: Into, - F: FnOnce() -> Result, -{ - let inner = client.inner(); - let mut command: RedisCommand = func()?.into(); - let (tx, rx) = oneshot_channel(); - command.response = ResponseKind::Respond(Some(tx)); - - let timed_out = command.timed_out.clone(); - let timeout_dur = prepare_command(client, &mut command); - check_blocking_policy(inner, &command).await?; - client.send_command(command)?; - - timeout(rx, timeout_dur) - .and_then(|r| async { r }) - .map_err(move |error| { - set_bool_atomic(&timed_out, true); - error - }) - .await -} - -/// Send a command to the server, with tracing. -#[cfg(any(feature = "full-tracing", feature = "partial-tracing"))] -#[allow(clippy::needless_borrows_for_generic_args)] -// despite what clippy says, this^ actually matters for tracing `record` calls (at least it seems where `V: Copy`) -pub async fn request_response(client: &C, func: F) -> Result -where - C: ClientLike, - R: Into, - F: FnOnce() -> Result, -{ - let inner = client.inner(); - if !inner.should_trace() { - return basic_request_response(client, func).await; - } - - let cmd_span = trace::create_command_span(inner); - let end_cmd_span = cmd_span.clone(); - - let (mut command, rx, req_size) = { - let args_span = trace::create_args_span(cmd_span.id(), inner); - #[allow(clippy::let_unit_value)] - let _guard = args_span.enter(); - let (tx, rx) = oneshot_channel(); - - let mut command: RedisCommand = func()?.into(); - command.response = ResponseKind::Respond(Some(tx)); - - let req_size = protocol_utils::args_size(command.args()); - args_span.record("num_args", &command.args().len()); - (command, rx, req_size) - }; - cmd_span.record("cmd.name", &command.kind.to_str_debug()); - cmd_span.record("cmd.req", &req_size); - - let queued_span = trace::create_queued_span(cmd_span.id(), inner); - let timed_out = command.timed_out.clone(); - _trace!( - inner, - "Setting command trace ID: {:?} for {} ({})", - cmd_span.id(), - command.kind.to_str_debug(), - command.debug_id() - ); - command.traces.cmd = Some(cmd_span.clone()); - command.traces.queued = Some(queued_span); - - let timeout_dur = prepare_command(client, &mut command); - check_blocking_policy(inner, &command).await?; - client.send_command(command)?; - - timeout(rx, timeout_dur) - .and_then(|r| async { r }) - .map_err(move |error| { - set_bool_atomic(&timed_out, true); - error - }) - .and_then(|frame| async move { - trace::record_response_size(&end_cmd_span, &frame); - Ok::<_, RedisError>(frame) - }) - .instrument(cmd_span) - .await -} - -#[cfg(not(any(feature = "full-tracing", feature = "partial-tracing")))] -pub async fn request_response(client: &C, func: F) -> Result -where - C: ClientLike, - R: Into, - F: FnOnce() -> Result, -{ - basic_request_response(client, func).await -} - -/// Send a command on the backchannel connection. -/// -/// A new connection may be created. -pub async fn backchannel_request_response( - inner: &RefCount, - command: RedisCommand, - use_blocked: bool, -) -> Result { - let mut backchannel = inner.backchannel.write().await; - let server = backchannel.find_server(inner, &command, use_blocked)?; - backchannel.request_response(inner, &server, command).await -} - -/// Check for a scan pattern without a hash tag, or with a wildcard in the hash tag. -/// -/// These patterns will result in scanning a random node if used against a clustered redis. -pub fn clustered_scan_pattern_has_hash_tag(inner: &RefCount, pattern: &str) -> bool { - let (mut i, mut j, mut has_wildcard) = (None, None, false); - for (idx, c) in pattern.chars().enumerate() { - if c == '{' && i.is_none() { - i = Some(idx); - } - if c == '}' && j.is_none() && i.is_some() { - j = Some(idx); - break; - } - if c == '*' && i.is_some() { - has_wildcard = true; - } - } - - if i.is_none() || j.is_none() { - return false; - } - - if has_wildcard { - _warn!( - inner, - "Found wildcard in scan pattern hash tag. You may not be scanning the correct node." - ); - } - - true -} - -/// A generic TryInto wrapper to work with the Infallible error type in the blanket From implementation. -pub fn try_into(val: S) -> Result -where - S: TryInto, - S::Error: Into, -{ - val.try_into().map_err(|e| e.into()) -} - -pub fn try_into_vec(values: Vec) -> Result, RedisError> -where - S: TryInto, - S::Error: Into, -{ - let mut out = Vec::with_capacity(values.len()); - for value in values.into_iter() { - out.push(try_into(value)?); - } - - Ok(out) -} - -pub fn add_jitter(delay: u64, jitter: u32) -> u64 { - if jitter == 0 { - delay - } else { - delay.saturating_add(rand::thread_rng().gen_range(0 .. jitter as u64)) - } -} - -pub fn into_redis_map(mut iter: I) -> Result, RedisError> -where - I: Iterator, - K: TryInto, - K::Error: Into, - V: TryInto, - V::Error: Into, -{ - let (lower, upper) = iter.size_hint(); - let capacity = if let Some(upper) = upper { upper } else { lower }; - let mut out = HashMap::with_capacity(capacity); - - while let Some((key, value)) = iter.next() { - out.insert(to!(key)?, to!(value)?); - } - Ok(out) -} - -pub fn flatten_nested_array_values(value: RedisValue, depth: usize) -> RedisValue { - if depth == 0 { - return value; - } - - match value { - RedisValue::Array(values) => { - let inner_size = values.iter().fold(0, |s, v| s + v.array_len().unwrap_or(1)); - let mut out = Vec::with_capacity(inner_size); - - for value in values.into_iter() { - match value { - RedisValue::Array(inner) => { - for value in inner.into_iter() { - out.push(flatten_nested_array_values(value, depth - 1)); - } - }, - _ => out.push(value), - } - } - RedisValue::Array(out) - }, - RedisValue::Map(values) => { - let mut out = HashMap::with_capacity(values.len()); - - for (key, value) in values.inner().into_iter() { - let value = if value.is_array() { - flatten_nested_array_values(value, depth - 1) - } else { - value - }; - - out.insert(key, value); - } - RedisValue::Map(RedisMap { inner: out }) - }, - _ => value, - } -} - -pub fn is_maybe_array_map(arr: &[RedisValue]) -> bool { - if !arr.is_empty() && arr.len() % 2 == 0 { - arr.chunks(2).all(|chunk| !chunk[0].is_aggregate_type()) - } else { - false - } -} - -#[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" -))] -pub fn check_tls_features() {} - -#[cfg(not(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" -)))] -pub fn check_tls_features() { - warn!("TLS features are not enabled, but a TLS feature may have been used."); -} - -#[cfg(all( - feature = "enable-native-tls", - not(any(feature = "enable-rustls", feature = "enable-rustls-ring")) -))] -pub fn tls_config_from_url(tls: bool) -> Result, RedisError> { - if tls { - TlsConnector::default_native_tls().map(|c| Some(c.into())) - } else { - Ok(None) - } -} - -#[cfg(all( - any(feature = "enable-rustls", feature = "enable-rustls-ring"), - not(feature = "enable-native-tls") -))] -pub fn tls_config_from_url(tls: bool) -> Result, RedisError> { - if tls { - TlsConnector::default_rustls().map(|c| Some(c.into())) - } else { - Ok(None) - } -} - -#[cfg(all( - feature = "enable-native-tls", - any(feature = "enable-rustls", feature = "enable-rustls-ring") -))] -pub fn tls_config_from_url(tls: bool) -> Result, RedisError> { - // default to native-tls when both are enabled - if tls { - TlsConnector::default_native_tls().map(|c| Some(c.into())) - } else { - Ok(None) - } -} - -pub fn swap_new_broadcast_channel(old: &RefSwap>>, capacity: usize) { - let new = broadcast_channel(capacity).0; - old.swap(RefCount::new(new)); -} - -pub fn url_uses_tls(url: &Url) -> bool { - url.scheme().starts_with(REDIS_TLS_SCHEME) -} - -pub fn url_is_clustered(url: &Url) -> bool { - url.scheme().ends_with(REDIS_CLUSTER_SCHEME_SUFFIX) -} - -pub fn url_is_sentinel(url: &Url) -> bool { - url.scheme().ends_with(REDIS_SENTINEL_SCHEME_SUFFIX) -} - -pub fn parse_url(url: &str, default_port: Option) -> Result<(Url, String, u16, bool), RedisError> { - let url = Url::parse(url)?; - let host = if let Some(host) = url.host_str() { - host.to_owned() - } else { - return Err(RedisError::new(RedisErrorKind::Config, "Invalid or missing host.")); - }; - let port = if let Some(port) = url.port().or(default_port) { - port - } else { - return Err(RedisError::new(RedisErrorKind::Config, "Invalid or missing port.")); - }; - - let tls = url_uses_tls(&url); - if tls { - check_tls_features(); - } - - Ok((url, host, port, tls)) -} - -pub fn url_is_unix_socket(url: &Url) -> bool { - url.scheme() == "redis+unix" -} - -#[cfg(feature = "unix-sockets")] -pub fn parse_unix_url(url: &str) -> Result<(Url, PathBuf), RedisError> { - let url = Url::parse(url)?; - let path: PathBuf = url.path().into(); - Ok((url, path)) -} - -pub fn parse_url_db(url: &Url) -> Result, RedisError> { - let parts: Vec<&str> = if let Some(parts) = url.path_segments() { - parts.collect() - } else { - return Ok(None); - }; - - if parts.len() > 1 { - return Err(RedisError::new(RedisErrorKind::Config, "Invalid database path.")); - } else if parts.is_empty() { - return Ok(None); - } - // handle empty paths with a / prefix - if parts[0].trim() == "" { - return Ok(None); - } - - Ok(Some(parts[0].parse()?)) -} - -pub fn parse_url_credentials(url: &Url) -> Result<(Option, Option), RedisError> { - let username = if url.username().is_empty() { - None - } else { - let username = percent_decode(url.username())?; - Some(username.into_owned()) - }; - let password = percent_decode(url.password().unwrap_or_default())?; - let password = if password.is_empty() { - None - } else { - Some(password.into_owned()) - }; - - Ok((username, password)) -} - -pub fn parse_url_other_nodes(url: &Url) -> Result, RedisError> { - let mut out = Vec::new(); - - for (key, value) in url.query_pairs().into_iter() { - if key == CLUSTER_NODE_QUERY { - let parts: Vec<&str> = value.split(':').collect(); - if parts.len() != 2 { - return Err(RedisError::new( - RedisErrorKind::Config, - format!("Invalid host:port for cluster node: {}", value), - )); - } - - let host = parts[0].to_owned(); - let port = parts[1].parse::()?; - out.push(Server::new(host, port)); - } - } - - Ok(out) -} - -pub fn parse_url_sentinel_service_name(url: &Url) -> Result { - for (key, value) in url.query_pairs().into_iter() { - if key == SENTINEL_NAME_QUERY { - return Ok(value.to_string()); - } - } - - Err(RedisError::new( - RedisErrorKind::Config, - "Invalid or missing sentinel service name query parameter.", - )) -} - -#[cfg(feature = "sentinel-auth")] -pub fn parse_url_sentinel_username(url: &Url) -> Option { - url.query_pairs().find_map(|(key, value)| { - if key == SENTINEL_USERNAME_QUERY { - Some(value.to_string()) - } else { - None - } - }) -} - -#[cfg(feature = "sentinel-auth")] -pub fn parse_url_sentinel_password(url: &Url) -> Option { - url.query_pairs().find_map(|(key, value)| { - if key == SENTINEL_PASSWORD_QUERY { - Some(value.to_string()) - } else { - None - } - }) -} - -pub async fn clear_backchannel_state(inner: &RefCount) { - inner.backchannel.write().await.clear_router_state(inner).await; -} - -/// Send QUIT to the servers and clean up the old router task's state. -fn close_router_channel(inner: &RefCount, command_tx: RefCount) { - inner.notifications.broadcast_close(); - inner.reset_server_state(); - - let command = RedisCommand::new(RedisCommandKind::Quit, vec![]); - inner.counters.incr_cmd_buffer_len(); - if let Err(_) = command_tx.send(command.into()) { - inner.counters.decr_cmd_buffer_len(); - _warn!(inner, "Failed to send QUIT when dropping old command channel."); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{error::RedisError, types::RedisValue}; - use std::{convert::TryInto, fmt::Debug}; - - fn m(v: V) -> RedisValue - where - V: TryInto + Debug, - V::Error: Into + Debug, - { - v.try_into().unwrap() - } - - fn a(v: Vec) -> RedisValue { - RedisValue::Array(v) - } - - #[test] - fn should_not_panic_with_zero_jitter() { - assert_eq!(add_jitter(10, 0), 10); - } - - #[test] - fn should_flatten_xread_example() { - // 127.0.0.1:6379> xread count 2 streams foo bar 1643479648480-0 1643479834990-0 - // 1) 1) "foo" - // 2) 1) 1) "1643479650336-0" - // 2) 1) "count" - // 2) "3" - // 2) 1) "bar" - // 2) 1) 1) "1643479837746-0" - // 2) 1) "count" - // 2) "5" - // 2) 1) "1643479925582-0" - // 2) 1) "count" - // 2) "6" - let actual: RedisValue = vec![ - a(vec![ - m("foo"), - a(vec![a(vec![m("1643479650336-0"), a(vec![m("count"), m(3)])])]), - ]), - a(vec![ - m("bar"), - a(vec![ - a(vec![m("1643479837746-0"), a(vec![m("count"), m(5)])]), - a(vec![m("1643479925582-0"), a(vec![m("count"), m(6)])]), - ]), - ]), - ] - .into_iter() - .collect(); - - // flatten the top level nested array into something that can be cast to a map - let expected: RedisValue = vec![ - m("foo"), - a(vec![a(vec![m("1643479650336-0"), a(vec![m("count"), m(3)])])]), - m("bar"), - a(vec![ - a(vec![m("1643479837746-0"), a(vec![m("count"), m(5)])]), - a(vec![m("1643479925582-0"), a(vec![m("count"), m(6)])]), - ]), - ] - .into_iter() - .collect(); - - assert_eq!(flatten_nested_array_values(actual, 1), expected); - } - - #[test] - fn should_parse_url_credentials_no_creds() { - let url = Url::parse("redis://localhost:6379").unwrap(); - let (username, password) = parse_url_credentials(&url).unwrap(); - - assert_eq!(username, None); - assert_eq!(password, None); - } - - #[test] - fn should_parse_url_credentials_with_creds() { - let url = Url::parse("redis://default:abc123@localhost:6379").unwrap(); - let (username, password) = parse_url_credentials(&url).unwrap(); - - assert_eq!(username.unwrap(), "default"); - assert_eq!(password.unwrap(), "abc123"); - } - - #[test] - fn should_parse_url_credentials_with_percent_encoded_creds() { - let url = Url::parse("redis://default:abc%2F123@localhost:6379").unwrap(); - let (username, password) = parse_url_credentials(&url).unwrap(); - - assert_eq!(username.unwrap(), "default"); - assert_eq!(password.unwrap(), "abc/123"); - } -} diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index 9be45a55..00000000 --- a/tests/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# Testing - -Tests are organized by category, similar to the [commands](../src/commands) folder. - -By default, most tests run 8 times based on the following configuration parameters: clustered vs centralized servers, -pipelined vs non-pipelined clients, and RESP2 vs RESP3 mode. Helper macros exist to make this easy so each test only has -to be written once. - -**The tests require Redis version >=6.2** As of writing the default version used is 7.2.4. - -## Installation - -The [environ](environ) file will bootstrap the local environment with all the environment variables and system settings -necessary to run the tests. It will prompt the caller for certain system-wide modifications if necessary. -The `/etc/hosts` modifications are only necessary if you wish to manually run the TLS tests outside the docker network. - -In order to run the testing scripts the following must be installed: - -* Bash (all the scripts assume `bash`) -* `docker` -* `docker-compose` (this may come with `docker` depending on the version you use) - -## Running Tests - -The runner scripts will set up the Redis servers and run the tests inside docker. - -* [all-features](runners/all-features.sh) will run tests with all features (except sentinel tests). -* [default-features](runners/default-features.sh) will run tests with default features (except sentinel tests). -* [default-nil-types](runners/default-nil-types.sh) will run tests with `default-nil-types`. -* [no-features](runners/no-features.sh) will run the tests without any of the feature flags. -* [sentinel-features](runners/sentinel-features.sh) will run the centralized tests against a sentinel deployment. This - is the only test runner that requires the sentinel deployment via docker-compose. -* [cluster-rustls](runners/cluster-rustls.sh) will set up a cluster with TLS enabled and run the cluster tests against - it with `rustls`. -* [cluster-native-tls](runners/cluster-native-tls.sh) will set up a cluster with TLS enabled and run the cluster tests - against it with `native-tls`. -* [redis-stack](runners/redis-stack.sh) will set up a centralized `redis/redis-stack` container and run - with `redis-stack` features. -* [everything](runners/everything.sh) will run all of the above scripts. - -These scripts will pass through any extra argv so callers can filter tests as needed. - -See the [CI configuration](../.circleci/config.yml) for more information. - -There's also a [debug container](runners/docker-bash.sh) script that can be used to run `redis-cli` inside the docker -network. - -### Example - -``` -cd path/to/fred -. ./tests/environ -./tests/runners/all-features.sh -``` - -### Checking Interface Features - -There's [a build script](scripts/check_features.sh) that -runs `cargo clippy --no-default-features --features -- -Dwarnings` on each of the interface -features individually, without any other features. - -``` -cd path/to/fred -./tests/scripts/check_features.sh -``` - -## Adding Tests - -Adding tests is straightforward with the help of some macros and utility functions. - -Note: When writing tests that operate on multiple keys be sure to use -a [hash_tag](https://redis.io/topics/cluster-spec#keys-hash-tags) so that all keys used by a command exist on the same -node in a cluster. - -1. If necessary create a new file in the appropriate folder. -2. Create a new async function in the appropriate file. This function should take a `RedisClient` and `RedisConfig` as - arguments and should return a `Result<(), RedisError>`. The client will already be connected when this function runs. -3. This new function should **not** be marked as a `#[test]` or `#[tokio::test]` -4. Call the test from the appropriate [integration/cluster.rs](integration/cluster.rs) - or [integration/centralized.rs](integration/centralized.rs) files, or both. Create a wrapping `mod` block with the - same name as the test's folder if necessary. -5. Use `centralized_test!` or `cluster_test!` to generate tests in the appropriate module. Centralized tests will be - converted to sentinel tests or redis-stack tests if needed. - -Tests that use this pattern will run 8 times to check the functionality against clustered and centralized redis servers -with using both pipelined and non-pipelined clients in RESP2 and RESP3 mode. - -## Notes - -* Since we're mutating shared state in external redis servers with these tests it's necessary to run the tests - with `--test-threads=1`. The test runner scripts will do this automatically. -* **The tests will periodically call `flushall` before each test iteration.** diff --git a/tests/doc-glommio.sh b/tests/doc-glommio.sh deleted file mode 100755 index 4e763a8e..00000000 --- a/tests/doc-glommio.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -FEATURES="network-logs custom-reconnect-errors serde-json blocking-encoding - full-tracing monitor metrics sentinel-client subscriber-client dns debug-ids - replicas sha-1 transactions i-all glommio i-redis-stack enable-rustls enable-native-tls" - -RUSTDOCFLAGS="" cargo +nightly rustdoc --features "$FEATURES" "$@" -- --cfg docsrs \ No newline at end of file diff --git a/tests/doc.sh b/tests/doc.sh deleted file mode 100755 index f4e91448..00000000 --- a/tests/doc.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -cargo +nightly rustdoc --all-features "$@" -- --cfg docsrs \ No newline at end of file diff --git a/tests/docker/compose/base.yml b/tests/docker/compose/base.yml deleted file mode 100644 index a5df970b..00000000 --- a/tests/docker/compose/base.yml +++ /dev/null @@ -1,48 +0,0 @@ -version: '2' - -networks: - fred-tests: - driver: bridge - -services: - debug: - depends_on: - - redis-cluster-tls-6 - # - redis-main - - redis-stack-main - - redis-cluster-6 - - redis-sentinel-3 - #- valkey-main - #- valkey-cluster-6 - container_name: "debug" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/debug.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - command: - - "/bin/bash" - environment: - RUST_LOG: "${RUST_LOG}" - CIRCLECI_TESTS: "${CIRCLECI_TESTS}" - REDIS_VERSION: "${REDIS_VERSION}" - FRED_REDIS_CENTRALIZED_HOST: "${FRED_REDIS_CENTRALIZED_HOST}" - FRED_REDIS_CENTRALIZED_PORT: "${FRED_REDIS_CENTRALIZED_PORT}" - FRED_VALKEY_CENTRALIZED_HOST: "${FRED_VALKEY_CENTRALIZED_HOST}" - FRED_VALKEY_CENTRALIZED_PORT: "${FRED_VALKEY_CENTRALIZED_PORT}" - FRED_REDIS_CLUSTER_HOST: "${FRED_REDIS_CLUSTER_HOST}" - FRED_REDIS_CLUSTER_PORT: "${FRED_REDIS_CLUSTER_PORT}" - FRED_VALKEY_CLUSTER_HOST: "${FRED_VALKEY_CLUSTER_HOST}" - FRED_VALKEY_CLUSTER_PORT: "${FRED_VALKEY_CLUSTER_PORT}" - FRED_REDIS_SENTINEL_HOST: "${FRED_REDIS_SENTINEL_HOST}" - FRED_REDIS_SENTINEL_PORT: "${FRED_REDIS_SENTINEL_PORT}" - FRED_REDIS_STACK_HOST: "${FRED_REDIS_STACK_HOST}" - FRED_REDIS_STACK_PORT: "${FRED_REDIS_STACK_PORT}" - REDIS_USERNAME: "${REDIS_USERNAME}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - REDIS_SENTINEL_PASSWORD: "${REDIS_SENTINEL_PASSWORD}" - volumes: - - "../../..:/project" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/tests/docker/compose/centralized.yml b/tests/docker/compose/centralized.yml deleted file mode 100644 index c05e2f63..00000000 --- a/tests/docker/compose/centralized.yml +++ /dev/null @@ -1,20 +0,0 @@ -version: '2' - -networks: - fred-tests: - driver: bridge - -services: - redis-main: - container_name: "redis-main" - image: 'bitnami/redis:${REDIS_VERSION}' - environment: - - 'REDIS_PORT_NUMBER=${FRED_REDIS_CENTRALIZED_PORT}' - - 'ALLOW_EMPTY_PASSWORD=yes' - ports: - - "6379:${FRED_REDIS_CENTRALIZED_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/default.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' \ No newline at end of file diff --git a/tests/docker/compose/cluster-tls.yml b/tests/docker/compose/cluster-tls.yml deleted file mode 100644 index 3dc86920..00000000 --- a/tests/docker/compose/cluster-tls.yml +++ /dev/null @@ -1,147 +0,0 @@ -version: '2' - -networks: - fred-tests: - driver: bridge - -services: - redis-cluster-tls-1: - container_name: "redis-cluster-tls-1" - image: 'bitnami/redis-cluster:${REDIS_VERSION}' - environment: - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_PORT_NUMBER=0' - - 'REDISCLI_AUTH=${REDIS_PASSWORD}' - - 'REDIS_NODES=redis-cluster-tls-1 redis-cluster-tls-2 redis-cluster-tls-3 redis-cluster-tls-4 redis-cluster-tls-5 redis-cluster-tls-6' - - 'REDIS_TLS_ENABLED=yes' - - 'REDIS_TLS_PORT_NUMBER=${FRED_REDIS_CLUSTER_TLS_PORT}' - - 'REDIS_TLS_CERT_FILE=/opt/bitnami/redis/mounted-etc/creds/node-1.pem' - - 'REDIS_TLS_KEY_FILE=/opt/bitnami/redis/mounted-etc/creds/node-1.key' - - 'REDIS_TLS_CA_FILE=/opt/bitnami/redis/mounted-etc/creds/ca.pem' - - 'REDIS_TLS_AUTH_CLIENTS=optional' - ports: - - "40001:${FRED_REDIS_CLUSTER_TLS_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/tmp/creds:/opt/bitnami/redis/mounted-etc/creds' - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/default.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' - redis-cluster-tls-2: - container_name: "redis-cluster-tls-2" - image: 'bitnami/redis-cluster:${REDIS_VERSION}' - environment: - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_PORT_NUMBER=0' - - 'REDISCLI_AUTH=${REDIS_PASSWORD}' - - 'REDIS_NODES=redis-cluster-tls-1 redis-cluster-tls-2 redis-cluster-tls-3 redis-cluster-tls-4 redis-cluster-tls-5 redis-cluster-tls-6' - - 'REDIS_TLS_ENABLED=yes' - - 'REDIS_TLS_PORT_NUMBER=${FRED_REDIS_CLUSTER_TLS_PORT}' - - 'REDIS_TLS_CERT_FILE=/opt/bitnami/redis/mounted-etc/creds/node-2.pem' - - 'REDIS_TLS_KEY_FILE=/opt/bitnami/redis/mounted-etc/creds/node-2.key' - - 'REDIS_TLS_CA_FILE=/opt/bitnami/redis/mounted-etc/creds/ca.pem' - - 'REDIS_TLS_AUTH_CLIENTS=optional' - ports: - - "40002:${FRED_REDIS_CLUSTER_TLS_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/tmp/creds:/opt/bitnami/redis/mounted-etc/creds' - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/default.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' - redis-cluster-tls-3: - container_name: "redis-cluster-tls-3" - image: 'bitnami/redis-cluster:${REDIS_VERSION}' - environment: - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_PORT_NUMBER=0' - - 'REDISCLI_AUTH=${REDIS_PASSWORD}' - - 'REDIS_NODES=redis-cluster-tls-1 redis-cluster-tls-2 redis-cluster-tls-3 redis-cluster-tls-4 redis-cluster-tls-5 redis-cluster-tls-6' - - 'REDIS_TLS_ENABLED=yes' - - 'REDIS_TLS_PORT_NUMBER=${FRED_REDIS_CLUSTER_TLS_PORT}' - - 'REDIS_TLS_CERT_FILE=/opt/bitnami/redis/mounted-etc/creds/node-3.pem' - - 'REDIS_TLS_KEY_FILE=/opt/bitnami/redis/mounted-etc/creds/node-3.key' - - 'REDIS_TLS_CA_FILE=/opt/bitnami/redis/mounted-etc/creds/ca.pem' - - 'REDIS_TLS_AUTH_CLIENTS=optional' - ports: - - "40003:${FRED_REDIS_CLUSTER_TLS_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/tmp/creds:/opt/bitnami/redis/mounted-etc/creds' - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/default.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' - redis-cluster-tls-4: - container_name: "redis-cluster-tls-4" - image: 'bitnami/redis-cluster:${REDIS_VERSION}' - environment: - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_PORT_NUMBER=0' - - 'REDISCLI_AUTH=${REDIS_PASSWORD}' - - 'REDIS_NODES=redis-cluster-tls-1 redis-cluster-tls-2 redis-cluster-tls-3 redis-cluster-tls-4 redis-cluster-tls-5 redis-cluster-tls-6' - - 'REDIS_TLS_ENABLED=yes' - - 'REDIS_TLS_PORT_NUMBER=${FRED_REDIS_CLUSTER_TLS_PORT}' - - 'REDIS_TLS_CERT_FILE=/opt/bitnami/redis/mounted-etc/creds/node-4.pem' - - 'REDIS_TLS_KEY_FILE=/opt/bitnami/redis/mounted-etc/creds/node-4.key' - - 'REDIS_TLS_CA_FILE=/opt/bitnami/redis/mounted-etc/creds/ca.pem' - - 'REDIS_TLS_AUTH_CLIENTS=optional' - ports: - - "40004:${FRED_REDIS_CLUSTER_TLS_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/tmp/creds:/opt/bitnami/redis/mounted-etc/creds' - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/default.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' - redis-cluster-tls-5: - container_name: "redis-cluster-tls-5" - image: 'bitnami/redis-cluster:${REDIS_VERSION}' - environment: - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_PORT_NUMBER=0' - - 'REDISCLI_AUTH=${REDIS_PASSWORD}' - - 'REDIS_NODES=redis-cluster-tls-1 redis-cluster-tls-2 redis-cluster-tls-3 redis-cluster-tls-4 redis-cluster-tls-5 redis-cluster-tls-6' - - 'REDIS_TLS_ENABLED=yes' - - 'REDIS_TLS_PORT_NUMBER=${FRED_REDIS_CLUSTER_TLS_PORT}' - - 'REDIS_TLS_CERT_FILE=/opt/bitnami/redis/mounted-etc/creds/node-5.pem' - - 'REDIS_TLS_KEY_FILE=/opt/bitnami/redis/mounted-etc/creds/node-5.key' - - 'REDIS_TLS_CA_FILE=/opt/bitnami/redis/mounted-etc/creds/ca.pem' - - 'REDIS_TLS_AUTH_CLIENTS=optional' - ports: - - "40005:${FRED_REDIS_CLUSTER_TLS_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/tmp/creds:/opt/bitnami/redis/mounted-etc/creds' - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/default.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' - redis-cluster-tls-6: - container_name: "redis-cluster-tls-6" - depends_on: - - redis-cluster-tls-1 - - redis-cluster-tls-2 - - redis-cluster-tls-3 - - redis-cluster-tls-4 - - redis-cluster-tls-5 - image: 'bitnami/redis-cluster:${REDIS_VERSION}' - environment: - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_PORT_NUMBER=0' - - 'REDISCLI_AUTH=${REDIS_PASSWORD}' - - 'REDIS_NODES=redis-cluster-tls-1 redis-cluster-tls-2 redis-cluster-tls-3 redis-cluster-tls-4 redis-cluster-tls-5 redis-cluster-tls-6' - - 'REDIS_CLUSTER_REPLICAS=1' - - 'REDIS_CLUSTER_CREATOR=yes' - - 'REDIS_TLS_ENABLED=yes' - - 'REDIS_TLS_PORT_NUMBER=${FRED_REDIS_CLUSTER_TLS_PORT}' - - 'REDIS_TLS_CERT_FILE=/opt/bitnami/redis/mounted-etc/creds/node-6.pem' - - 'REDIS_TLS_KEY_FILE=/opt/bitnami/redis/mounted-etc/creds/node-6.key' - - 'REDIS_TLS_CA_FILE=/opt/bitnami/redis/mounted-etc/creds/ca.pem' - - 'REDIS_TLS_AUTH_CLIENTS=optional' - ports: - - "40006:${FRED_REDIS_CLUSTER_TLS_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/tmp/creds:/opt/bitnami/redis/mounted-etc/creds' - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/default.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' \ No newline at end of file diff --git a/tests/docker/compose/cluster.yml b/tests/docker/compose/cluster.yml deleted file mode 100644 index a1a66428..00000000 --- a/tests/docker/compose/cluster.yml +++ /dev/null @@ -1,105 +0,0 @@ -version: '2' - -networks: - fred-tests: - driver: bridge - -services: - redis-cluster-1: - container_name: "redis-cluster-1" - image: 'bitnami/redis-cluster:${REDIS_VERSION}' - environment: - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_PORT_NUMBER=${FRED_REDIS_CLUSTER_PORT}' - - 'REDISCLI_AUTH=${REDIS_PASSWORD}' - - 'REDIS_NODES=redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6' - ports: - - "30001:${FRED_REDIS_CLUSTER_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/default.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' - redis-cluster-2: - container_name: "redis-cluster-2" - image: 'bitnami/redis-cluster:${REDIS_VERSION}' - environment: - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_PORT_NUMBER=${FRED_REDIS_CLUSTER_PORT}' - - 'REDISCLI_AUTH=${REDIS_PASSWORD}' - - 'REDIS_NODES=redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6' - ports: - - "30002:${FRED_REDIS_CLUSTER_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/default.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' - redis-cluster-3: - container_name: "redis-cluster-3" - image: 'bitnami/redis-cluster:${REDIS_VERSION}' - environment: - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_PORT_NUMBER=${FRED_REDIS_CLUSTER_PORT}' - - 'REDISCLI_AUTH=${REDIS_PASSWORD}' - - 'REDIS_NODES=redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6' - ports: - - "30003:${FRED_REDIS_CLUSTER_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/default.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' - redis-cluster-4: - container_name: "redis-cluster-4" - image: 'bitnami/redis-cluster:${REDIS_VERSION}' - environment: - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_PORT_NUMBER=${FRED_REDIS_CLUSTER_PORT}' - - 'REDISCLI_AUTH=${REDIS_PASSWORD}' - - 'REDIS_NODES=redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6' - ports: - - "30004:${FRED_REDIS_CLUSTER_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/default.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' - redis-cluster-5: - container_name: "redis-cluster-5" - image: 'bitnami/redis-cluster:${REDIS_VERSION}' - environment: - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_PORT_NUMBER=${FRED_REDIS_CLUSTER_PORT}' - - 'REDISCLI_AUTH=${REDIS_PASSWORD}' - - 'REDIS_NODES=redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6' - ports: - - "30005:${FRED_REDIS_CLUSTER_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/default.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' - redis-cluster-6: - container_name: "redis-cluster-6" - depends_on: - - redis-cluster-1 - - redis-cluster-2 - - redis-cluster-3 - - redis-cluster-4 - - redis-cluster-5 - image: 'bitnami/redis-cluster:${REDIS_VERSION}' - environment: - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_PORT_NUMBER=${FRED_REDIS_CLUSTER_PORT}' - - 'REDISCLI_AUTH=${REDIS_PASSWORD}' - - 'REDIS_NODES=redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6' - - 'REDIS_CLUSTER_REPLICAS=1' - - 'REDIS_CLUSTER_CREATOR=yes' - ports: - - "30006:${FRED_REDIS_CLUSTER_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/default.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' \ No newline at end of file diff --git a/tests/docker/compose/glommio.yml b/tests/docker/compose/glommio.yml deleted file mode 100644 index 79d6be79..00000000 --- a/tests/docker/compose/glommio.yml +++ /dev/null @@ -1,25 +0,0 @@ -version: '2' - -networks: - fred-tests: - driver: bridge - -services: - glommio: - container_name: "check-glommio" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/debug.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - command: - - "/project/tests/docker/runners/bash/check-glommio.sh" - - "${TEST_ARGV}" - environment: - RUST_LOG: "${RUST_LOG}" - RUST_BACKTRACE: "${RUST_BACKTRACE}" - volumes: - - "../../..:/project" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/tests/docker/compose/jaeger.yml b/tests/docker/compose/jaeger.yml deleted file mode 100644 index c6e7ec08..00000000 --- a/tests/docker/compose/jaeger.yml +++ /dev/null @@ -1,29 +0,0 @@ -version: "3" - -networks: - fred-tests: - driver: bridge - -services: - jaeger: - container_name: "jaeger" - image: "jaegertracing/all-in-one:latest" - networks: - - fred-tests - environment: - - "COLLECTOR_ZIPKIN_HOST_PORT=:9411" - - "COLLECTOR_OTLP_ENABLED=true" - ports: - - "5775:5775/udp" - - "6831:6831/udp" - - "6832:6832/udp" - - "5778:5778" - - "16686:16686" - - "14268:14268" - - "14250:14250" - - "9411:9411" - - "4318:4318" - - "4317:4317" - - - diff --git a/tests/docker/compose/redis-stack.yml b/tests/docker/compose/redis-stack.yml deleted file mode 100644 index 7bc91db0..00000000 --- a/tests/docker/compose/redis-stack.yml +++ /dev/null @@ -1,23 +0,0 @@ -version: '2' - -networks: - fred-tests: - driver: bridge - -services: - redis-stack-main: - container_name: "redis-stack-main" - image: 'redis/redis-stack:latest' - environment: - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_ARGS="--requirepass ${REDIS_PASSWORD}"' - # - 'REDISEARCH_ARGS=""' - # - 'REDISJSON_ARGS=""' - # - 'REDISGRAPH_ARGS=""' - # - 'REDISTIMESERIES_ARGS=""' - # - 'REDISBLOOM_ARGS=""' - ports: - - "6382:${FRED_REDIS_STACK_PORT}" - - "8001:8001" - networks: - - fred-tests \ No newline at end of file diff --git a/tests/docker/compose/sentinel.yml b/tests/docker/compose/sentinel.yml deleted file mode 100644 index cdfa77e8..00000000 --- a/tests/docker/compose/sentinel.yml +++ /dev/null @@ -1,107 +0,0 @@ -version: '2' - -networks: - fred-tests: - driver: bridge - -services: - redis-sentinel-main: - container_name: "redis-sentinel-main" - image: 'bitnami/redis:${REDIS_VERSION}' - environment: - - 'REDIS_PORT_NUMBER=6380' - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_PASSWORD=${REDIS_PASSWORD}' - - 'REDIS_ACLFILE=/opt/bitnami/redis/mounted-etc/users.acl' - ports: - - "6380:6380" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - redis-sentinel-replica: - container_name: "redis-sentinel-replica" - image: 'bitnami/redis:${REDIS_VERSION}' - depends_on: - - redis-sentinel-main - environment: - - 'REDIS_PORT_NUMBER=6381' - - 'REDIS_PASSWORD=${REDIS_PASSWORD}' - - 'REDIS_MASTER_PASSWORD=${REDIS_PASSWORD}' - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_REPLICATION_MODE=slave' - - 'REDIS_REPLICA_PORT=6381' - - 'REDIS_MASTER_HOST=redis-sentinel-main' - - 'REDIS_MASTER_PORT_NUMBER=6380' - - 'REDIS_ACLFILE=/opt/bitnami/redis/mounted-etc/users.acl' - ports: - - "6381:6381" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - redis-sentinel-1: - container_name: "redis-sentinel-1" - image: 'bitnami/redis-sentinel:${REDIS_VERSION}' - depends_on: - - redis-sentinel-main - - redis-sentinel-replica - environment: - - 'REDIS_SENTINEL_PORT_NUMBER=26379' - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_MASTER_HOST=redis-sentinel-main' - - 'REDIS_MASTER_PORT_NUMBER=6380' - - 'REDIS_MASTER_SET=redis-sentinel-main' - - 'REDIS_SENTINEL_PASSWORD=${REDIS_SENTINEL_PASSWORD}' - - 'REDIS_MASTER_PASSWORD=${REDIS_PASSWORD}' - - 'REDIS_ACLFILE=/opt/bitnami/redis/mounted-etc/users.acl' - ports: - - '26379:26379' - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - redis-sentinel-2: - container_name: "redis-sentinel-2" - image: 'bitnami/redis-sentinel:${REDIS_VERSION}' - depends_on: - - redis-sentinel-main - - redis-sentinel-replica - - redis-sentinel-1 - environment: - - 'REDIS_SENTINEL_PORT_NUMBER=26380' - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_MASTER_HOST=redis-sentinel-main' - - 'REDIS_MASTER_PORT_NUMBER=6380' - - 'REDIS_MASTER_SET=redis-sentinel-main' - - 'REDIS_SENTINEL_PASSWORD=${REDIS_SENTINEL_PASSWORD}' - - 'REDIS_MASTER_PASSWORD=${REDIS_PASSWORD}' - - 'REDIS_ACLFILE=/opt/bitnami/redis/mounted-etc/users.acl' - ports: - - '26380:26380' - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - redis-sentinel-3: - container_name: "redis-sentinel-3" - image: 'bitnami/redis-sentinel:${REDIS_VERSION}' - depends_on: - - redis-sentinel-main - - redis-sentinel-replica - - redis-sentinel-2 - environment: - - 'REDIS_SENTINEL_PORT_NUMBER=26381' - - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_MASTER_HOST=redis-sentinel-main' - - 'REDIS_MASTER_PORT_NUMBER=6380' - - 'REDIS_MASTER_SET=redis-sentinel-main' - - 'REDIS_SENTINEL_PASSWORD=${REDIS_SENTINEL_PASSWORD}' - - 'REDIS_MASTER_PASSWORD=${REDIS_PASSWORD}' - - 'REDIS_ACLFILE=/opt/bitnami/redis/mounted-etc/users.acl' - ports: - - '26381:26381' - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' \ No newline at end of file diff --git a/tests/docker/compose/unix-socket.yml b/tests/docker/compose/unix-socket.yml deleted file mode 100644 index 894d6b54..00000000 --- a/tests/docker/compose/unix-socket.yml +++ /dev/null @@ -1,27 +0,0 @@ -version: '2' - -networks: - fred-tests: - driver: bridge - -services: - unix-socket-tmp: - image: busybox - command: "chmod -R 777 ${REDIS_UNIX_SOCK_CONTAINER_DIR}" - volumes: - - '${REDIS_UNIX_SOCK_HOST_DIR}:${REDIS_UNIX_SOCK_CONTAINER_DIR}' - redis-main-unix-socket: - container_name: "redis-main-unix-socket" - image: 'bitnami/redis:${REDIS_VERSION}' - environment: - - 'REDIS_PORT_NUMBER=${FRED_REDIS_CENTRALIZED_PORT}' - - 'ALLOW_EMPTY_PASSWORD=yes' - ports: - - "6381:${FRED_REDIS_CENTRALIZED_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/opt/bitnami/redis/mounted-etc/users.acl' - - '../../../tests/docker/overrides/unix-socket.conf:/opt/bitnami/redis/mounted-etc/overrides.conf' - volumes_from: - - unix-socket-tmp \ No newline at end of file diff --git a/tests/docker/compose/valkey-centralized.yml b/tests/docker/compose/valkey-centralized.yml deleted file mode 100644 index 7debf012..00000000 --- a/tests/docker/compose/valkey-centralized.yml +++ /dev/null @@ -1,24 +0,0 @@ -version: '2' - -networks: - fred-tests: - driver: bridge - -services: - valkey-main: - container_name: "valkey-main" - image: 'valkey/valkey:${VALKEY_VERSION}' - command: - - "valkey-server" - - "/usr/local/etc/valkey/valkey.conf" - - "--port" - - "${FRED_VALKEY_CENTRALIZED_PORT}" - - "--aclfile" - - "/usr/local/etc/valkey/users.acl" - ports: - - "6379:${FRED_VALKEY_CENTRALIZED_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/usr/local/etc/valkey/users.acl' - - '../../../tests/docker/overrides/default.conf:/usr/local/etc/valkey/valkey.conf' \ No newline at end of file diff --git a/tests/docker/compose/valkey-cluster.yml b/tests/docker/compose/valkey-cluster.yml deleted file mode 100644 index 72d72476..00000000 --- a/tests/docker/compose/valkey-cluster.yml +++ /dev/null @@ -1,123 +0,0 @@ -version: '2' - -networks: - fred-tests: - driver: bridge - -services: - valkey-cluster-1: - container_name: "valkey-cluster-1" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/valkey-cluster-node.dockerfile - args: - VALKEY_VERSION: "${VALKEY_VERSION}" - environment: - - 'VALKEY_PORT_NUMBER=${FRED_VALKEY_CLUSTER_PORT}' - - 'VALKEYCLI_AUTH=${REDIS_PASSWORD}' - - 'VALKEY_NODES=valkey-cluster-1 valkey-cluster-2 valkey-cluster-3 valkey-cluster-4 valkey-cluster-5 valkey-cluster-6' - - 'VALKEY_ACLFILE=/usr/local/etc/valkey/users.acl' - ports: - - "30001:${FRED_VALKEY_CLUSTER_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/usr/local/etc/valkey/users.acl' - valkey-cluster-2: - container_name: "valkey-cluster-2" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/valkey-cluster-node.dockerfile - args: - VALKEY_VERSION: "${VALKEY_VERSION}" - environment: - - 'VALKEY_PORT_NUMBER=${FRED_VALKEY_CLUSTER_PORT}' - - 'VALKEYCLI_AUTH=${REDIS_PASSWORD}' - - 'VALKEY_NODES=valkey-cluster-1 valkey-cluster-2 valkey-cluster-3 valkey-cluster-4 valkey-cluster-5 valkey-cluster-6' - - 'VALKEY_ACLFILE=/usr/local/etc/valkey/users.acl' - ports: - - "30002:${FRED_VALKEY_CLUSTER_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/usr/local/etc/valkey/users.acl' - valkey-cluster-3: - container_name: "valkey-cluster-3" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/valkey-cluster-node.dockerfile - args: - VALKEY_VERSION: "${VALKEY_VERSION}" - environment: - - 'VALKEY_PORT_NUMBER=${FRED_VALKEY_CLUSTER_PORT}' - - 'VALKEYCLI_AUTH=${REDIS_PASSWORD}' - - 'VALKEY_NODES=valkey-cluster-1 valkey-cluster-2 valkey-cluster-3 valkey-cluster-4 valkey-cluster-5 valkey-cluster-6' - - 'VALKEY_ACLFILE=/usr/local/etc/valkey/users.acl' - ports: - - "30003:${FRED_VALKEY_CLUSTER_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/usr/local/etc/valkey/users.acl' - valkey-cluster-4: - container_name: "valkey-cluster-4" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/valkey-cluster-node.dockerfile - args: - VALKEY_VERSION: "${VALKEY_VERSION}" - environment: - - 'VALKEY_PORT_NUMBER=${FRED_VALKEY_CLUSTER_PORT}' - - 'VALKEYCLI_AUTH=${REDIS_PASSWORD}' - - 'VALKEY_NODES=valkey-cluster-1 valkey-cluster-2 valkey-cluster-3 valkey-cluster-4 valkey-cluster-5 valkey-cluster-6' - - 'VALKEY_ACLFILE=/usr/local/etc/valkey/users.acl' - ports: - - "30004:${FRED_VALKEY_CLUSTER_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/usr/local/etc/valkey/users.acl' - valkey-cluster-5: - container_name: "valkey-cluster-5" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/valkey-cluster-node.dockerfile - args: - VALKEY_VERSION: "${VALKEY_VERSION}" - environment: - - 'VALKEY_PORT_NUMBER=${FRED_VALKEY_CLUSTER_PORT}' - - 'VALKEYCLI_AUTH=${REDIS_PASSWORD}' - - 'VALKEY_NODES=valkey-cluster-1 valkey-cluster-2 valkey-cluster-3 valkey-cluster-4 valkey-cluster-5 valkey-cluster-6' - - 'VALKEY_ACLFILE=/usr/local/etc/valkey/users.acl' - ports: - - "30005:${FRED_VALKEY_CLUSTER_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/usr/local/etc/valkey/users.acl' - valkey-cluster-6: - container_name: "valkey-cluster-6" - depends_on: - - valkey-cluster-1 - - valkey-cluster-2 - - valkey-cluster-3 - - valkey-cluster-4 - - valkey-cluster-5 - build: - context: ../../../ - dockerfile: tests/docker/runners/images/valkey-cluster-node.dockerfile - args: - VALKEY_VERSION: "${VALKEY_VERSION}" - environment: - - 'VALKEY_PORT_NUMBER=${FRED_VALKEY_CLUSTER_PORT}' - - 'VALKEYCLI_AUTH=${REDIS_PASSWORD}' - - 'VALKEY_NODES=valkey-cluster-1 valkey-cluster-2 valkey-cluster-3 valkey-cluster-4 valkey-cluster-5 valkey-cluster-6' - - 'VALKEY_CLUSTER_REPLICAS=1' - - 'VALKEY_CLUSTER_CREATOR=yes' - - 'VALKEY_ACLFILE=/usr/local/etc/valkey/users.acl' - ports: - - "30006:${FRED_VALKEY_CLUSTER_PORT}" - networks: - - fred-tests - volumes: - - '../../../tests/users.acl:/usr/local/etc/valkey/users.acl' \ No newline at end of file diff --git a/tests/docker/overrides/.gitkeep b/tests/docker/overrides/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/docker/runners/bash/all-features.sh b/tests/docker/runners/bash/all-features.sh deleted file mode 100755 index 0e12ef68..00000000 --- a/tests/docker/runners/bash/all-features.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -declare -a arr=("REDIS_VERSION" "REDIS_USERNAME" "REDIS_PASSWORD" "REDIS_SENTINEL_PASSWORD") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -# can't use --all-features here since that enables the TLS features and redis-json tests, which each require a -# different server configuration. the `cluster-tls.sh` and `cluster-rustls.sh` scripts can be used to test -# those features individually. -FEATURES="network-logs custom-reconnect-errors serde-json blocking-encoding - full-tracing monitor metrics sentinel-client subscriber-client dns debug-ids - replicas sha-1 transactions i-all" - -if [ -z "$FRED_CI_NEXTEST" ]; then - cargo test --release --lib --tests --features "$FEATURES" -- --test-threads=1 "$@" -else - cargo nextest run --release --lib --tests --features "$FEATURES" --test-threads=1 "$@" -fi \ No newline at end of file diff --git a/tests/docker/runners/bash/check-glommio.sh b/tests/docker/runners/bash/check-glommio.sh deleted file mode 100755 index afe97693..00000000 --- a/tests/docker/runners/bash/check-glommio.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -FEATURES="network-logs custom-reconnect-errors serde-json blocking-encoding - full-tracing monitor metrics sentinel-client subscriber-client dns debug-ids - replicas sha-1 transactions i-all glommio i-redis-stack enable-rustls enable-native-tls" - -cargo clippy --features "$FEATURES" -- "$@" \ No newline at end of file diff --git a/tests/docker/runners/bash/cluster-rustls-ring.sh b/tests/docker/runners/bash/cluster-rustls-ring.sh deleted file mode 100755 index 2575daf4..00000000 --- a/tests/docker/runners/bash/cluster-rustls-ring.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -declare -a arr=("REDIS_VERSION" "REDIS_USERNAME" "REDIS_PASSWORD", "FRED_TEST_TLS_CREDS") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -FEATURES="enable-rustls-ring transactions i-all network-logs debug-ids" - -if [ -z "$FRED_CI_NEXTEST" ]; then - FRED_CI_TLS=true cargo test --release --lib --tests --features "$FEATURES" -- --test-threads=1 "$@" -else - FRED_CI_TLS=true cargo nextest run --release --lib --tests --features "$FEATURES" --test-threads=1 "$@" -fi \ No newline at end of file diff --git a/tests/docker/runners/bash/cluster-rustls.sh b/tests/docker/runners/bash/cluster-rustls.sh deleted file mode 100755 index 572a7734..00000000 --- a/tests/docker/runners/bash/cluster-rustls.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -declare -a arr=("REDIS_VERSION" "REDIS_USERNAME" "REDIS_PASSWORD", "FRED_TEST_TLS_CREDS") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -FEATURES="enable-rustls transactions i-all" - -if [ -z "$FRED_CI_NEXTEST" ]; then - FRED_CI_TLS=true cargo test --release --lib --tests --features "$FEATURES" -- --test-threads=1 "$@" -else - FRED_CI_TLS=true cargo nextest run --release --lib --tests --features "$FEATURES" --test-threads=1 "$@" -fi \ No newline at end of file diff --git a/tests/docker/runners/bash/cluster-tls.sh b/tests/docker/runners/bash/cluster-tls.sh deleted file mode 100755 index 93e5c465..00000000 --- a/tests/docker/runners/bash/cluster-tls.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -declare -a arr=("REDIS_VERSION" "REDIS_USERNAME" "REDIS_PASSWORD", "FRED_TEST_TLS_CREDS") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -FEATURES="enable-native-tls vendored-openssl transactions i-all" -# https://github.com/sfackler/rust-native-tls/issues/143 -echo "This may not work on Mac" - -if [ -z "$FRED_CI_NEXTEST" ]; then - FRED_CI_TLS=true cargo test --release --lib --tests --features "$FEATURES" -- --test-threads=1 "$@" -else - FRED_CI_TLS=true cargo nextest run --release --lib --tests --features "$FEATURES" --test-threads=1 "$@" -fi \ No newline at end of file diff --git a/tests/docker/runners/bash/default-features.sh b/tests/docker/runners/bash/default-features.sh deleted file mode 100755 index f147c3eb..00000000 --- a/tests/docker/runners/bash/default-features.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -declare -a arr=("REDIS_VERSION" "REDIS_USERNAME" "REDIS_PASSWORD" "REDIS_SENTINEL_PASSWORD") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -if [ -z "$FRED_CI_NEXTEST" ]; then - cargo test --release --lib --tests --features "i-all" -- --test-threads=1 "$@" -else - cargo nextest run --release --lib --tests --features "i-all" --test-threads=1 "$@" -fi \ No newline at end of file diff --git a/tests/docker/runners/bash/default-nil-types.sh b/tests/docker/runners/bash/default-nil-types.sh deleted file mode 100755 index 8e7aa87c..00000000 --- a/tests/docker/runners/bash/default-nil-types.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -declare -a arr=("REDIS_VERSION" "REDIS_USERNAME" "REDIS_PASSWORD" "REDIS_SENTINEL_PASSWORD") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -FEATURES="network-logs serde-json debug-ids replicas i-all default-nil-types" - -if [ -z "$FRED_CI_NEXTEST" ]; then - cargo test --release --lib --tests --features "$FEATURES" -- --test-threads=1 "$@" -else - cargo nextest run --release --lib --tests --features "$FEATURES" --test-threads=1 "$@" -fi \ No newline at end of file diff --git a/tests/docker/runners/bash/everything.sh b/tests/docker/runners/bash/everything.sh deleted file mode 100755 index 39b23894..00000000 --- a/tests/docker/runners/bash/everything.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -./tests/runners/default-features.sh \ - && ./tests/runners/no-features.sh \ - && ./tests/runners/all-features.sh \ - && ./tests/runners/sentinel-features.sh \ No newline at end of file diff --git a/tests/docker/runners/bash/mocks.sh b/tests/docker/runners/bash/mocks.sh deleted file mode 100755 index 585fd118..00000000 --- a/tests/docker/runners/bash/mocks.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -if [ -z "$FRED_CI_NEXTEST" ]; then - cargo test --release --lib --features "mocks i-keys" -else - cargo nextest run --release --lib --features "mocks i-keys" -fi \ No newline at end of file diff --git a/tests/docker/runners/bash/no-features.sh b/tests/docker/runners/bash/no-features.sh deleted file mode 100755 index bef0d77d..00000000 --- a/tests/docker/runners/bash/no-features.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -declare -a arr=("REDIS_VERSION" "REDIS_USERNAME" "REDIS_PASSWORD" "REDIS_SENTINEL_PASSWORD") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -if [ -z "$FRED_CI_NEXTEST" ]; then - cargo test --release --lib --tests --no-default-features --features "i-all" -- --test-threads=1 "$@" -else - cargo nextest run --release --lib --tests --no-default-features --features "i-all" --test-threads=1 "$@" -fi \ No newline at end of file diff --git a/tests/docker/runners/bash/redis-stack.sh b/tests/docker/runners/bash/redis-stack.sh deleted file mode 100755 index b8eaa3f7..00000000 --- a/tests/docker/runners/bash/redis-stack.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -declare -a arr=("REDIS_VERSION" "REDIS_PASSWORD" "FRED_REDIS_STACK_HOST" "FRED_REDIS_STACK_PORT") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -FEATURES="network-logs serde-json debug-ids i-redis-stack i-all" - -if [ -z "$FRED_CI_NEXTEST" ]; then - cargo test --release --lib --tests --features "$FEATURES" -- --test-threads=1 "$@" -else - cargo nextest run --release --lib --tests --features "$FEATURES" --test-threads=1 "$@" -fi \ No newline at end of file diff --git a/tests/docker/runners/bash/sentinel-features.sh b/tests/docker/runners/bash/sentinel-features.sh deleted file mode 100755 index 6488ce10..00000000 --- a/tests/docker/runners/bash/sentinel-features.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -declare -a arr=("REDIS_VERSION" "REDIS_USERNAME" "REDIS_PASSWORD" "REDIS_SENTINEL_PASSWORD") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -FEATURES="network-logs debug-ids sentinel-auth replicas i-all" - -if [ -z "$FRED_CI_NEXTEST" ]; then - cargo test --release --lib --tests --features "$FEATURES" -- --test-threads=1 "$@" -else - cargo nextest run --release --lib --tests --features "$FEATURES" --test-threads=1 "$@" -fi \ No newline at end of file diff --git a/tests/docker/runners/bash/unix-socket.sh b/tests/docker/runners/bash/unix-socket.sh deleted file mode 100755 index f116027f..00000000 --- a/tests/docker/runners/bash/unix-socket.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -declare -a arr=("REDIS_VERSION" "REDIS_USERNAME" "REDIS_PASSWORD" "REDIS_SENTINEL_PASSWORD") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -FEATURES="network-logs subscriber-client debug-ids transactions unix-sockets i-all" - -if [ -z "$FRED_CI_NEXTEST" ]; then - cargo test --release --lib --tests --features "$FEATURES" -- --test-threads=1 "$@" -else - cargo nextest run --release --lib --tests --test-threads=1 --features "$FEATURES" "$@" -fi \ No newline at end of file diff --git a/tests/docker/runners/bash/valkey-cluster-node.sh b/tests/docker/runners/bash/valkey-cluster-node.sh deleted file mode 100755 index 146c905a..00000000 --- a/tests/docker/runners/bash/valkey-cluster-node.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash - -CLUSTER_HOST=127.0.0.1 -TIMEOUT=2000 - -if [[ -z "${VALKEYCLI_AUTH}" ]]; then - echo "Skipping authentication checks..." -else - # it's unclear which of these env variables have been changed to use "valkey" instead of "redis". at the time of - # writing there still seems to be several references that use the old names. - export REDIS_PASSWORD="${VALKEYCLI_AUTH}" - export VALKEY_PASSWORD="${VALKEYCLI_AUTH}" - export REDISCLI_AUTH="${VALKEYCLI_AUTH}" -fi - -function start_server { - [[ -z "${VALKEYCLI_AUTH}" ]] && AUTH_ARGV="" || AUTH_ARGV="--requirepass ${VALKEYCLI_AUTH} --masterauth ${VALKEYCLI_AUTH}" - [[ -z "${VALKEY_ACLFILE}" ]] && ACL_ARGV="" || ACL_ARGV="--aclfile ${VALKEY_ACLFILE}" - - valkey-server --port $VALKEY_PORT_NUMBER --cluster-enabled yes --cluster-config-file nodes-${VALKEY_PORT_NUMBER}.conf \ - --cluster-node-timeout $TIMEOUT --appendonly yes --appendfilename appendonly-${VALKEY_PORT_NUMBER}.aof \ - --loglevel verbose --appenddirname appendonlydir-${VALKEY_PORT_NUMBER} --dbfilename dump-${VALKEY_PORT_NUMBER}.rdb \ - --logfile ${VALKEY_PORT_NUMBER}.log --daemonize yes --enable-debug-command yes $AUTH_ARGV $ACL_ARGV - - echo $! > valkey-server.pid -} - -function wait_for_server { - [[ -z "${VALKEYCLI_AUTH}" ]] && AUTH_ARGV="" || AUTH_ARGV="-a ${VALKEYCLI_AUTH}" - - for i in `seq 1 10`; do - if [[ `valkey-cli -h $CLUSTER_HOST -p $VALKEY_PORT_NUMBER $AUTH_ARGV --raw PING` == "PONG" ]]; then - return - else - sleep 1 - fi - done - - echo "Timed out waiting for server to start." - exit 1 -} - -function create_cluster { - echo "Creating cluster..." - [[ -z "${VALKEYCLI_AUTH}" ]] && AUTH_ARGV="" || AUTH_ARGV="-a ${VALKEYCLI_AUTH}" - - HOSTS="" - for cluster_host in $VALKEY_NODES; do - HOSTS="$HOSTS $cluster_host:$VALKEY_PORT_NUMBER"; - done - - valkey-cli $AUTH_ARGV --cluster create $HOSTS --cluster-replicas $VALKEY_CLUSTER_REPLICAS --cluster-yes -} - -parse_config_file -start_server -wait_for_server -if [[ $VALKEY_CLUSTER_CREATOR == "yes" ]]; then - create_cluster -fi -# TODO fix this so docker can properly control the valkey server process -tail -f ${VALKEY_PORT_NUMBER}.log diff --git a/tests/docker/runners/compose/all-features.yml b/tests/docker/runners/compose/all-features.yml deleted file mode 100644 index b79b0b4b..00000000 --- a/tests/docker/runners/compose/all-features.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: '2' - -services: - all-features-tests: - depends_on: - - redis-main - - redis-cluster-6 - container_name: "all-features-tests" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - privileged: true - command: - - "/project/tests/docker/runners/bash/all-features.sh" - - "${TEST_ARGV}" - environment: - RUST_LOG: "${RUST_LOG}" - CIRCLECI_TESTS: "${CIRCLECI_TESTS}" - REDIS_VERSION: "${REDIS_VERSION}" - FRED_REDIS_CENTRALIZED_HOST: "${FRED_REDIS_CENTRALIZED_HOST}" - FRED_REDIS_CENTRALIZED_PORT: "${FRED_REDIS_CENTRALIZED_PORT}" - FRED_REDIS_CLUSTER_HOST: "${FRED_REDIS_CLUSTER_HOST}" - FRED_REDIS_CLUSTER_PORT: "${FRED_REDIS_CLUSTER_PORT}" - REDIS_USERNAME: "${REDIS_USERNAME}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - volumes: - - "../../..:/project" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/tests/docker/runners/compose/cluster-native-tls.yml b/tests/docker/runners/compose/cluster-native-tls.yml deleted file mode 100644 index 18243bd6..00000000 --- a/tests/docker/runners/compose/cluster-native-tls.yml +++ /dev/null @@ -1,30 +0,0 @@ -version: '2' - -services: - cluster-native-tls-tests: - depends_on: - - redis-cluster-tls-6 - container_name: "cluster-native-tls-tests" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - privileged: true - command: - - "/project/tests/docker/runners/bash/cluster-tls.sh" - - "${TEST_ARGV}" - environment: - RUST_LOG: "${RUST_LOG}" - CIRCLECI_TESTS: "${CIRCLECI_TESTS}" - REDIS_VERSION: "${REDIS_VERSION}" - FRED_REDIS_CLUSTER_TLS_HOST: "${FRED_REDIS_CLUSTER_TLS_HOST}" - FRED_REDIS_CLUSTER_TLS_PORT: "${FRED_REDIS_CLUSTER_TLS_PORT}" - REDIS_USERNAME: "${REDIS_USERNAME}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - FRED_TEST_TLS_CREDS: "/project/tests/tmp/creds" - volumes: - - "../../..:/project" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/tests/docker/runners/compose/cluster-rustls-ring.yml b/tests/docker/runners/compose/cluster-rustls-ring.yml deleted file mode 100644 index 635238a8..00000000 --- a/tests/docker/runners/compose/cluster-rustls-ring.yml +++ /dev/null @@ -1,30 +0,0 @@ -version: '2' - -services: - cluster-rustls-ring-tests: - depends_on: - - redis-cluster-tls-6 - container_name: "cluster-rustls-ring-tests" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - privileged: true - command: - - "/project/tests/docker/runners/bash/cluster-rustls-ring.sh" - - "${TEST_ARGV}" - environment: - RUST_LOG: "${RUST_LOG}" - CIRCLECI_TESTS: "${CIRCLECI_TESTS}" - REDIS_VERSION: "${REDIS_VERSION}" - FRED_REDIS_CLUSTER_TLS_HOST: "${FRED_REDIS_CLUSTER_TLS_HOST}" - FRED_REDIS_CLUSTER_TLS_PORT: "${FRED_REDIS_CLUSTER_TLS_PORT}" - REDIS_USERNAME: "${REDIS_USERNAME}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - FRED_TEST_TLS_CREDS: "/project/tests/tmp/creds" - volumes: - - "../../..:/project" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/tests/docker/runners/compose/cluster-rustls.yml b/tests/docker/runners/compose/cluster-rustls.yml deleted file mode 100644 index 008f4209..00000000 --- a/tests/docker/runners/compose/cluster-rustls.yml +++ /dev/null @@ -1,30 +0,0 @@ -version: '2' - -services: - cluster-rustls-tests: - depends_on: - - redis-cluster-tls-6 - container_name: "cluster-rustls-tests" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - privileged: true - command: - - "/project/tests/docker/runners/bash/cluster-rustls.sh" - - "${TEST_ARGV}" - environment: - RUST_LOG: "${RUST_LOG}" - CIRCLECI_TESTS: "${CIRCLECI_TESTS}" - REDIS_VERSION: "${REDIS_VERSION}" - FRED_REDIS_CLUSTER_TLS_HOST: "${FRED_REDIS_CLUSTER_TLS_HOST}" - FRED_REDIS_CLUSTER_TLS_PORT: "${FRED_REDIS_CLUSTER_TLS_PORT}" - REDIS_USERNAME: "${REDIS_USERNAME}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - FRED_TEST_TLS_CREDS: "/project/tests/tmp/creds" - volumes: - - "../../..:/project" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/tests/docker/runners/compose/default-features.yml b/tests/docker/runners/compose/default-features.yml deleted file mode 100644 index e0dbe794..00000000 --- a/tests/docker/runners/compose/default-features.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: '2' - -services: - default-features-tests: - depends_on: - - redis-main - - redis-cluster-6 - container_name: "default-features-tests" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - command: - - "/project/tests/docker/runners/bash/default-features.sh" - - "${TEST_ARGV}" - privileged: true - environment: - RUST_LOG: "${RUST_LOG}" - CIRCLECI_TESTS: "${CIRCLECI_TESTS}" - REDIS_VERSION: "${REDIS_VERSION}" - FRED_REDIS_CENTRALIZED_HOST: "${FRED_REDIS_CENTRALIZED_HOST}" - FRED_REDIS_CENTRALIZED_PORT: "${FRED_REDIS_CENTRALIZED_PORT}" - FRED_REDIS_CLUSTER_HOST: "${FRED_REDIS_CLUSTER_HOST}" - FRED_REDIS_CLUSTER_PORT: "${FRED_REDIS_CLUSTER_PORT}" - REDIS_USERNAME: "${REDIS_USERNAME}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - volumes: - - "../../..:/project" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/tests/docker/runners/compose/default-nil-types.yml b/tests/docker/runners/compose/default-nil-types.yml deleted file mode 100644 index c5e34385..00000000 --- a/tests/docker/runners/compose/default-nil-types.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: '2' - -services: - default-nil-types-tests: - depends_on: - - redis-main - - redis-cluster-6 - container_name: "default-nil-types-tests" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - command: - - "/project/tests/docker/runners/bash/default-nil-types.sh" - - "${TEST_ARGV}" - privileged: true - environment: - RUST_LOG: "${RUST_LOG}" - CIRCLECI_TESTS: "${CIRCLECI_TESTS}" - REDIS_VERSION: "${REDIS_VERSION}" - FRED_REDIS_CENTRALIZED_HOST: "${FRED_REDIS_CENTRALIZED_HOST}" - FRED_REDIS_CENTRALIZED_PORT: "${FRED_REDIS_CENTRALIZED_PORT}" - FRED_REDIS_CLUSTER_HOST: "${FRED_REDIS_CLUSTER_HOST}" - FRED_REDIS_CLUSTER_PORT: "${FRED_REDIS_CLUSTER_PORT}" - REDIS_USERNAME: "${REDIS_USERNAME}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - volumes: - - "../../..:/project" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/tests/docker/runners/compose/mocks.yml b/tests/docker/runners/compose/mocks.yml deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/docker/runners/compose/no-features.yml b/tests/docker/runners/compose/no-features.yml deleted file mode 100644 index 44f116c7..00000000 --- a/tests/docker/runners/compose/no-features.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: '2' - -services: - no-features-tests: - depends_on: - - redis-main - - redis-cluster-6 - container_name: "no-features-tests" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - privileged: true - command: - - "/project/tests/docker/runners/bash/no-features.sh" - - "${TEST_ARGV}" - environment: - RUST_LOG: "${RUST_LOG}" - CIRCLECI_TESTS: "${CIRCLECI_TESTS}" - REDIS_VERSION: "${REDIS_VERSION}" - FRED_REDIS_CENTRALIZED_HOST: "${FRED_REDIS_CENTRALIZED_HOST}" - FRED_REDIS_CENTRALIZED_PORT: "${FRED_REDIS_CENTRALIZED_PORT}" - FRED_REDIS_CLUSTER_HOST: "${FRED_REDIS_CLUSTER_HOST}" - FRED_REDIS_CLUSTER_PORT: "${FRED_REDIS_CLUSTER_PORT}" - REDIS_USERNAME: "${REDIS_USERNAME}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - volumes: - - "../../..:/project" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/tests/docker/runners/compose/redis-stack.yml b/tests/docker/runners/compose/redis-stack.yml deleted file mode 100644 index 87d6f8b6..00000000 --- a/tests/docker/runners/compose/redis-stack.yml +++ /dev/null @@ -1,28 +0,0 @@ -version: '2' - -services: - redis-stack-tests: - depends_on: - - redis-stack-main - container_name: "redis-stack-tests" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - command: - - "/project/tests/docker/runners/bash/redis-stack.sh" - - "${TEST_ARGV}" - privileged: true - environment: - RUST_LOG: "${RUST_LOG}" - CIRCLECI_TESTS: "${CIRCLECI_TESTS}" - REDIS_VERSION: "${REDIS_VERSION}" - FRED_REDIS_STACK_HOST: "${FRED_REDIS_STACK_HOST}" - FRED_REDIS_STACK_PORT: "${FRED_REDIS_STACK_PORT}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - volumes: - - "../../..:/project" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/tests/docker/runners/compose/sentinel-features.yml b/tests/docker/runners/compose/sentinel-features.yml deleted file mode 100644 index 44b6484e..00000000 --- a/tests/docker/runners/compose/sentinel-features.yml +++ /dev/null @@ -1,33 +0,0 @@ -version: '2' - -services: - sentinel-tests: - depends_on: - - redis-sentinel-main - - redis-sentinel-replica - - redis-sentinel-3 - container_name: "sentinel-tests" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - privileged: true - command: - - "/project/tests/docker/runners/bash/sentinel-features.sh" - - "${TEST_ARGV}" - environment: - RUST_LOG: "${RUST_LOG}" - CIRCLECI_TESTS: "${CIRCLECI_TESTS}" - REDIS_VERSION: "${REDIS_VERSION}" - FRED_REDIS_SENTINEL_HOST: "${FRED_REDIS_SENTINEL_HOST}" - FRED_REDIS_SENTINEL_PORT: "${FRED_REDIS_SENTINEL_PORT}" - REDIS_USERNAME: "${REDIS_USERNAME}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - REDIS_SENTINEL_PASSWORD: "${REDIS_SENTINEL_PASSWORD}" - FRED_SENTINEL_TESTS: "1" - volumes: - - "../../..:/project" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/tests/docker/runners/compose/unix-socket.yml b/tests/docker/runners/compose/unix-socket.yml deleted file mode 100644 index 2cacec05..00000000 --- a/tests/docker/runners/compose/unix-socket.yml +++ /dev/null @@ -1,36 +0,0 @@ -version: '2' - -services: - unix-socket-tmp: - image: busybox - command: "chmod -R 777 ${REDIS_UNIX_SOCK_CONTAINER_DIR}" - volumes: - - '${REDIS_UNIX_SOCK_HOST_DIR}:${REDIS_UNIX_SOCK_CONTAINER_DIR}' - unix-socket-tests: - depends_on: - - redis-main-unix-socket - container_name: "unix-socket-tests" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - networks: - - fred-tests - command: - - "/project/tests/docker/runners/bash/unix-socket.sh" - - "${TEST_ARGV}" - privileged: true - environment: - RUST_LOG: "${RUST_LOG}" - CIRCLECI_TESTS: "${CIRCLECI_TESTS}" - REDIS_VERSION: "${REDIS_VERSION}" - REDIS_UNIX_SOCK_CONTAINER_DIR: "${REDIS_UNIX_SOCK_CONTAINER_DIR}" - REDIS_UNIX_SOCK: "${REDIS_UNIX_SOCK}" - REDIS_USERNAME: "${REDIS_USERNAME}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - volumes: - - "../../..:/project" - - "~/.cargo/registry:/usr/local/cargo/registry" - volumes_from: - - unix-socket-tmp \ No newline at end of file diff --git a/tests/docker/runners/compose/valkey-all-features.yml b/tests/docker/runners/compose/valkey-all-features.yml deleted file mode 100644 index 6d009210..00000000 --- a/tests/docker/runners/compose/valkey-all-features.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: '2' - -services: - valkey-all-features-tests: - depends_on: - - valkey-main - - valkey-cluster-6 - container_name: "valkey-all-features-tests" - build: - context: ../../../ - dockerfile: tests/docker/runners/images/base.dockerfile - args: - REDIS_VERSION: "${REDIS_VERSION}" - VALKEY_VERSION: "${VALKEY_VERSION}" - networks: - - fred-tests - privileged: true - command: - - "/project/tests/docker/runners/bash/all-features.sh" - - "${TEST_ARGV}" - environment: - RUST_LOG: "${RUST_LOG}" - CIRCLECI_TESTS: "${CIRCLECI_TESTS}" - REDIS_VERSION: "${REDIS_VERSION}" - VALKEY_VERSION: "${VALKEY_VERSION}" - FRED_REDIS_CENTRALIZED_HOST: "${FRED_VALKEY_CENTRALIZED_HOST}" - FRED_REDIS_CENTRALIZED_PORT: "${FRED_VALKEY_CENTRALIZED_PORT}" - FRED_REDIS_CLUSTER_HOST: "${FRED_VALKEY_CLUSTER_HOST}" - FRED_REDIS_CLUSTER_PORT: "${FRED_VALKEY_CLUSTER_PORT}" - REDIS_USERNAME: "${REDIS_USERNAME}" - REDIS_PASSWORD: "${REDIS_PASSWORD}" - volumes: - - "../../..:/project" - - "~/.cargo/registry:/usr/local/cargo/registry" \ No newline at end of file diff --git a/tests/docker/runners/images/base.dockerfile b/tests/docker/runners/images/base.dockerfile deleted file mode 100644 index ca2e8f98..00000000 --- a/tests/docker/runners/images/base.dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -# https://github.com/docker/for-mac/issues/5548#issuecomment-1029204019 -# FROM rust:1.77-slim-buster -FROM rust:1.80-slim-bullseye - -WORKDIR /project - -ARG RUST_LOG -ARG REDIS_VERSION -ARG REDIS_USERNAME -ARG REDIS_PASSWORD -ARG REDIS_SENTINEL_PASSWORD -ARG FRED_REDIS_CLUSTER_HOST -ARG FRED_REDIS_CLUSTER_PORT -ARG FRED_REDIS_CLUSTER_TLS_HOST -ARG FRED_REDIS_CLUSTER_TLS_PORT -ARG FRED_REDIS_CENTRALIZED_HOST -ARG FRED_REDIS_CENTRALIZED_PORT -ARG FRED_REDIS_SENTINEL_HOST -ARG FRED_REDIS_SENTINEL_PORT -ARG CIRCLECI_TESTS - -RUN USER=root apt-get update && apt-get install -y build-essential libssl-dev dnsutils curl pkg-config cmake -RUN echo "REDIS_VERSION=$REDIS_VERSION" - -# For debugging -RUN cargo --version && rustc --version \ No newline at end of file diff --git a/tests/docker/runners/images/ci.dockerfile b/tests/docker/runners/images/ci.dockerfile deleted file mode 100644 index c787a76a..00000000 --- a/tests/docker/runners/images/ci.dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM rust:1.80-slim-buster - -WORKDIR /project -# circleci doesn't mount volumes with a remote docker engine so we have to copy everything -COPY --chown=1001:1001 . /project -COPY --chown=1001:1001 ~/.cargo/registry /usr/local/cargo/registry - -ARG RUST_LOG -ARG REDIS_VERSION -ARG REDIS_USERNAME -ARG REDIS_PASSWORD -ARG REDIS_SENTINEL_PASSWORD -ARG FRED_REDIS_CLUSTER_HOST -ARG FRED_REDIS_CLUSTER_PORT -ARG FRED_REDIS_CLUSTER_TLS_HOST -ARG FRED_REDIS_CLUSTER_TLS_PORT -ARG FRED_REDIS_CENTRALIZED_HOST -ARG FRED_REDIS_CENTRALIZED_PORT -ARG FRED_REDIS_SENTINEL_HOST -ARG FRED_REDIS_SENTINEL_PORT -ARG CIRCLECI_TESTS - -RUN USER=root apt-get update && apt-get install -y build-essential libssl-dev dnsutils cmake -RUN echo "REDIS_VERSION=$REDIS_VERSION" - -# For debugging -RUN cargo --version && rustc --version \ No newline at end of file diff --git a/tests/docker/runners/images/debug.dockerfile b/tests/docker/runners/images/debug.dockerfile deleted file mode 100644 index 6c85798d..00000000 --- a/tests/docker/runners/images/debug.dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -# https://github.com/docker/for-mac/issues/5548#issuecomment-1029204019 -# FROM rust:1.77-slim-buster -FROM rust:1.80-slim-bullseye - -WORKDIR /project - -ARG RUST_LOG -ARG REDIS_VERSION -ARG REDIS_USERNAME -ARG REDIS_PASSWORD -ARG REDIS_SENTINEL_PASSWORD -ARG FRED_REDIS_CLUSTER_HOST -ARG FRED_REDIS_CLUSTER_PORT -ARG FRED_REDIS_CLUSTER_TLS_HOST -ARG FRED_REDIS_CLUSTER_TLS_PORT -ARG FRED_REDIS_CENTRALIZED_HOST -ARG FRED_REDIS_CENTRALIZED_PORT -ARG FRED_REDIS_SENTINEL_HOST -ARG FRED_REDIS_SENTINEL_PORT -ARG CIRCLECI_TESTS - -RUN USER=root apt-get update && apt-get install -y build-essential libssl-dev dnsutils curl pkg-config cmake git -RUN echo "REDIS_VERSION=$REDIS_VERSION" - -# For debugging -RUN cargo --version && rustc --version -RUN rustup component add clippy \ No newline at end of file diff --git a/tests/docker/runners/images/valkey-cluster-node.dockerfile b/tests/docker/runners/images/valkey-cluster-node.dockerfile deleted file mode 100644 index be2317cf..00000000 --- a/tests/docker/runners/images/valkey-cluster-node.dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -# https://github.com/docker/for-mac/issues/5548#issuecomment-1029204019 -FROM valkey/valkey:7.2-bookworm -ARG VALKEY_VERSION -ARG VALKEY_PORT_NUMBER -ARG VALKEYCLI_AUTH -ARG VALKEY_NODES -ARG VALKEY_CLUSTER_REPLICAS -ARG VALKEY_CLUSTER_CREATOR -ARG VALKEY_ACLFILE - -COPY tests/docker/runners/bash/valkey-cluster-node.sh /usr/bin/ -ENTRYPOINT ["valkey-cluster-node.sh"] \ No newline at end of file diff --git a/tests/environ b/tests/environ deleted file mode 100644 index 7b8cd4bf..00000000 --- a/tests/environ +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/bash -. ./tests/scripts/utils.sh - -if [ -z "$REDIS_VERSION" ]; then - export REDIS_VERSION=7.2.4 -fi -if [ -z "$VALKEY_VERSION" ]; then - export VALKEY_VERSION=7.2.5 -fi - -if [ -z "$CARGO_HTTP_DEBUG" ]; then - export CARGO_HTTP_DEBUG=false -fi - -# FIXME: changing the redis stack port here doesn't work. this might just be a limitation on the image's config interface though -export ROOT=$PWD \ - RUST_BACKTRACE=full \ - FRED_REDIS_CLUSTER_HOST=redis-cluster-1 \ - FRED_VALKEY_CLUSTER_HOST=valkey-cluster-1 \ - FRED_REDIS_CLUSTER_PORT=30001 \ - FRED_VALKEY_CLUSTER_PORT=50001 \ - FRED_REDIS_CLUSTER_TLS_HOST=redis-cluster-tls-1 \ - FRED_REDIS_CLUSTER_TLS_PORT=40001 \ - FRED_REDIS_CENTRALIZED_HOST=redis-main \ - FRED_VALKEY_CENTRALIZED_HOST=valkey-main \ - FRED_REDIS_CENTRALIZED_PORT=6379 \ - FRED_VALKEY_CENTRALIZED_PORT=7379 \ - FRED_REDIS_SENTINEL_HOST=redis-sentinel-1 \ - FRED_REDIS_SENTINEL_PORT=26379 \ - FRED_REDIS_STACK_HOST=redis-stack-main \ - FRED_REDIS_STACK_PORT=6379 \ - FRED_TEST_TLS_CREDS=$PWD/tests/tmp/creds \ - REDIS_USERNAME=foo \ - REDIS_PASSWORD=bar \ - REDIS_SENTINEL_PASSWORD=baz \ - REDIS_UNIX_SOCK=redis-main.sock \ - REDIS_UNIX_SOCK_HOST_DIR=$PWD/tests/tmp/fred-redis-main \ - REDIS_UNIX_SOCK_CONTAINER_DIR=/opt/bitnami/redis/mounted-etc - -REDIS_CLI_PATH="$PWD/tests/tmp/redis_$REDIS_VERSION/redis-$REDIS_VERSION/src/redis-cli" - -if [ -z "${CIRCLECI_TESTS}" ]; then - read -p "Build redis-cli for Redis $REDIS_VERSION? [y/n]: " INSTALL_CLI - if [ "$INSTALL_CLI" = "y" ]; then - check_redis - if [[ "$?" -eq 0 ]]; then - install_redis - fi - fi -fi - -if [ -f "$REDIS_CLI_PATH" ]; then - alias fred_redis_cli='$REDIS_CLI_PATH "$@"' - echo "Use the \`fred_redis_cli\` alias to run redis-cli@$REDIS_VERSION" -fi - -# if [ -z "${CIRCLECI_TESTS}" ]; then -# read -p "Use cargo nextest? [y/n]: " NEXTEST -# if [ "$NEXTEST" = "y" ]; then -# export FRED_CI_NEXTEST=1 -# else -# unset FRED_CI_NEXTEST -# fi -#fi - -modify_etc_hosts - -# generate ACL users and redis.conf overrides for the docker images based on the environment variables above -echo "Setting up docker redis.conf overrides..." -truncate -s 0 $PWD/tests/users.acl -echo "user $REDIS_USERNAME on allkeys allcommands allchannels >$REDIS_PASSWORD" | tee -a $PWD/tests/users.acl > /dev/null -echo "user default on allkeys allcommands allchannels >$REDIS_PASSWORD" | tee -a $PWD/tests/users.acl > /dev/null - -# generate redis.conf overrides based on the env variables above -truncate -s 0 $PWD/tests/docker/overrides/default.conf -echo "enable-debug-command yes" | tee -a $PWD/tests/docker/overrides/default.conf > /dev/null -echo "masterauth $REDIS_PASSWORD" | tee -a $PWD/tests/docker/overrides/default.conf > /dev/null -echo "masteruser default" | tee -a $PWD/tests/docker/overrides/default.conf > /dev/null -echo "aclfile /opt/bitnami/redis/mounted-etc/users.acl" | tee -a $PWD/tests/docker/overrides/default.conf > /dev/null -echo "requirepass $REDIS_PASSWORD" | tee -a $PWD/tests/docker/overrides/default.conf > /dev/null -echo "loglevel verbose" | tee -a $PWD/tests/docker/overrides/default.conf > /dev/null - -# generate the unix socket config based on the variables above -cp $PWD/tests/docker/overrides/default.conf $PWD/tests/docker/overrides/unix-socket.conf -echo "unixsocket $REDIS_UNIX_SOCK_CONTAINER_DIR/$REDIS_UNIX_SOCK" | tee -a $PWD/tests/docker/overrides/unix-socket.conf > /dev/null -echo "unixsocketperm 770" | tee -a $PWD/tests/docker/overrides/unix-socket.conf > /dev/null - -if [ -z "${CIRCLECI_TESTS}" ]; then - check_cluster_credentials - if [[ "$?" -eq 0 ]]; then - generate_cluster_credentials - fi -fi - -if [ -z "${CIRCLECI_TESTS}" ]; then - echo "Skip checking registry cache." -else - mkdir -p /home/circleci/.cargo/registry -fi \ No newline at end of file diff --git a/tests/integration/acl/mod.rs b/tests/integration/acl/mod.rs deleted file mode 100644 index 52a3673f..00000000 --- a/tests/integration/acl/mod.rs +++ /dev/null @@ -1,56 +0,0 @@ -use super::utils::{read_env_var, should_use_sentinel_config}; -use fred::{ - clients::RedisClient, - error::RedisError, - interfaces::*, - types::{RedisConfig, RedisValue}, -}; -use std::collections::HashMap; - -// the docker image we use for sentinel tests doesn't allow for configuring users, just passwords, -// so for the tests here we just use an empty username so it uses the `default` user -fn read_redis_username() -> Option { - if should_use_sentinel_config() { - None - } else { - read_env_var("REDIS_USERNAME") - } -} - -fn check_env_creds() -> (Option, Option) { - (read_redis_username(), read_env_var("REDIS_PASSWORD")) -} - -// note: currently this only works in CI against the centralized server -pub async fn should_auth_as_test_user(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let (username, password) = check_env_creds(); - if let Some(password) = password { - client.auth(username, password).await?; - client.ping().await?; - } - - Ok(()) -} - -// FIXME currently this only works in CI against the centralized server -pub async fn should_auth_as_test_user_via_config(_: RedisClient, mut config: RedisConfig) -> Result<(), RedisError> { - let (username, password) = check_env_creds(); - if let Some(password) = password { - config.username = username; - config.password = Some(password); - let client = RedisClient::new(config, None, None, None); - client.connect(); - client.wait_for_connect().await?; - client.ping().await?; - } - - Ok(()) -} - -pub async fn should_run_acl_getuser(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let user: HashMap = client.acl_getuser("default").await?; - let flags: Vec = user.get("flags").unwrap().clone().convert()?; - assert!(flags.contains(&"on".to_string())); - - Ok(()) -} diff --git a/tests/integration/centralized.rs b/tests/integration/centralized.rs deleted file mode 100644 index d6668bb2..00000000 --- a/tests/integration/centralized.rs +++ /dev/null @@ -1,412 +0,0 @@ -#[cfg(feature = "i-keys")] -mod keys { - centralized_test!(keys, should_handle_missing_keys); - centralized_test!(keys, should_set_and_get_a_value); - centralized_test!(keys, should_set_and_del_a_value); - centralized_test!(keys, should_set_with_get_argument); - centralized_test!(keys, should_incr_and_decr_a_value); - centralized_test!(keys, should_incr_by_float); - centralized_test!(keys, should_mset_a_non_empty_map); - centralized_test_panic!(keys, should_error_mset_empty_map); - centralized_test!(keys, should_expire_key); - centralized_test!(keys, should_pexpire_key); - centralized_test!(keys, should_persist_key); - centralized_test!(keys, should_check_ttl); - centralized_test!(keys, should_check_pttl); - centralized_test!(keys, should_dump_key); - centralized_test!(keys, should_dump_and_restore_key); - centralized_test!(keys, should_modify_ranges); - centralized_test!(keys, should_getset_value); - centralized_test!(keys, should_getdel_value); - centralized_test!(keys, should_get_strlen); - centralized_test!(keys, should_mget_values); - centralized_test!(keys, should_msetnx_values); - centralized_test!(keys, should_copy_values); - centralized_test!(keys, should_unlink); - centralized_test_panic!(keys, should_error_rename_does_not_exist); - centralized_test_panic!(keys, should_error_renamenx_does_not_exist); - centralized_test!(keys, should_rename); - centralized_test!(keys, should_renamenx); - - centralized_test!(keys, should_get_keys_from_pool_in_a_stream); -} - -#[cfg(all(feature = "transactions", feature = "i-keys"))] -mod multi { - - centralized_test!(multi, should_run_get_set_trx); - centralized_test_panic!(multi, should_run_error_get_set_trx); -} - -mod other { - centralized_test!(other, should_connect_correctly_via_init_interface); - centralized_test!(other, should_fail_with_bad_host_via_init_interface); - centralized_test!(other, should_connect_correctly_via_wait_interface); - centralized_test!(other, should_fail_with_bad_host_via_wait_interface); - centralized_test!(other, pool_should_connect_correctly_via_init_interface); - centralized_test!(other, pool_should_fail_with_bad_host_via_init_interface); - centralized_test!(other, pool_should_connect_correctly_via_wait_interface); - centralized_test!(other, pool_should_fail_with_bad_host_via_wait_interface); - centralized_test!(other, should_fail_on_centralized_connect); - - #[cfg(feature = "metrics")] - centralized_test!(other, should_track_size_stats); - #[cfg(all(feature = "i-client", feature = "i-lists"))] - centralized_test!(other, should_automatically_unblock); - #[cfg(all(feature = "i-client", feature = "i-lists"))] - centralized_test!(other, should_manually_unblock); - #[cfg(all(feature = "i-client", feature = "i-lists"))] - centralized_test!(other, should_error_when_blocked); - #[cfg(all(feature = "i-keys", feature = "i-hashes"))] - centralized_test!(other, should_smoke_test_from_redis_impl); - centralized_test!(other, should_safely_change_protocols_repeatedly); - #[cfg(feature = "i-keys")] - centralized_test!(other, should_pipeline_all); - #[cfg(all(feature = "i-keys", feature = "i-hashes"))] - centralized_test!(other, should_pipeline_all_error_early); - #[cfg(feature = "i-keys")] - centralized_test!(other, should_pipeline_last); - #[cfg(all(feature = "i-keys", feature = "i-hashes"))] - centralized_test!(other, should_pipeline_try_all); - #[cfg(feature = "i-server")] - centralized_test!(other, should_use_all_cluster_nodes_repeatedly); - centralized_test!(other, should_gracefully_quit); - #[cfg(feature = "i-lists")] - centralized_test!(other, should_support_options_with_pipeline); - #[cfg(feature = "i-keys")] - centralized_test!(other, should_reuse_pipeline); - #[cfg(all(feature = "i-keys", feature = "i-lists"))] - centralized_test!(other, should_manually_connect_twice); - #[cfg(all(feature = "transactions", feature = "i-keys"))] - centralized_test!(other, should_support_options_with_trx); - - //#[cfg(feature = "dns")] - // centralized_test!(other, should_use_trust_dns); - // centralized_test!(other, should_test_high_concurrency_pool); - - #[cfg(all(feature = "partial-tracing", feature = "i-keys"))] - centralized_test!(other, should_use_tracing_get_set); - #[cfg(feature = "subscriber-client")] - centralized_test!(other, should_ping_with_subscriber_client); - - #[cfg(all(feature = "replicas", feature = "i-keys"))] - centralized_test!(other, should_replica_set_and_get); - #[cfg(all(feature = "replicas", feature = "i-keys"))] - centralized_test!(other, should_replica_set_and_get_not_lazy); - #[cfg(all(feature = "replicas", feature = "i-keys"))] - centralized_test!(other, should_pipeline_with_replicas); -} - -mod pool { - centralized_test!(pool, should_connect_and_ping_static_pool_single_conn); - centralized_test!(pool, should_connect_and_ping_static_pool_two_conn); - #[cfg(feature = "i-keys")] - centralized_test!(pool, should_incr_exclusive_pool); - #[cfg(all(feature = "i-keys", feature = "transactions"))] - centralized_test!(pool, should_watch_and_trx_exclusive_pool); -} - -#[cfg(feature = "i-hashes")] -mod hashes { - centralized_test!(hashes, should_hset_and_hget); - centralized_test!(hashes, should_hset_and_hdel); - centralized_test!(hashes, should_hexists); - centralized_test!(hashes, should_hgetall); - centralized_test!(hashes, should_hincryby); - centralized_test!(hashes, should_hincryby_float); - centralized_test!(hashes, should_get_keys); - centralized_test!(hashes, should_hmset); - centralized_test!(hashes, should_hmget); - centralized_test!(hashes, should_hsetnx); - centralized_test!(hashes, should_get_random_field); - centralized_test!(hashes, should_get_strlen); - centralized_test!(hashes, should_get_values); -} - -#[cfg(feature = "i-pubsub")] -mod pubsub { - centralized_test!(pubsub, should_publish_and_recv_messages); - centralized_test!(pubsub, should_psubscribe_and_recv_messages); - centralized_test!(pubsub, should_unsubscribe_from_all); - - centralized_test!(pubsub, should_get_pubsub_channels); - centralized_test!(pubsub, should_get_pubsub_numpat); - centralized_test!(pubsub, should_get_pubsub_nunmsub); - centralized_test!(pubsub, should_get_pubsub_shard_channels); - centralized_test!(pubsub, should_get_pubsub_shard_numsub); -} - -#[cfg(feature = "i-hyperloglog")] -mod hyperloglog { - - centralized_test!(hyperloglog, should_pfadd_elements); - centralized_test!(hyperloglog, should_pfcount_elements); - centralized_test!(hyperloglog, should_pfmerge_elements); -} - -mod scanning { - - #[cfg(feature = "i-keys")] - cluster_test!(scanning, should_scan_keyspace); - #[cfg(feature = "i-hashes")] - cluster_test!(scanning, should_hscan_hash); - #[cfg(feature = "i-sets")] - cluster_test!(scanning, should_sscan_set); - #[cfg(feature = "i-sorted-sets")] - cluster_test!(scanning, should_zscan_sorted_set); -} - -#[cfg(feature = "i-slowlog")] -mod slowlog { - - centralized_test!(slowlog, should_read_slowlog_length); - centralized_test!(slowlog, should_read_slowlog_entries); - centralized_test!(slowlog, should_reset_slowlog); -} - -#[cfg(feature = "i-server")] -mod server { - - centralized_test!(server, should_flushall); - centralized_test!(server, should_read_server_info); - centralized_test!(server, should_ping_server); - centralized_test!(server, should_run_custom_command); - centralized_test!(server, should_read_last_save); - centralized_test!(server, should_read_db_size); - centralized_test!(server, should_start_bgsave); - centralized_test!(server, should_do_bgrewriteaof); -} - -#[cfg(feature = "i-sets")] -mod sets { - - centralized_test!(sets, should_sadd_elements); - centralized_test!(sets, should_scard_elements); - centralized_test!(sets, should_sdiff_elements); - centralized_test!(sets, should_sdiffstore_elements); - centralized_test!(sets, should_sinter_elements); - centralized_test!(sets, should_sinterstore_elements); - centralized_test!(sets, should_check_sismember); - centralized_test!(sets, should_check_smismember); - centralized_test!(sets, should_read_smembers); - centralized_test!(sets, should_smove_elements); - centralized_test!(sets, should_spop_elements); - centralized_test!(sets, should_get_random_member); - centralized_test!(sets, should_remove_elements); - centralized_test!(sets, should_sunion_elements); - centralized_test!(sets, should_sunionstore_elements); -} - -#[cfg(feature = "i-memory")] -pub mod memory { - - centralized_test!(memory, should_run_memory_doctor); - centralized_test!(memory, should_run_memory_malloc_stats); - centralized_test!(memory, should_run_memory_purge); - centralized_test!(memory, should_run_memory_stats); - centralized_test!(memory, should_run_memory_usage); -} - -#[cfg(feature = "i-scripts")] -pub mod lua { - - #[cfg(feature = "sha-1")] - centralized_test!(lua, should_load_script); - centralized_test!(lua, should_eval_echo_script); - #[cfg(feature = "sha-1")] - centralized_test!(lua, should_eval_get_script); - #[cfg(feature = "sha-1")] - centralized_test!(lua, should_evalsha_echo_script); - #[cfg(feature = "sha-1")] - centralized_test!(lua, should_evalsha_with_reload_echo_script); - #[cfg(feature = "sha-1")] - centralized_test!(lua, should_evalsha_get_script); - - centralized_test!(lua, should_function_load_scripts); - centralized_test!(lua, should_function_dump_and_restore); - centralized_test!(lua, should_function_flush); - centralized_test!(lua, should_function_delete); - centralized_test!(lua, should_function_list); - centralized_test!(lua, should_function_list_multiple); - #[cfg(feature = "i-keys")] - centralized_test!(lua, should_function_fcall_getset); - centralized_test!(lua, should_function_fcall_echo); - centralized_test!(lua, should_function_fcall_ro_echo); - - #[cfg(feature = "sha-1")] - centralized_test!(lua, should_create_lua_script_helper_from_code); - #[cfg(feature = "sha-1")] - centralized_test!(lua, should_create_lua_script_helper_from_hash); - centralized_test!(lua, should_create_function_from_code); - centralized_test!(lua, should_create_function_from_name); -} - -#[cfg(feature = "i-sorted-sets")] -pub mod sorted_sets { - - centralized_test!(sorted_sets, should_bzpopmin); - centralized_test!(sorted_sets, should_bzpopmax); - centralized_test!(sorted_sets, should_zadd_values); - centralized_test!(sorted_sets, should_zcard_values); - centralized_test!(sorted_sets, should_zcount_values); - centralized_test!(sorted_sets, should_zdiff_values); - centralized_test!(sorted_sets, should_zdiffstore_values); - centralized_test!(sorted_sets, should_zincrby_values); - centralized_test!(sorted_sets, should_zinter_values); - centralized_test!(sorted_sets, should_zinterstore_values); - centralized_test!(sorted_sets, should_zlexcount); - centralized_test!(sorted_sets, should_zpopmax); - centralized_test!(sorted_sets, should_zpopmin); - centralized_test!(sorted_sets, should_zrandmember); - centralized_test!(sorted_sets, should_zrangestore_values); - centralized_test!(sorted_sets, should_zrangebylex); - centralized_test!(sorted_sets, should_zrevrangebylex); - centralized_test!(sorted_sets, should_zrangebyscore); - centralized_test!(sorted_sets, should_zrevrangebyscore); - centralized_test!(sorted_sets, should_zrank_values); - centralized_test!(sorted_sets, should_zrem_values); - centralized_test!(sorted_sets, should_zremrangebylex); - centralized_test!(sorted_sets, should_zremrangebyrank); - centralized_test!(sorted_sets, should_zremrangebyscore); - centralized_test!(sorted_sets, should_zrevrank_values); - centralized_test!(sorted_sets, should_zscore_values); - centralized_test!(sorted_sets, should_zunion_values); - centralized_test!(sorted_sets, should_zunionstore_values); - centralized_test!(sorted_sets, should_zmscore_values); - centralized_test!(sorted_sets, should_zrangebyscore_neg_infinity); -} - -#[cfg(feature = "i-lists")] -pub mod lists { - centralized_test!(lists, should_blpop_values); - centralized_test!(lists, should_brpop_values); - centralized_test!(lists, should_brpoplpush_values); - centralized_test!(lists, should_blmove_values); - - centralized_test!(lists, should_lindex_values); - centralized_test!(lists, should_linsert_values); - centralized_test!(lists, should_lpop_values); - centralized_test!(lists, should_lpos_values); - centralized_test!(lists, should_lpush_values); - centralized_test!(lists, should_lpushx_values); - centralized_test!(lists, should_lrange_values); - centralized_test!(lists, should_lrem_values); - centralized_test!(lists, should_lset_values); - #[cfg(feature = "i-keys")] - centralized_test!(lists, should_ltrim_values); - centralized_test!(lists, should_rpop_values); - centralized_test!(lists, should_rpoplpush_values); - centralized_test!(lists, should_lmove_values); - centralized_test!(lists, should_rpush_values); - centralized_test!(lists, should_rpushx_values); - centralized_test!(lists, should_sort_int_list); - centralized_test!(lists, should_sort_alpha_list); - centralized_test!(lists, should_sort_int_list_with_limit); - #[cfg(feature = "i-keys")] - centralized_test!(lists, should_sort_int_list_with_patterns); -} - -#[cfg(feature = "i-geo")] -pub mod geo { - - centralized_test!(geo, should_geoadd_values); - centralized_test!(geo, should_geohash_values); - centralized_test!(geo, should_geopos_values); - centralized_test!(geo, should_geodist_values); - centralized_test!(geo, should_georadius_values); - centralized_test!(geo, should_georadiusbymember_values); - centralized_test!(geo, should_geosearch_values); -} - -#[cfg(feature = "i-acl")] -pub mod acl { - centralized_test!(acl, should_auth_as_test_user); - centralized_test!(acl, should_auth_as_test_user_via_config); - centralized_test!(acl, should_run_acl_getuser); -} - -#[cfg(feature = "i-streams")] -mod streams { - centralized_test!(streams, should_xinfo_consumers); - centralized_test!(streams, should_xinfo_groups); - centralized_test!(streams, should_xinfo_streams); - centralized_test!(streams, should_xadd_auto_id_to_a_stream); - centralized_test!(streams, should_xadd_manual_id_to_a_stream); - centralized_test!(streams, should_xadd_with_cap_to_a_stream); - centralized_test!(streams, should_xadd_nomkstream_to_a_stream); - centralized_test!(streams, should_xtrim_a_stream_approx_cap); - centralized_test!(streams, should_xtrim_a_stream_eq_cap); - centralized_test!(streams, should_xdel_one_id_in_a_stream); - centralized_test!(streams, should_xdel_multiple_ids_in_a_stream); - centralized_test!(streams, should_xrange_no_count); - centralized_test!(streams, should_xrange_with_count); - centralized_test!(streams, should_xrange_values_no_count); - centralized_test!(streams, should_xrevrange_no_count); - centralized_test!(streams, should_xrevrange_with_count); - centralized_test!(streams, should_xrevrange_values_no_count); - centralized_test!(streams, should_run_xlen_on_stream); - centralized_test!(streams, should_xread_one_key_count_1); - centralized_test!(streams, should_xread_map_one_key); - centralized_test!(streams, should_xread_multiple_keys_count_2); - centralized_test!(streams, should_xread_with_blocking); - centralized_test!(streams, should_xgroup_create_no_mkstream); - centralized_test!(streams, should_xgroup_create_mkstream); - centralized_test!(streams, should_xgroup_createconsumer); - centralized_test!(streams, should_xgroup_delconsumer); - centralized_test!(streams, should_xgroup_destroy); - centralized_test!(streams, should_xgroup_setid); - centralized_test!(streams, should_xreadgroup_one_stream); - centralized_test!(streams, should_xreadgroup_multiple_stream); - centralized_test!(streams, should_xreadgroup_block); - centralized_test!(streams, should_xack_one_id); - centralized_test!(streams, should_xack_multiple_ids); - centralized_test!(streams, should_xclaim_one_id); - centralized_test!(streams, should_xclaim_multiple_ids); - centralized_test!(streams, should_xclaim_with_justid); - centralized_test!(streams, should_xautoclaim_default); -} - -#[cfg(feature = "i-tracking")] -mod tracking { - #[cfg(feature = "i-keys")] - centralized_test!(tracking, should_invalidate_foo_resp3); - #[cfg(feature = "i-keys")] - centralized_test!(tracking, should_invalidate_foo_resp2_centralized); -} - -// The CI settings for redis-stack only support centralized configs for now. -#[cfg(feature = "i-redis-json")] -mod redis_json { - centralized_test!(redis_json, should_get_and_set_basic_obj); - centralized_test!(redis_json, should_get_and_set_stringified_obj); - centralized_test!(redis_json, should_array_append); - centralized_test!(redis_json, should_modify_arrays); - centralized_test!(redis_json, should_pop_and_trim_arrays); - centralized_test!(redis_json, should_get_set_del_obj); - centralized_test!(redis_json, should_merge_objects); - centralized_test!(redis_json, should_mset_and_mget); - centralized_test!(redis_json, should_incr_numbers); - centralized_test!(redis_json, should_inspect_objects); - centralized_test!(redis_json, should_modify_strings); - centralized_test!(redis_json, should_toggle_boolean); - centralized_test!(redis_json, should_get_value_type); -} - -#[cfg(feature = "i-time-series")] -mod timeseries { - centralized_test!(timeseries, should_ts_add_get_and_range); - centralized_test!(timeseries, should_create_alter_and_del_timeseries); - centralized_test!(timeseries, should_madd_and_mget); - centralized_test!(timeseries, should_incr_and_decr); - centralized_test!(timeseries, should_create_and_delete_rules); - centralized_test!(timeseries, should_madd_and_mrange); - centralized_test!(timeseries, should_madd_and_mrevrange); -} - -#[cfg(feature = "i-redisearch")] -mod redisearch { - centralized_test!(redisearch, should_list_indexes); - centralized_test!(redisearch, should_index_and_info_basic_hash); - centralized_test!(redisearch, should_index_and_search_hash); - centralized_test!(redisearch, should_index_and_aggregate_timestamps); -} diff --git a/tests/integration/client/mod.rs b/tests/integration/client/mod.rs deleted file mode 100644 index 8b137891..00000000 --- a/tests/integration/client/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/integration/cluster/mod.rs b/tests/integration/cluster/mod.rs deleted file mode 100644 index 36f5eed7..00000000 --- a/tests/integration/cluster/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![allow(unused_imports)] -use fred::{error::RedisError, interfaces::*, prelude::RedisClient, types::RedisConfig}; - -#[cfg(all(feature = "i-cluster", feature = "i-client"))] -pub async fn should_use_each_cluster_node(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let connections = client.active_connections().await?; - - let mut servers = Vec::new(); - for server in connections.iter() { - let server_addr = client - .with_cluster_node(server) - .client_info::() - .await? - .split(' ') - .find_map(|s| { - let parts: Vec<&str> = s.split('=').collect(); - if parts[0] == "laddr" { - Some(parts[1].to_owned()) - } else { - None - } - }) - .expect("Failed to read or parse client info."); - - assert_eq!(server_addr, format!("{}", server)); - servers.push(server_addr); - } - - assert_eq!(servers.len(), connections.len()); - Ok(()) -} diff --git a/tests/integration/clustered.rs b/tests/integration/clustered.rs deleted file mode 100644 index 24153a00..00000000 --- a/tests/integration/clustered.rs +++ /dev/null @@ -1,401 +0,0 @@ -#[cfg(feature = "i-keys")] -mod keys { - cluster_test!(keys, should_handle_missing_keys); - cluster_test!(keys, should_set_and_get_a_value); - cluster_test!(keys, should_set_and_del_a_value); - cluster_test!(keys, should_set_with_get_argument); - cluster_test!(keys, should_incr_and_decr_a_value); - cluster_test!(keys, should_incr_by_float); - cluster_test!(keys, should_mset_a_non_empty_map); - cluster_test_panic!(keys, should_error_mset_empty_map); - cluster_test!(keys, should_expire_key); - cluster_test!(keys, should_pexpire_key); - cluster_test!(keys, should_persist_key); - cluster_test!(keys, should_check_ttl); - cluster_test!(keys, should_check_pttl); - cluster_test!(keys, should_dump_key); - cluster_test!(keys, should_dump_and_restore_key); - cluster_test!(keys, should_modify_ranges); - cluster_test!(keys, should_getset_value); - cluster_test!(keys, should_getdel_value); - cluster_test!(keys, should_get_strlen); - cluster_test!(keys, should_mget_values); - cluster_test!(keys, should_msetnx_values); - cluster_test!(keys, should_copy_values); - - cluster_test!(keys, should_unlink); - cluster_test_panic!(keys, should_error_rename_does_not_exist); - cluster_test_panic!(keys, should_error_renamenx_does_not_exist); - cluster_test!(keys, should_rename); - cluster_test!(keys, should_renamenx); - - cluster_test!(keys, should_get_keys_from_pool_in_a_stream); -} - -#[cfg(all(feature = "transactions", feature = "i-keys"))] -mod multi { - - cluster_test!(multi, should_run_get_set_trx); - cluster_test_panic!(multi, should_fail_with_hashslot_error); - cluster_test_panic!(multi, should_run_error_get_set_trx); -} - -mod other { - cluster_test!(other, should_connect_correctly_via_init_interface); - cluster_test!(other, should_fail_with_bad_host_via_init_interface); - cluster_test!(other, should_connect_correctly_via_wait_interface); - cluster_test!(other, should_fail_with_bad_host_via_wait_interface); - cluster_test!(other, pool_should_connect_correctly_via_init_interface); - cluster_test!(other, pool_should_fail_with_bad_host_via_init_interface); - cluster_test!(other, pool_should_connect_correctly_via_wait_interface); - cluster_test!(other, pool_should_fail_with_bad_host_via_wait_interface); - - #[cfg(feature = "metrics")] - cluster_test!(other, should_track_size_stats); - - cluster_test!(other, should_split_clustered_connection); - #[cfg(feature = "i-server")] - cluster_test!(other, should_run_flushall_cluster); - #[cfg(all(feature = "i-client", feature = "i-lists"))] - cluster_test!(other, should_automatically_unblock); - #[cfg(all(feature = "i-client", feature = "i-lists"))] - cluster_test!(other, should_manually_unblock); - #[cfg(all(feature = "i-client", feature = "i-lists"))] - cluster_test!(other, should_error_when_blocked); - cluster_test!(other, should_safely_change_protocols_repeatedly); - #[cfg(feature = "i-keys")] - cluster_test!(other, should_pipeline_all); - #[cfg(all(feature = "i-keys", feature = "i-hashes"))] - cluster_test!(other, should_pipeline_all_error_early); - #[cfg(feature = "i-keys")] - cluster_test!(other, should_pipeline_last); - #[cfg(all(feature = "i-keys", feature = "i-hashes"))] - cluster_test!(other, should_pipeline_try_all); - #[cfg(feature = "i-server")] - cluster_test!(other, should_use_all_cluster_nodes_repeatedly); - cluster_test!(other, should_gracefully_quit); - #[cfg(feature = "i-lists")] - cluster_test!(other, should_support_options_with_pipeline); - #[cfg(feature = "i-keys")] - cluster_test!(other, should_reuse_pipeline); - #[cfg(all(feature = "i-keys", feature = "i-lists"))] - cluster_test!(other, should_manually_connect_twice); - #[cfg(all(feature = "transactions", feature = "i-keys"))] - cluster_test!(other, should_support_options_with_trx); - - //#[cfg(feature = "dns")] - // cluster_test!(other, should_use_trust_dns); - #[cfg(all(feature = "partial-tracing", feature = "i-keys"))] - cluster_test!(other, should_use_tracing_get_set); - #[cfg(feature = "subscriber-client")] - cluster_test!(other, should_ping_with_subscriber_client); - - #[cfg(all(feature = "replicas", feature = "i-keys"))] - cluster_test!(other, should_replica_set_and_get); - #[cfg(all(feature = "replicas", feature = "i-keys"))] - cluster_test!(other, should_replica_set_and_get_not_lazy); - #[cfg(all(feature = "replicas", feature = "i-keys"))] - cluster_test!(other, should_use_cluster_replica_without_redirection); - //#[cfg(all(feature = "replicas", feature = "i-keys"))] - // cluster_test!(other, should_combine_options_and_replicas); - #[cfg(all(feature = "replicas", feature = "i-keys"))] - cluster_test!(other, should_pipeline_with_replicas); -} - -mod pool { - cluster_test!(pool, should_connect_and_ping_static_pool_single_conn); - cluster_test!(pool, should_connect_and_ping_static_pool_two_conn); - #[cfg(feature = "i-keys")] - cluster_test!(pool, should_incr_exclusive_pool); - #[cfg(all(feature = "i-keys", feature = "transactions"))] - cluster_test!(pool, should_watch_and_trx_exclusive_pool); -} - -#[cfg(feature = "i-hashes")] -mod hashes { - - cluster_test!(hashes, should_hset_and_hget); - cluster_test!(hashes, should_hset_and_hdel); - cluster_test!(hashes, should_hexists); - cluster_test!(hashes, should_hgetall); - cluster_test!(hashes, should_hincryby); - cluster_test!(hashes, should_hincryby_float); - cluster_test!(hashes, should_get_keys); - cluster_test!(hashes, should_hmset); - cluster_test!(hashes, should_hmget); - cluster_test!(hashes, should_hsetnx); - cluster_test!(hashes, should_get_random_field); - cluster_test!(hashes, should_get_strlen); - cluster_test!(hashes, should_get_values); -} - -#[cfg(feature = "i-pubsub")] -mod pubsub { - - cluster_test!(pubsub, should_publish_and_recv_messages); - cluster_test!(pubsub, should_ssubscribe_and_recv_messages); - cluster_test!(pubsub, should_psubscribe_and_recv_messages); - cluster_test!(pubsub, should_unsubscribe_from_all); - - // TODO fix these tests so they work with clusters. the connection management logic could be better. - // cluster_test!(pubsub, should_get_pubsub_channels); - // cluster_test!(pubsub, should_get_pubsub_numpat); - // cluster_test!(pubsub, should_get_pubsub_nunmsub); - cluster_test!(pubsub, should_get_pubsub_shard_channels); - cluster_test!(pubsub, should_get_pubsub_shard_numsub); -} - -#[cfg(feature = "i-hyperloglog")] -mod hyperloglog { - - cluster_test!(hyperloglog, should_pfadd_elements); - cluster_test!(hyperloglog, should_pfcount_elements); - cluster_test!(hyperloglog, should_pfmerge_elements); -} - -mod scanning { - - #[cfg(feature = "i-keys")] - cluster_test!(scanning, should_scan_keyspace); - #[cfg(feature = "i-hashes")] - cluster_test!(scanning, should_hscan_hash); - #[cfg(feature = "i-sets")] - cluster_test!(scanning, should_sscan_set); - #[cfg(feature = "i-sorted-sets")] - cluster_test!(scanning, should_zscan_sorted_set); - #[cfg(feature = "i-keys")] - cluster_test!(scanning, should_scan_cluster); -} - -#[cfg(feature = "i-slowlog")] -mod slowlog { - - cluster_test!(slowlog, should_read_slowlog_length); - cluster_test!(slowlog, should_read_slowlog_entries); - cluster_test!(slowlog, should_reset_slowlog); -} - -#[cfg(feature = "i-server")] -mod server { - - cluster_test!(server, should_flushall); - cluster_test!(server, should_read_server_info); - cluster_test!(server, should_ping_server); - cluster_test!(server, should_run_custom_command); - cluster_test!(server, should_read_last_save); - cluster_test!(server, should_read_db_size); - cluster_test!(server, should_start_bgsave); - cluster_test!(server, should_do_bgrewriteaof); -} - -#[cfg(feature = "i-sets")] -mod sets { - - cluster_test!(sets, should_sadd_elements); - cluster_test!(sets, should_scard_elements); - cluster_test!(sets, should_sdiff_elements); - cluster_test!(sets, should_sdiffstore_elements); - cluster_test!(sets, should_sinter_elements); - cluster_test!(sets, should_sinterstore_elements); - cluster_test!(sets, should_check_sismember); - cluster_test!(sets, should_check_smismember); - cluster_test!(sets, should_read_smembers); - cluster_test!(sets, should_smove_elements); - cluster_test!(sets, should_spop_elements); - cluster_test!(sets, should_get_random_member); - cluster_test!(sets, should_remove_elements); - cluster_test!(sets, should_sunion_elements); - cluster_test!(sets, should_sunionstore_elements); -} - -#[cfg(feature = "i-memory")] -pub mod memory { - - cluster_test!(memory, should_run_memory_doctor); - cluster_test!(memory, should_run_memory_malloc_stats); - cluster_test!(memory, should_run_memory_purge); - cluster_test!(memory, should_run_memory_stats); - cluster_test!(memory, should_run_memory_usage); -} - -#[cfg(feature = "i-scripts")] -pub mod lua { - - #[cfg(feature = "sha-1")] - cluster_test!(lua, should_load_script); - #[cfg(feature = "sha-1")] - cluster_test!(lua, should_load_script_cluster); - cluster_test!(lua, should_eval_echo_script); - #[cfg(feature = "sha-1")] - cluster_test!(lua, should_eval_get_script); - #[cfg(feature = "sha-1")] - cluster_test!(lua, should_evalsha_echo_script); - #[cfg(feature = "sha-1")] - cluster_test!(lua, should_evalsha_with_reload_echo_script); - #[cfg(feature = "sha-1")] - cluster_test!(lua, should_evalsha_get_script); - - cluster_test!(lua, should_function_load_scripts); - cluster_test!(lua, should_function_dump_and_restore); - cluster_test!(lua, should_function_flush); - cluster_test!(lua, should_function_delete); - cluster_test!(lua, should_function_list); - cluster_test!(lua, should_function_list_multiple); - #[cfg(feature = "i-keys")] - cluster_test!(lua, should_function_fcall_getset); - cluster_test!(lua, should_function_fcall_echo); - cluster_test!(lua, should_function_fcall_ro_echo); - - #[cfg(feature = "sha-1")] - cluster_test!(lua, should_create_lua_script_helper_from_code); - #[cfg(feature = "sha-1")] - cluster_test!(lua, should_create_lua_script_helper_from_hash); - cluster_test!(lua, should_create_function_from_code); - cluster_test!(lua, should_create_function_from_name); -} - -#[cfg(feature = "i-sorted-sets")] -pub mod sorted_sets { - - cluster_test!(sorted_sets, should_bzpopmin); - cluster_test!(sorted_sets, should_bzpopmax); - cluster_test!(sorted_sets, should_zadd_values); - cluster_test!(sorted_sets, should_zcard_values); - cluster_test!(sorted_sets, should_zcount_values); - cluster_test!(sorted_sets, should_zdiff_values); - cluster_test!(sorted_sets, should_zdiffstore_values); - cluster_test!(sorted_sets, should_zincrby_values); - cluster_test!(sorted_sets, should_zinter_values); - cluster_test!(sorted_sets, should_zinterstore_values); - cluster_test!(sorted_sets, should_zlexcount); - cluster_test!(sorted_sets, should_zpopmax); - cluster_test!(sorted_sets, should_zpopmin); - cluster_test!(sorted_sets, should_zrandmember); - cluster_test!(sorted_sets, should_zrangestore_values); - cluster_test!(sorted_sets, should_zrangebylex); - cluster_test!(sorted_sets, should_zrevrangebylex); - cluster_test!(sorted_sets, should_zrangebyscore); - cluster_test!(sorted_sets, should_zrevrangebyscore); - cluster_test!(sorted_sets, should_zrank_values); - cluster_test!(sorted_sets, should_zrem_values); - cluster_test!(sorted_sets, should_zremrangebylex); - cluster_test!(sorted_sets, should_zremrangebyrank); - cluster_test!(sorted_sets, should_zremrangebyscore); - cluster_test!(sorted_sets, should_zrevrank_values); - cluster_test!(sorted_sets, should_zscore_values); - cluster_test!(sorted_sets, should_zunion_values); - cluster_test!(sorted_sets, should_zunionstore_values); - cluster_test!(sorted_sets, should_zmscore_values); - cluster_test!(sorted_sets, should_zrangebyscore_neg_infinity); -} - -#[cfg(feature = "i-lists")] -pub mod lists { - - cluster_test!(lists, should_blpop_values); - cluster_test!(lists, should_brpop_values); - cluster_test!(lists, should_brpoplpush_values); - cluster_test!(lists, should_blmove_values); - cluster_test!(lists, should_lindex_values); - cluster_test!(lists, should_linsert_values); - cluster_test!(lists, should_lpop_values); - cluster_test!(lists, should_lpos_values); - cluster_test!(lists, should_lpush_values); - cluster_test!(lists, should_lpushx_values); - cluster_test!(lists, should_lrange_values); - cluster_test!(lists, should_lrem_values); - cluster_test!(lists, should_lset_values); - #[cfg(feature = "i-keys")] - cluster_test!(lists, should_ltrim_values); - cluster_test!(lists, should_rpop_values); - cluster_test!(lists, should_rpoplpush_values); - cluster_test!(lists, should_lmove_values); - cluster_test!(lists, should_rpush_values); - cluster_test!(lists, should_rpushx_values); - cluster_test!(lists, should_sort_int_list); - cluster_test!(lists, should_sort_alpha_list); - cluster_test!(lists, should_sort_int_list_with_limit); - #[cfg(feature = "replicas")] - cluster_test!(lists, should_sort_ro_int_list); -} - -#[cfg(feature = "i-geo")] -pub mod geo { - - cluster_test!(geo, should_geoadd_values); - cluster_test!(geo, should_geohash_values); - cluster_test!(geo, should_geopos_values); - cluster_test!(geo, should_geodist_values); - cluster_test!(geo, should_georadius_values); - cluster_test!(geo, should_georadiusbymember_values); - cluster_test!(geo, should_geosearch_values); -} - -#[cfg(all(not(feature = "i-redis-stack"), feature = "i-acl"))] -pub mod acl { - cluster_test!(acl, should_run_acl_getuser); -} - -#[cfg(feature = "i-streams")] -mod streams { - cluster_test!(streams, should_xinfo_consumers); - cluster_test!(streams, should_xinfo_groups); - cluster_test!(streams, should_xinfo_streams); - cluster_test!(streams, should_xadd_auto_id_to_a_stream); - cluster_test!(streams, should_xadd_manual_id_to_a_stream); - cluster_test!(streams, should_xadd_with_cap_to_a_stream); - cluster_test!(streams, should_xadd_nomkstream_to_a_stream); - cluster_test!(streams, should_xtrim_a_stream_approx_cap); - cluster_test!(streams, should_xtrim_a_stream_eq_cap); - cluster_test!(streams, should_xdel_one_id_in_a_stream); - cluster_test!(streams, should_xdel_multiple_ids_in_a_stream); - cluster_test!(streams, should_xrange_no_count); - cluster_test!(streams, should_xrange_with_count); - cluster_test!(streams, should_xrange_values_no_count); - cluster_test!(streams, should_xrevrange_no_count); - cluster_test!(streams, should_xrevrange_with_count); - cluster_test!(streams, should_xrevrange_values_no_count); - cluster_test!(streams, should_run_xlen_on_stream); - cluster_test!(streams, should_xread_one_key_count_1); - cluster_test!(streams, should_xread_multiple_keys_count_2); - cluster_test!(streams, should_xread_with_blocking); - cluster_test!(streams, should_xread_map_one_key); - cluster_test!(streams, should_xgroup_create_no_mkstream); - cluster_test!(streams, should_xgroup_create_mkstream); - cluster_test!(streams, should_xgroup_createconsumer); - cluster_test!(streams, should_xgroup_delconsumer); - cluster_test!(streams, should_xgroup_destroy); - cluster_test!(streams, should_xgroup_setid); - cluster_test!(streams, should_xreadgroup_one_stream); - cluster_test!(streams, should_xreadgroup_multiple_stream); - cluster_test!(streams, should_xreadgroup_block); - cluster_test!(streams, should_xack_one_id); - cluster_test!(streams, should_xack_multiple_ids); - cluster_test!(streams, should_xclaim_one_id); - cluster_test!(streams, should_xclaim_multiple_ids); - cluster_test!(streams, should_xclaim_with_justid); - cluster_test!(streams, should_xautoclaim_default); -} - -#[cfg(feature = "i-cluster")] -mod cluster { - #[cfg(feature = "i-client")] - cluster_test!(cluster, should_use_each_cluster_node); -} - -#[cfg(feature = "i-tracking")] -mod tracking { - #[cfg(feature = "i-keys")] - cluster_test!(tracking, should_invalidate_foo_resp3); -} - -#[cfg(feature = "i-time-series")] -mod timeseries { - cluster_test!(timeseries, should_ts_add_get_and_range); - cluster_test!(timeseries, should_create_alter_and_del_timeseries); - cluster_test!(timeseries, should_madd_and_mget); - cluster_test!(timeseries, should_incr_and_decr); - cluster_test!(timeseries, should_create_and_delete_rules); - cluster_test!(timeseries, should_madd_and_mrange); - cluster_test!(timeseries, should_madd_and_mrevrange); -} diff --git a/tests/integration/docker.rs b/tests/integration/docker.rs deleted file mode 100644 index 00cdd7da..00000000 --- a/tests/integration/docker.rs +++ /dev/null @@ -1,210 +0,0 @@ -#![allow(dead_code)] -use crate::integration::{ - docker::env::{COMPOSE_NETWORK_NAME, NETWORK_NAME}, - utils, -}; -use bollard::{ - container::{ - Config, CreateContainerOptions, LogOutput, NetworkingConfig, RemoveContainerOptions, StartContainerOptions, - }, - errors::Error as BollardError, - exec::{CreateExecOptions, StartExecResults}, - network::{ConnectNetworkOptions, ListNetworksOptions}, - ClientVersion, Docker, API_DEFAULT_VERSION, -}; -use bytes::Bytes; -use fred::prelude::*; -use fred::types::ClusterRouting; -use futures::stream::StreamExt; -use redis_protocol::resp2::decode::decode_bytes as resp2_decode; -use std::collections::HashMap; - -macro_rules! e ( - ($arg:expr) => ($arg.map_err(|e| RedisError::new(RedisErrorKind::Unknown, format!("{:?}", e)))) -); - -pub mod env { - use fred::error::{RedisError, RedisErrorKind}; - use std::env; - - // compat check - pub const COMPOSE_NETWORK_NAME: &str = "compose_fred-tests"; - pub const NETWORK_NAME: &str = "fred-tests"; - - pub const CENTRALIZED_HOST: &str = "FRED_REDIS_CENTRALIZED_HOST"; - pub const CENTRALIZED_PORT: &str = "FRED_REDIS_CENTRALIZED_PORT"; - pub const CLUSTER_HOST: &str = "FRED_REDIS_CLUSTER_HOST"; - pub const CLUSTER_PORT: &str = "FRED_REDIS_CLUSTER_PORT"; - pub const CLUSTER_TLS_HOST: &str = "FRED_REDIS_CLUSTER_TLS_HOST"; - pub const CLUSTER_TLS_PORT: &str = "FRED_REDIS_CLUSTER_TLS_PORT"; - pub const SENTINEL_HOST: &str = "FRED_REDIS_SENTINEL_HOST"; - pub const SENTINEL_PORT: &str = "FRED_REDIS_SENTINEL_PORT"; - - pub fn read(name: &str) -> Option { - env::var_os(name).and_then(|s| s.into_string().ok()) - } - - pub fn try_read(name: &str) -> Result { - read(name).ok_or(RedisError::new(RedisErrorKind::Unknown, "Failed to read env")) - } -} - -/// Read the name of the network, which may have a different prefix on older docker installs. -pub async fn read_network_name(docker: &Docker) -> Result { - let networks = e!(docker.list_networks(None::>).await)?; - - for network in networks.into_iter() { - if let Some(ref name) = network.name { - if name == NETWORK_NAME || name == COMPOSE_NETWORK_NAME { - return Ok(name.to_owned()); - } - } - } - Err(RedisError::new( - RedisErrorKind::Unknown, - "Failed to read fred test network.", - )) -} - -/// Run a command in the bitnami redis container. -pub async fn run_in_redis_container(docker: &Docker, command: Vec) -> Result, RedisError> { - let redis_version = env::try_read("REDIS_VERSION")?; - - let redis_container_config = Config { - image: Some(format!("bitnami/redis:{}", redis_version)), - tty: Some(true), - ..Default::default() - }; - debug!("Creating test cli container..."); - let container_id = e!( - docker - .create_container( - Some(CreateContainerOptions { - name: "redis-cli-tmp".to_owned(), - ..Default::default() - }), - redis_container_config, - ) - .await - )? - .id; - debug!("Starting test cli container..."); - e!( - docker - .start_container(&container_id, None::>) - .await - )?; - - let test_network = read_network_name(docker).await?; - debug!("Connecting container to the test network..."); - e!( - docker - .connect_network( - &test_network, - ConnectNetworkOptions { - container: container_id.clone(), - ..Default::default() - } - ) - .await - )?; - - debug!("Running command: {:?}", command); - let exec = e!( - docker - .create_exec( - &container_id, - CreateExecOptions { - attach_stdout: Some(true), - attach_stderr: Some(true), - cmd: Some(command), - ..Default::default() - } - ) - .await - )? - .id; - let exec_state = e!(docker.start_exec(&exec, None).await)?; - - let mut out = Vec::with_capacity(1024); - if let StartExecResults::Attached { mut output, .. } = exec_state { - while let Some(Ok(msg)) = output.next().await { - match msg { - LogOutput::StdOut { message } => out.extend(&message), - LogOutput::StdErr { message } => { - warn!("stderr from cli container: {}", String::from_utf8_lossy(&message)); - }, - _ => {}, - }; - } - } else { - return Err(RedisError::new(RedisErrorKind::Unknown, "Missing start exec result")); - } - - debug!("Cleaning up cli container..."); - let result = e!( - docker - .remove_container( - &container_id, - Some(RemoveContainerOptions { - force: true, - ..Default::default() - }), - ) - .await - ); - if let Err(e) = result { - error!("Failed to remove cli container: {:?}", e); - } - - Ok(out) -} - -/// Read the cluster state via CLUSTER SLOTS. -// This tries to run: -// -// docker run -it --name redis-cli-tmp --rm --network compose_fred-tests bitnami/redis:7.0.9 -// redis-cli -h redis-cluster-1 -p 30001 -a bar --raw CLUSTER SLOTS -pub async fn inspect_cluster(tls: bool) -> Result { - let docker = e!(Docker::connect_with_http("", 10, API_DEFAULT_VERSION))?; - - debug!("Connected to docker"); - let password = env::try_read("REDIS_PASSWORD")?; - - let cluster_slots: Vec = if tls { - let (host, port) = ( - env::try_read(env::CLUSTER_TLS_HOST)?, - env::try_read(env::CLUSTER_TLS_PORT)?, - ); - - // TODO add ca/cert/key argv - format!( - "redis-cli -h {} -p {} -a {} --raw --tls CLUSTER SLOTS", - host, port, password - ) - .split(' ') - .map(|s| s.to_owned()) - .collect() - } else { - let (host, port) = (env::try_read(env::CLUSTER_HOST)?, env::try_read(env::CLUSTER_PORT)?); - - format!("redis-cli -h {} -p {} -a {} --raw CLUSTER SLOTS", host, port, password) - .split(' ') - .map(|s| s.to_owned()) - .collect() - }; - - let result = run_in_redis_container(&docker, cluster_slots).await?; - debug!("CLUSTER SLOTS response: {}", String::from_utf8_lossy(&result)); - let parsed: RedisValue = match resp2_decode(&Bytes::from(result))? { - Some((frame, _)) => frame.into_resp3().try_into()?, - None => { - return Err(RedisError::new( - RedisErrorKind::Unknown, - "Failed to read cluster slots.", - )) - }, - }; - - ClusterRouting::from_cluster_slots(parsed, "") -} diff --git a/tests/integration/geo/mod.rs b/tests/integration/geo/mod.rs deleted file mode 100644 index 0bbf9fc7..00000000 --- a/tests/integration/geo/mod.rs +++ /dev/null @@ -1,328 +0,0 @@ -use fred::{ - prelude::*, - types::{GeoPosition, GeoRadiusInfo, GeoUnit, GeoValue, SortOrder}, -}; -use std::convert::TryInto; - -fn loose_eq(lhs: f64, rhs: f64, precision: u32) -> bool { - let pow = 10_u32.pow(precision) as f64; - (lhs * pow).round() / pow == (rhs * pow).round() / pow -} - -fn loose_eq_pos(lhs: &GeoPosition, rhs: &GeoPosition) -> bool { - // the test values are accurate to ~5 decimal places but the same values from redis go to ~10 decimal places - loose_eq(lhs.longitude, rhs.longitude, 5) && loose_eq(lhs.latitude, rhs.latitude, 5) -} - -async fn create_fake_data(client: &RedisClient, key: &str) -> Result, RedisError> { - // GEOADD key 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania" - - let values = vec![ - (13.361389, 38.115556, "Palermo").try_into()?, - (15.087269, 37.502669, "Catania").try_into()?, - ]; - - client.geoadd(key, None, false, values.clone()).await?; - Ok(values.into_iter().map(|p| p.coordinates).collect()) -} - -pub async fn should_geoadd_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let values: Vec = vec![ - (13.361389, 38.115556, "Palermo").try_into()?, - (15.087269, 37.502669, "Catania").try_into()?, - ]; - - for value in values.into_iter() { - let result: i64 = client.geoadd("foo", None, false, value).await?; - assert_eq!(result, 1); - } - let result: usize = client.zcard("foo").await?; - assert_eq!(result, 2); - - Ok(()) -} - -pub async fn should_geohash_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_fake_data(&client, "foo").await?; - - let result: String = client.geohash("foo", "Palermo").await?; - assert_eq!(result, "sqc8b49rny0"); - let result: String = client.geohash("foo", "Catania").await?; - assert_eq!(result, "sqdtr74hyu0"); - - let result: Vec = client.geohash("foo", vec!["Palermo", "Catania"]).await?; - assert_eq!(result, vec!["sqc8b49rny0", "sqdtr74hyu0"]); - - Ok(()) -} - -pub async fn should_geopos_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = create_fake_data(&client, "foo").await?; - - let result: RedisValue = client.geopos("foo", vec!["Palermo", "Catania"]).await?; - let result: Vec = result - .into_array() - .into_iter() - .map(|p| p.as_geo_position().unwrap().unwrap()) - .collect(); - for (idx, value) in result.into_iter().enumerate() { - if !loose_eq_pos(&value, &expected[idx]) { - panic!("{:?} not equal to {:?}", value, &expected[idx]); - } - } - - let result: Vec = client.geopos("foo", "Palermo").await?; - let result = result[0].as_geo_position().unwrap().unwrap(); - assert!(loose_eq_pos(&result, &expected[0])); - - let result: Vec = client.geopos("foo", "Catania").await?; - let result = result[0].as_geo_position().unwrap().unwrap(); - assert!(loose_eq_pos(&result, &expected[1])); - - Ok(()) -} - -pub async fn should_geodist_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_fake_data(&client, "foo").await?; - - let result: f64 = client.geodist("foo", "Palermo", "Catania", None).await?; - assert!(loose_eq(result, 166274.1516, 4)); - let result: f64 = client - .geodist("foo", "Palermo", "Catania", Some(GeoUnit::Kilometers)) - .await?; - assert!(loose_eq(result, 166.2742, 4)); - let result: f64 = client - .geodist("foo", "Palermo", "Catania", Some(GeoUnit::Miles)) - .await?; - assert!(loose_eq(result, 103.3182, 4)); - - Ok(()) -} - -pub async fn should_georadius_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_fake_data(&client, "foo").await?; - - let result = client - .georadius::( - "foo", - (15.0, 37.0), - 200.0, - GeoUnit::Kilometers, - false, - true, - false, - None, - None, - None, - None, - ) - .await? - .into_geo_radius_result(false, true, false)?; - let expected: Vec = vec![ - GeoRadiusInfo { - member: "Palermo".into(), - distance: Some(190.4424), - position: None, - hash: None, - }, - GeoRadiusInfo { - member: "Catania".into(), - distance: Some(56.4413), - position: None, - hash: None, - }, - ]; - assert_eq!(result, expected); - - let result = client - .georadius::( - "foo", - (15.0, 37.0), - 200.0, - GeoUnit::Kilometers, - true, - false, - false, - None, - None, - None, - None, - ) - .await? - .into_geo_radius_result(true, false, false)?; - let expected: Vec = vec![ - GeoRadiusInfo { - member: "Palermo".into(), - distance: None, - position: Some((13.361_389_338_970_184, 38.115_556_395_496_3).into()), - hash: None, - }, - GeoRadiusInfo { - member: "Catania".into(), - distance: None, - position: Some((15.087_267_458_438_873, 37.502_668_423_331_62).into()), - hash: None, - }, - ]; - assert_eq!(result, expected); - - let result = client - .georadius::( - "foo", - (15.0, 37.0), - 200.0, - GeoUnit::Kilometers, - true, - true, - false, - None, - None, - None, - None, - ) - .await? - .into_geo_radius_result(true, true, false)?; - let expected: Vec = vec![ - GeoRadiusInfo { - member: "Palermo".into(), - distance: Some(190.4424), - position: Some((13.361_389_338_970_184, 38.115_556_395_496_3).into()), - hash: None, - }, - GeoRadiusInfo { - member: "Catania".into(), - distance: Some(56.4413), - position: Some((15.087_267_458_438_873, 37.502_668_423_331_62).into()), - hash: None, - }, - ]; - assert_eq!(result, expected); - - Ok(()) -} - -pub async fn should_georadiusbymember_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_fake_data(&client, "foo").await?; - let agrigento: GeoValue = (13.583333, 37.316667, "Agrigento").try_into()?; - client.geoadd("foo", None, false, agrigento).await?; - - let result = client - .georadiusbymember::( - "foo", - "Agrigento", - 100.0, - GeoUnit::Kilometers, - false, - false, - false, - None, - None, - None, - None, - ) - .await? - .into_geo_radius_result(false, false, false)?; - let expected = vec![ - GeoRadiusInfo { - member: "Agrigento".into(), - distance: None, - position: None, - hash: None, - }, - GeoRadiusInfo { - member: "Palermo".into(), - distance: None, - position: None, - hash: None, - }, - ]; - assert_eq!(result, expected); - - Ok(()) -} - -pub async fn should_geosearch_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_fake_data(&client, "foo").await?; - let values = vec![ - (12.758489, 38.788135, "edge1").try_into()?, - (17.241510, 38.788135, "edge2").try_into()?, - ]; - client.geoadd("foo", None, false, values).await?; - - let lonlat: GeoPosition = (15.0, 37.0).into(); - let result = client - .geosearch::( - "foo", - None, - Some(lonlat.clone()), - Some((200.0, GeoUnit::Kilometers)), - None, - Some(SortOrder::Asc), - None, - false, - false, - false, - ) - .await? - .into_geo_radius_result(false, false, false)?; - let expected = vec![ - GeoRadiusInfo { - member: "Catania".into(), - distance: None, - position: None, - hash: None, - }, - GeoRadiusInfo { - member: "Palermo".into(), - distance: None, - position: None, - hash: None, - }, - ]; - assert_eq!(result, expected); - - let result = client - .geosearch::( - "foo", - None, - Some(lonlat), - None, - Some((400.0, 400.0, GeoUnit::Kilometers)), - Some(SortOrder::Asc), - None, - true, - true, - false, - ) - .await? - .into_geo_radius_result(true, true, false)?; - let expected = vec![ - GeoRadiusInfo { - member: "Catania".into(), - distance: Some(56.4413), - position: Some((15.087_267_458_438_873, 37.502_668_423_331_62).into()), - hash: None, - }, - GeoRadiusInfo { - member: "Palermo".into(), - distance: Some(190.4424), - position: Some((13.361_389_338_970_184, 38.115_556_395_496_3).into()), - hash: None, - }, - GeoRadiusInfo { - member: "edge2".into(), - distance: Some(279.7403), - position: Some((17.241_510_450_839_996, 38.788_134_516_242_25).into()), - hash: None, - }, - GeoRadiusInfo { - member: "edge1".into(), - distance: Some(279.7405), - position: Some((12.758_487_761_020_66, 38.788_134_516_242_25).into()), - hash: None, - }, - ]; - assert_eq!(result, expected); - - Ok(()) -} diff --git a/tests/integration/hashes/mod.rs b/tests/integration/hashes/mod.rs deleted file mode 100644 index 4a326ba9..00000000 --- a/tests/integration/hashes/mod.rs +++ /dev/null @@ -1,196 +0,0 @@ -use fred::{ - clients::RedisClient, - error::RedisError, - interfaces::*, - types::{RedisConfig, RedisValue}, -}; -use std::collections::{HashMap, HashSet}; - -fn assert_contains(values: Vec, item: &T) { - for value in values.iter() { - if value == item { - return; - } - } - - panic!("Failed to find item in set."); -} - -fn assert_diff_len(values: Vec<&'static str>, value: RedisValue, len: usize) { - if let RedisValue::Array(items) = value { - let mut expected = HashSet::with_capacity(values.len()); - for value in values.into_iter() { - expected.insert(value.to_owned()); - } - let mut actual = HashSet::with_capacity(items.len()); - for item in items.into_iter() { - let s = &*item.as_str().unwrap(); - actual.insert(s.to_owned()); - } - - let diff = expected.difference(&actual).fold(0, |m, _| m + 1); - assert_eq!(diff, len); - } else { - panic!("Expected value array"); - } -} - -pub async fn should_hset_and_hget(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: i64 = client.hset("foo", ("a", 1)).await?; - assert_eq!(result, 1); - let result: i64 = client.hset("foo", vec![("b", 2), ("c", 3)]).await?; - assert_eq!(result, 2); - - let a: i64 = client.hget("foo", "a").await?; - assert_eq!(a, 1); - let b: i64 = client.hget("foo", "b").await?; - assert_eq!(b, 2); - let c: i64 = client.hget("foo", "c").await?; - assert_eq!(c, 3); - - Ok(()) -} - -pub async fn should_hset_and_hdel(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: i64 = client.hset("foo", vec![("a", 1), ("b", 2), ("c", 3)]).await?; - assert_eq!(result, 3); - let result: i64 = client.hdel("foo", vec!["a", "b"]).await?; - assert_eq!(result, 2); - let result: i64 = client.hdel("foo", "c").await?; - assert_eq!(result, 1); - let result: Option = client.hget("foo", "a").await?; - assert!(result.is_none()); - - Ok(()) -} - -pub async fn should_hexists(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.hset("foo", ("a", 1)).await?; - let a: bool = client.hexists("foo", "a").await?; - assert!(a); - let b: bool = client.hexists("foo", "b").await?; - assert!(!b); - - Ok(()) -} - -pub async fn should_hgetall(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.hset("foo", vec![("a", 1), ("b", 2), ("c", 3)]).await?; - let values: HashMap = client.hgetall("foo").await?; - - assert_eq!(values.len(), 3); - let mut expected = HashMap::new(); - expected.insert("a".into(), 1); - expected.insert("b".into(), 2); - expected.insert("c".into(), 3); - assert_eq!(values, expected); - - Ok(()) -} - -pub async fn should_hincryby(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: i64 = client.hincrby("foo", "a", 1).await?; - assert_eq!(result, 1); - let result: i64 = client.hincrby("foo", "a", 2).await?; - assert_eq!(result, 3); - - Ok(()) -} - -pub async fn should_hincryby_float(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: f64 = client.hincrbyfloat("foo", "a", 0.5).await?; - assert_eq!(result, 0.5); - let result: f64 = client.hincrbyfloat("foo", "a", 3.7).await?; - assert_eq!(result, 4.2); - - Ok(()) -} - -pub async fn should_get_keys(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.hset("foo", vec![("a", 1), ("b", 2), ("c", 3)]).await?; - - let keys = client.hkeys("foo").await?; - assert_diff_len(vec!["a", "b", "c"], keys, 0); - - Ok(()) -} - -pub async fn should_hmset(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.hmset("foo", vec![("a", 1), ("b", 2), ("c", 3)]).await?; - - let a: i64 = client.hget("foo", "a").await?; - assert_eq!(a, 1); - let b: i64 = client.hget("foo", "b").await?; - assert_eq!(b, 2); - let c: i64 = client.hget("foo", "c").await?; - assert_eq!(c, 3); - - Ok(()) -} - -pub async fn should_hmget(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.hmset("foo", vec![("a", 1), ("b", 2), ("c", 3)]).await?; - - let result: Vec = client.hmget("foo", vec!["a", "b"]).await?; - assert_eq!(result, vec![1, 2]); - - Ok(()) -} - -pub async fn should_hsetnx(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.hset("foo", ("a", 1)).await?; - let result: bool = client.hsetnx("foo", "a", 2).await?; - assert!(!result); - let result: i64 = client.hget("foo", "a").await?; - assert_eq!(result, 1); - let result: bool = client.hsetnx("foo", "b", 2).await?; - assert!(result); - let result: i64 = client.hget("foo", "b").await?; - assert_eq!(result, 2); - - Ok(()) -} - -pub async fn should_get_random_field(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.hmset("foo", vec![("a", 1), ("b", 2), ("c", 3)]).await?; - - let field: String = client.hrandfield("foo", None).await?; - assert_contains(vec!["a", "b", "c"], &field.as_str()); - - let fields = client.hrandfield("foo", Some((2, false))).await?; - assert_diff_len(vec!["a", "b", "c"], fields, 1); - - let actual: HashMap = client.hrandfield("foo", Some((2, true))).await?; - assert_eq!(actual.len(), 2); - - let mut expected: HashMap = HashMap::new(); - expected.insert("a".into(), 1); - expected.insert("b".into(), 2); - expected.insert("c".into(), 3); - - for (key, value) in actual.into_iter() { - let expected_val: i64 = *expected.get(&key).unwrap(); - assert_eq!(value, expected_val); - } - - Ok(()) -} - -pub async fn should_get_strlen(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = "abcdefhijklmnopqrstuvwxyz"; - client.hset("foo", ("a", expected)).await?; - - let len: usize = client.hstrlen("foo", "a").await?; - assert_eq!(len, expected.len()); - - Ok(()) -} - -pub async fn should_get_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.hmset("foo", vec![("a", "1"), ("b", "2")]).await?; - - let values: RedisValue = client.hvals("foo").await?; - assert_diff_len(vec!["1", "2"], values, 0); - - Ok(()) -} diff --git a/tests/integration/hyperloglog/mod.rs b/tests/integration/hyperloglog/mod.rs deleted file mode 100644 index f39c491d..00000000 --- a/tests/integration/hyperloglog/mod.rs +++ /dev/null @@ -1,36 +0,0 @@ -use fred::prelude::*; - -pub async fn should_pfadd_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: i64 = client.pfadd("foo", vec!["a", "b"]).await?; - assert_eq!(result, 1); - let result: i64 = client.pfadd("foo", "a").await?; - assert_eq!(result, 0); - - Ok(()) -} - -pub async fn should_pfcount_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: i64 = client.pfadd("foo", vec!["a", "b", "c"]).await?; - assert_eq!(result, 1); - let result: i64 = client.pfcount("foo").await?; - assert_eq!(result, 3); - let result: i64 = client.pfadd("foo", vec!["c", "d", "e"]).await?; - assert_eq!(result, 1); - let result: i64 = client.pfcount("foo").await?; - assert_eq!(result, 5); - - Ok(()) -} - -pub async fn should_pfmerge_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: i64 = client.pfadd("foo{1}", vec!["a", "b", "c"]).await?; - assert_eq!(result, 1); - let result: i64 = client.pfadd("bar{1}", vec!["c", "d", "e"]).await?; - assert_eq!(result, 1); - - client.pfmerge("baz{1}", vec!["foo{1}", "bar{1}"]).await?; - let result: i64 = client.pfcount("baz{1}").await?; - assert_eq!(result, 5); - - Ok(()) -} diff --git a/tests/integration/keys/mod.rs b/tests/integration/keys/mod.rs deleted file mode 100644 index 55e78474..00000000 --- a/tests/integration/keys/mod.rs +++ /dev/null @@ -1,349 +0,0 @@ -use bytes::Bytes; -use fred::{ - clients::{RedisClient, RedisPool}, - error::RedisError, - interfaces::*, - types::{Expiration, ReconnectPolicy, RedisConfig, RedisMap, RedisValue}, -}; -use futures::{pin_mut, StreamExt}; -use std::{collections::HashMap, time::Duration}; -use tokio::{self, time::sleep}; - -#[cfg(feature = "default-nil-types")] -pub async fn should_handle_missing_keys(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - assert!(client.get::("foo").await?.is_empty()); - Ok(()) -} - -#[cfg(not(feature = "default-nil-types"))] -pub async fn should_handle_missing_keys(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - assert!(client.get::("foo").await.is_err()); - Ok(()) -} - -pub async fn should_set_and_get_a_value(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - client.set("foo", "bar", None, None, false).await?; - - assert_eq!(client.get::("foo").await?, "bar"); - Ok(()) -} - -pub async fn should_set_and_del_a_value(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - let result: Option = client.set("foo", "bar", None, None, true).await?; - assert!(result.is_none()); - - assert_eq!(client.get::("foo").await?, "bar"); - assert_eq!(client.del::("foo").await?, 1); - - Ok(()) -} - -pub async fn should_set_with_get_argument(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - client.set("foo", "bar", None, None, false).await?; - - let result: String = client.set("foo", "baz", None, None, true).await?; - assert_eq!(result, "bar"); - - let result: String = client.get("foo").await?; - assert_eq!(result, "baz"); - - Ok(()) -} - -pub async fn should_rename(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - client.set("{foo}.1", "baz", None, None, false).await?; - - client.rename("{foo}.1", "{foo}.2").await?; - let result: String = client.get("{foo}.2").await?; - assert_eq!(result, "baz"); - - Ok(()) -} - -pub async fn should_error_rename_does_not_exist(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - client.rename("{foo}", "{foo}.bar").await -} - -pub async fn should_renamenx(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - client.set("{foo}.1", "baz", None, None, false).await?; - - client.renamenx("{foo}.1", "{foo}.2").await?; - let result: String = client.get("{foo}.2").await?; - assert_eq!(result, "baz"); - - Ok(()) -} - -pub async fn should_error_renamenx_does_not_exist( - client: RedisClient, - _config: RedisConfig, -) -> Result<(), RedisError> { - client.renamenx("{foo}", "{foo}.bar").await -} - -pub async fn should_unlink(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - client.set("{foo}1", "bar", None, None, false).await?; - - assert_eq!(client.get::("{foo}1").await?, "bar"); - assert_eq!( - client - .unlink::(vec!["{foo}1", "{foo}", "{foo}:something"]) - .await?, - 1 - ); - - Ok(()) -} - -pub async fn should_incr_and_decr_a_value(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - let count: u64 = client.incr("foo").await?; - assert_eq!(count, 1); - let count: u64 = client.incr_by("foo", 2).await?; - assert_eq!(count, 3); - let count: u64 = client.decr("foo").await?; - assert_eq!(count, 2); - let count: u64 = client.decr_by("foo", 2).await?; - assert_eq!(count, 0); - - Ok(()) -} - -pub async fn should_incr_by_float(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - let count: f64 = client.incr_by_float("foo", 1.5).await?; - assert_eq!(count, 1.5); - let count: f64 = client.incr_by_float("foo", 2.2).await?; - assert_eq!(count, 3.7); - let count: f64 = client.incr_by_float("foo", -1.2).await?; - assert_eq!(count, 2.5); - - Ok(()) -} - -pub async fn should_mset_a_non_empty_map(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - let mut map: HashMap = HashMap::new(); - // MSET args all have to map to the same cluster node - map.insert("a{1}".into(), 1.into()); - map.insert("b{1}".into(), 2.into()); - map.insert("c{1}".into(), 3.into()); - - client.mset(map).await?; - let a: i64 = client.get("a{1}").await?; - let b: i64 = client.get("b{1}").await?; - let c: i64 = client.get("c{1}").await?; - - assert_eq!(a, 1); - assert_eq!(b, 2); - assert_eq!(c, 3); - - Ok(()) -} - -// should panic -pub async fn should_error_mset_empty_map(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - client.mset(RedisMap::new()).await.map(|_| ()) -} - -pub async fn should_expire_key(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - client.set("foo", "bar", None, None, false).await?; - - client.expire("foo", 1).await?; - sleep(Duration::from_millis(1500)).await; - let foo: Option = client.get("foo").await?; - assert!(foo.is_none()); - - Ok(()) -} - -pub async fn should_persist_key(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - client.set("foo", "bar", Some(Expiration::EX(5)), None, false).await?; - - let removed: bool = client.persist("foo").await?; - assert!(removed); - - let ttl: i64 = client.ttl("foo").await?; - assert_eq!(ttl, -1); - - Ok(()) -} - -pub async fn should_check_ttl(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - client.set("foo", "bar", Some(Expiration::EX(5)), None, false).await?; - - let ttl: i64 = client.ttl("foo").await?; - assert!(ttl > 0 && ttl < 6); - - Ok(()) -} - -pub async fn should_check_pttl(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - client.set("foo", "bar", Some(Expiration::EX(5)), None, false).await?; - - let ttl: i64 = client.pttl("foo").await?; - assert!(ttl > 0 && ttl < 5001); - - Ok(()) -} - -pub async fn should_dump_key(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.set("foo", "abc123", None, None, false).await?; - let dump: RedisValue = client.dump("foo").await?; - assert!(dump.is_bytes()); - - Ok(()) -} - -pub async fn should_dump_and_restore_key(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = "abc123"; - - client.set("foo", expected, None, None, false).await?; - let dump = client.dump("foo").await?; - client.del("foo").await?; - - client.restore("foo", 0, dump, false, false, None, None).await?; - let value: String = client.get("foo").await?; - assert_eq!(value, expected); - - Ok(()) -} - -pub async fn should_modify_ranges(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.set("foo", "0123456789", None, None, false).await?; - - let range: String = client.getrange("foo", 0, 4).await?; - assert_eq!(range, "01234"); - - client.setrange("foo", 4, "abc").await?; - let value: String = client.get("foo").await?; - assert_eq!(value, "0123abc789"); - - Ok(()) -} - -pub async fn should_getset_value(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let value: Option = client.getset("foo", "bar").await?; - assert!(value.is_none()); - let value: String = client.getset("foo", "baz").await?; - assert_eq!(value, "bar"); - let value: String = client.get("foo").await?; - assert_eq!(value, "baz"); - - Ok(()) -} - -pub async fn should_getdel_value(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let value: Option = client.getdel("foo").await?; - assert!(value.is_none()); - - client.set("foo", "bar", None, None, false).await?; - let value: String = client.getdel("foo").await?; - assert_eq!(value, "bar"); - let value: Option = client.get("foo").await?; - assert!(value.is_none()); - - Ok(()) -} - -pub async fn should_get_strlen(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = "abcdefghijklmnopqrstuvwxyz"; - client.set("foo", expected, None, None, false).await?; - let len: usize = client.strlen("foo").await?; - assert_eq!(len, expected.len()); - - Ok(()) -} - -pub async fn should_mget_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected: Vec<(&str, RedisValue)> = vec![("a{1}", 1.into()), ("b{1}", 2.into()), ("c{1}", 3.into())]; - for (key, value) in expected.iter() { - client.set(*key, value.clone(), None, None, false).await?; - } - let values: Vec = client.mget(vec!["a{1}", "b{1}", "c{1}"]).await?; - assert_eq!(values, vec![1, 2, 3]); - - Ok(()) -} - -pub async fn should_msetnx_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected: Vec<(&str, RedisValue)> = vec![("a{1}", 1.into()), ("b{1}", 2.into())]; - - // do it first, check they're there - let values: i64 = client.msetnx(expected.clone()).await?; - assert_eq!(values, 1); - let a: i64 = client.get("a{1}").await?; - let b: i64 = client.get("b{1}").await?; - assert_eq!(a, 1); - assert_eq!(b, 2); - - client.del(vec!["a{1}", "b{1}"]).await?; - client.set("a{1}", 3, None, None, false).await?; - - let values: i64 = client.msetnx(expected.clone()).await?; - assert_eq!(values, 0); - let b: Option = client.get("b{1}").await?; - assert!(b.is_none()); - - Ok(()) -} - -pub async fn should_copy_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.set("a{1}", "bar", None, None, false).await?; - let result: i64 = client.copy("a{1}", "b{1}", None, false).await?; - assert_eq!(result, 1); - - let b: String = client.get("b{1}").await?; - assert_eq!(b, "bar"); - - client.set("a{1}", "baz", None, None, false).await?; - let result: i64 = client.copy("a{1}", "b{1}", None, false).await?; - assert_eq!(result, 0); - - let result: i64 = client.copy("a{1}", "b{1}", None, true).await?; - assert_eq!(result, 1); - let b: String = client.get("b{1}").await?; - assert_eq!(b, "baz"); - - Ok(()) -} - -pub async fn should_get_keys_from_pool_in_a_stream( - client: RedisClient, - config: RedisConfig, -) -> Result<(), RedisError> { - client.set("foo", "bar", None, None, false).await?; - - let pool = RedisPool::new(config, None, None, None, 5)?; - pool.connect(); - pool.wait_for_connect().await?; - - let stream = - tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(Duration::from_millis(100))).then(move |_| { - let pool = pool.clone(); - - async move { - let value: Option = pool.get("foo").await.unwrap(); - value - } - }); - pin_mut!(stream); - - let mut count = 0; - while let Some(value) = stream.next().await { - assert_eq!(value, Some("bar".into())); - count += 1; - - if count >= 10 { - break; - } - } - - Ok(()) -} - -pub async fn should_pexpire_key(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _: () = client.set("foo", "bar", None, None, false).await?; - assert_eq!(client.pexpire::("foo", 100, None).await?, 1); - - sleep(Duration::from_millis(150)).await; - assert_eq!(client.get::, _>("foo").await?, None); - Ok(()) -} diff --git a/tests/integration/lists/mod.rs b/tests/integration/lists/mod.rs deleted file mode 100644 index 29df249c..00000000 --- a/tests/integration/lists/mod.rs +++ /dev/null @@ -1,462 +0,0 @@ -use fred::{ - interfaces::*, - prelude::*, - types::{LMoveDirection, ListLocation, SortOrder}, -}; -use std::time::Duration; -use tokio::time::sleep; - -const COUNT: i64 = 10; - -async fn create_count_data(client: &RedisClient, key: &str) -> Result, RedisError> { - let mut values = Vec::with_capacity(COUNT as usize); - for idx in 0 .. COUNT { - client.rpush(key, idx).await?; - values.push(idx.to_string().into()); - } - - Ok(values) -} - -pub async fn should_blpop_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let publisher = client.clone_new(); - publisher.connect(); - publisher.wait_for_connect().await?; - - let jh = tokio::spawn(async move { - for idx in 0 .. COUNT { - let mut result: Vec = client.blpop("foo", 30.0).await?; - assert_eq!(result.pop().unwrap().as_i64().unwrap(), idx); - } - - Ok::<_, RedisError>(()) - }); - - for idx in 0 .. COUNT { - // the assertion below checks the length of the list, so we have to make sure not to push faster than elements are - // removed - sleep(Duration::from_millis(100)).await; - let result: i64 = publisher.rpush("foo", idx).await?; - assert_eq!(result, 1); - } - - let _ = jh.await?; - Ok(()) -} - -pub async fn should_brpop_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let publisher = client.clone_new(); - publisher.connect(); - publisher.wait_for_connect().await?; - - let jh = tokio::spawn(async move { - for idx in 0 .. COUNT { - let mut result: Vec = client.brpop("foo", 30.0).await?; - assert_eq!(result.pop().unwrap().as_i64().unwrap(), idx); - } - - Ok::<_, RedisError>(()) - }); - - for idx in 0 .. COUNT { - // the assertion below checks the length of the list, so we have to make sure not to push faster than elements are - // removed - sleep(Duration::from_millis(50)).await; - let result: i64 = publisher.lpush("foo", idx).await?; - assert_eq!(result, 1); - } - - let _ = jh.await?; - Ok(()) -} - -pub async fn should_brpoplpush_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let publisher = client.clone_new(); - publisher.connect(); - publisher.wait_for_connect().await?; - - let jh = tokio::spawn(async move { - for idx in 0 .. COUNT { - let result: i64 = client.brpoplpush("foo{1}", "bar{1}", 30.0).await?; - assert_eq!(result, idx); - } - - Ok::<_, RedisError>(()) - }); - - for idx in 0 .. COUNT { - let result: i64 = publisher.lpush("foo{1}", idx).await?; - assert!(result > 0); - } - let _ = jh.await?; - - for idx in 0 .. COUNT { - let result: i64 = publisher.rpop("bar{1}", None).await?; - assert_eq!(result, idx); - } - - Ok(()) -} - -pub async fn should_blmove_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let publisher = client.clone_new(); - publisher.connect(); - publisher.wait_for_connect().await?; - - let jh = tokio::spawn(async move { - for idx in 0 .. COUNT { - let result: i64 = client - .blmove("foo{1}", "bar{1}", LMoveDirection::Right, LMoveDirection::Left, 30.0) - .await?; - assert_eq!(result, idx); - } - - Ok::<_, RedisError>(()) - }); - - for idx in 0 .. COUNT { - let result: i64 = publisher.lpush("foo{1}", idx).await?; - assert!(result > 0); - } - let _ = jh.await?; - - for idx in 0 .. COUNT { - let result: i64 = publisher.rpop("bar{1}", None).await?; - assert_eq!(result, idx); - } - - Ok(()) -} - -pub async fn should_lindex_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = create_count_data(&client, "foo").await?; - - for (idx, expected_value) in expected.into_iter().enumerate() { - let result: RedisValue = client.lindex("foo", idx as i64).await?; - assert_eq!(result, expected_value); - } - - Ok(()) -} - -pub async fn should_linsert_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: usize = client.linsert("foo", ListLocation::Before, 1, 0).await?; - assert_eq!(result, 0); - let result: usize = client.llen("foo").await?; - assert_eq!(result, 0); - - client.lpush("foo", 0).await?; - let mut expected: Vec = vec!["0".into()]; - for idx in 1 .. COUNT { - let result: i64 = client.linsert("foo", ListLocation::After, idx - 1, idx).await?; - assert_eq!(result, idx + 1); - expected.push(idx.to_string().into()); - } - let values: Vec = client.lrange("foo", 0, COUNT).await?; - assert_eq!(values, expected); - - Ok(()) -} - -pub async fn should_lpop_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = create_count_data(&client, "foo").await?; - - for idx in 0 .. COUNT { - let result: i64 = client.lpop("foo", None).await?; - assert_eq!(result, idx); - } - - let _ = create_count_data(&client, "foo").await?; - let result: Vec = client.lpop("foo", Some(COUNT as usize)).await?; - assert_eq!(result, expected); - - Ok(()) -} - -pub async fn should_lpos_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_count_data(&client, "foo").await?; - - for idx in 0 .. COUNT { - let result: i64 = client.lpos("foo", idx, None, None, None).await?; - assert_eq!(result, idx); - } - - let _ = create_count_data(&client, "foo").await?; - let _ = create_count_data(&client, "foo").await?; - - for idx in 0 .. COUNT { - let result: i64 = client.lpos("foo", idx, Some(2), None, None).await?; - assert_eq!(result, idx + COUNT); - let result: i64 = client.lpos("foo", idx, Some(3), None, None).await?; - assert_eq!(result, idx + COUNT * 2); - - let result: Vec = client.lpos("foo", idx, None, Some(2), None).await?; - let expected = vec![idx, (idx + COUNT)]; - assert_eq!(result, expected); - - let result: Vec = client.lpos("foo", idx, None, Some(3), None).await?; - let expected = vec![idx, (idx + COUNT), (idx + COUNT * 2)]; - assert_eq!(result, expected); - } - - Ok(()) -} - -pub async fn should_lpush_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - for idx in 0 .. COUNT { - let result: i64 = client.lpush("foo", idx).await?; - assert_eq!(result, idx + 1); - let result: i64 = client.lrange("foo", 0, 0).await?; - assert_eq!(result, idx); - } - let result: i64 = client.llen("foo").await?; - assert_eq!(result, COUNT); - - Ok(()) -} - -pub async fn should_lpushx_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: i64 = client.lpushx("foo", 0).await?; - assert_eq!(result, 0); - - client.lpush("foo", 0).await?; - for idx in 0 .. COUNT { - let result: i64 = client.lpushx("foo", idx).await?; - assert_eq!(result, idx + 2); - let result: i64 = client.lrange("foo", 0, 0).await?; - assert_eq!(result, idx); - } - let result: i64 = client.llen("foo").await?; - assert_eq!(result, COUNT + 1); - - Ok(()) -} - -pub async fn should_lrange_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = create_count_data(&client, "foo").await?; - - let result: Vec = client.lrange("foo", 0, COUNT).await?; - assert_eq!(result, expected); - - for idx in 0 .. COUNT { - let result: i64 = client.lrange("foo", idx, idx).await?; - assert_eq!(result, idx); - } - - Ok(()) -} - -pub async fn should_lrem_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_count_data(&client, "foo").await?; - for idx in 0 .. COUNT { - let result: usize = client.lrem("foo", 1, idx).await?; - assert_eq!(result, 1); - } - let result: usize = client.llen("foo").await?; - assert_eq!(result, 0); - - let _ = create_count_data(&client, "foo").await?; - let _ = create_count_data(&client, "foo").await?; - for idx in 0 .. COUNT { - let result: usize = client.lrem("foo", 2, idx).await?; - assert_eq!(result, 2); - } - let result: usize = client.llen("foo").await?; - assert_eq!(result, 0); - - Ok(()) -} - -pub async fn should_lset_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - assert!(client.lset::("foo", 1, 0).await.is_err()); - let mut expected = create_count_data(&client, "foo").await?; - expected.reverse(); - - for idx in 0 .. COUNT { - client.lset("foo", idx, COUNT - (idx + 1)).await?; - } - let result: Vec = client.lrange("foo", 0, COUNT).await?; - assert_eq!(result, expected); - - Ok(()) -} - -#[cfg(feature = "i-keys")] -pub async fn should_ltrim_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = create_count_data(&client, "foo").await?; - - client.ltrim("foo", 0, COUNT).await?; - let result: Vec = client.lrange("foo", 0, COUNT).await?; - assert_eq!(result, expected); - - for idx in 0 .. COUNT { - client.ltrim("foo", 0, idx).await?; - let result: Vec = client.lrange("foo", 0, COUNT).await?; - assert_eq!(result, expected[0 .. (idx + 1) as usize]); - - client.del("foo").await?; - let _ = create_count_data(&client, "foo").await?; - } - - Ok(()) -} - -pub async fn should_rpop_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let mut expected = create_count_data(&client, "foo").await?; - expected.reverse(); - - for idx in 0 .. COUNT { - let result: i64 = client.rpop("foo", None).await?; - assert_eq!(result, COUNT - (idx + 1)); - } - - let _ = create_count_data(&client, "foo").await?; - let result: Vec = client.rpop("foo", Some(COUNT as usize)).await?; - assert_eq!(result, expected); - - Ok(()) -} - -pub async fn should_rpoplpush_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - for idx in 0 .. COUNT { - let result: i64 = client.lpush("foo{1}", idx).await?; - assert_eq!(result, 1); - let result: i64 = client.rpoplpush("foo{1}", "bar{1}").await?; - assert_eq!(result, idx); - let result: i64 = client.rpop("bar{1}", None).await?; - assert_eq!(result, idx); - } - - Ok(()) -} - -pub async fn should_lmove_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - for idx in 0 .. COUNT { - let result: i64 = client.lpush("foo{1}", idx).await?; - assert_eq!(result, 1); - let result: i64 = client - .lmove("foo{1}", "bar{1}", LMoveDirection::Right, LMoveDirection::Left) - .await?; - assert_eq!(result, idx); - let result: i64 = client.rpop("bar{1}", None).await?; - assert_eq!(result, idx); - } - - Ok(()) -} - -pub async fn should_rpush_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - for idx in 0 .. COUNT { - let result: i64 = client.rpush("foo", idx).await?; - assert_eq!(result, idx + 1); - let result: i64 = client.lrange("foo", -1, -1).await?; - assert_eq!(result, idx); - } - let result: i64 = client.llen("foo").await?; - assert_eq!(result, COUNT); - - Ok(()) -} - -pub async fn should_rpushx_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: i64 = client.rpushx("foo", 0).await?; - assert_eq!(result, 0); - - client.rpush("foo", 0).await?; - for idx in 0 .. COUNT { - let result: i64 = client.rpushx("foo", idx).await?; - assert_eq!(result, idx + 2); - let result: i64 = client.lrange("foo", -1, -1).await?; - assert_eq!(result, idx); - } - let result: i64 = client.llen("foo").await?; - assert_eq!(result, COUNT + 1); - - Ok(()) -} - -pub async fn should_sort_int_list(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.lpush("foo", vec![1, 2, 3, 4, 5]).await?; - - let sorted: Vec = client.sort("foo", None, None, (), None, false, None).await?; - assert_eq!(sorted, vec![1, 2, 3, 4, 5]); - Ok(()) -} - -pub async fn should_sort_alpha_list(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.lpush("foo", vec!["a", "b", "c", "d", "e"]).await?; - - let sorted: Vec = client - .sort("foo", None, None, (), Some(SortOrder::Desc), true, None) - .await?; - assert_eq!(sorted, vec!["e", "d", "c", "b", "a"]); - Ok(()) -} - -pub async fn should_sort_int_list_with_limit(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.lpush("foo", vec![1, 2, 3, 4, 5]).await?; - - let sorted: Vec = client.sort("foo", None, Some((2, 2)), (), None, false, None).await?; - assert_eq!(sorted, vec![3, 4]); - Ok(()) -} - -#[cfg(feature = "i-keys")] -pub async fn should_sort_int_list_with_patterns(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let vals: Vec = (1 .. 6).collect(); - let key: RedisKey = "foo".into(); - - client.lpush(&key, vals.clone()).await?; - for val in vals.iter() { - // reverse the weights - client - .set( - format!("{}_weight_{}", key.as_str().unwrap(), val), - 7 - *val, - None, - None, - false, - ) - .await?; - } - for val in vals.iter() { - client - .set( - format!("{}_val_{}", key.as_str().unwrap(), val), - *val * 2, - None, - None, - false, - ) - .await?; - } - - let sorted: Vec = client - .sort( - &key, - Some(format!("{}_weight_*", key.as_str().unwrap()).into()), - None, - format!("{}_val_*", key.as_str().unwrap()), - None, - false, - None, - ) - .await?; - assert_eq!(sorted, vec![10, 8, 6, 4, 2]); - - Ok(()) -} - -#[cfg(feature = "replicas")] -pub async fn should_sort_ro_int_list(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.lpush("foo", vec![1, 2, 3, 4, 5]).await?; - // wait for replicas to recv the command - tokio::time::sleep(Duration::from_millis(500)).await; - - let sorted: Vec = client - .replicas() - .sort_ro("foo", None, None, (), Some(SortOrder::Desc), false) - .await?; - assert_eq!(sorted, vec![5, 4, 3, 2, 1]); - Ok(()) -} diff --git a/tests/integration/lua/mod.rs b/tests/integration/lua/mod.rs deleted file mode 100644 index 5af4033c..00000000 --- a/tests/integration/lua/mod.rs +++ /dev/null @@ -1,328 +0,0 @@ -use bytes::Bytes; -use fred::{ - prelude::*, - types::{FnPolicy, Function, Library, Script}, - util, -}; -use std::{ - collections::{BTreeSet, HashMap}, - ops::Deref, -}; - -static ECHO_SCRIPT: &str = "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}"; -#[cfg(feature = "sha-1")] -static GET_SCRIPT: &str = "return redis.call('get', KEYS[1])"; - -#[cfg(feature = "sha-1")] -pub async fn load_script(client: &RedisClient, script: &str) -> Result { - if client.is_clustered() { - client.script_load_cluster(script).await - } else { - client.script_load(script).await - } -} - -pub async fn flush_scripts(client: &RedisClient) -> Result<(), RedisError> { - if client.is_clustered() { - client.script_flush_cluster(false).await - } else { - client.script_flush(false).await - } -} - -#[cfg(feature = "sha-1")] -pub async fn should_load_script(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let script_hash = util::sha1_hash(ECHO_SCRIPT); - let hash: String = client.script_load(ECHO_SCRIPT).await?; - assert_eq!(hash, script_hash); - - Ok(()) -} - -#[cfg(feature = "sha-1")] -pub async fn should_load_script_cluster(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let script_hash = util::sha1_hash(ECHO_SCRIPT); - let hash: String = client.script_load_cluster(ECHO_SCRIPT).await?; - assert_eq!(hash, script_hash); - - Ok(()) -} - -#[cfg(feature = "sha-1")] -pub async fn should_evalsha_echo_script(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let hash = load_script(&client, ECHO_SCRIPT).await?; - - let result: Vec = client.evalsha(hash, vec!["a{1}", "b{1}"], vec!["c{1}", "d{1}"]).await?; - assert_eq!(result, vec!["a{1}", "b{1}", "c{1}", "d{1}"]); - - flush_scripts(&client).await?; - Ok(()) -} - -#[cfg(feature = "sha-1")] -pub async fn should_evalsha_with_reload_echo_script(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let script = Script::from_lua(ECHO_SCRIPT); - - let result: Vec = script - .evalsha_with_reload(&client, vec!["a{1}", "b{1}"], vec!["c{1}", "d{1}"]) - .await?; - assert_eq!(result, vec!["a{1}", "b{1}", "c{1}", "d{1}"]); - - flush_scripts(&client).await?; - Ok(()) -} - -#[cfg(feature = "sha-1")] -pub async fn should_evalsha_get_script(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let script_hash = util::sha1_hash(GET_SCRIPT); - let hash = load_script(&client, GET_SCRIPT).await?; - assert_eq!(hash, script_hash); - - let result: Option = client.evalsha(&script_hash, vec!["foo"], ()).await?; - assert!(result.is_none()); - - client.set("foo", "bar", None, None, false).await?; - let result: String = client.evalsha(&script_hash, vec!["foo"], ()).await?; - assert_eq!(result, "bar"); - - flush_scripts(&client).await?; - Ok(()) -} - -pub async fn should_eval_echo_script(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: Vec = client - .eval(ECHO_SCRIPT, vec!["a{1}", "b{1}"], vec!["c{1}", "d{1}"]) - .await?; - assert_eq!(result, vec!["a{1}", "b{1}", "c{1}", "d{1}"]); - - flush_scripts(&client).await?; - Ok(()) -} - -#[cfg(feature = "sha-1")] -pub async fn should_eval_get_script(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: Option = client.eval(GET_SCRIPT, vec!["foo"], ()).await?; - assert!(result.is_none()); - - let hash = util::sha1_hash(GET_SCRIPT); - let result: Option = client.evalsha(&hash, vec!["foo"], ()).await?; - assert!(result.is_none()); - - client.set("foo", "bar", None, None, false).await?; - let result: String = client.eval(GET_SCRIPT, vec!["foo"], ()).await?; - assert_eq!(result, "bar"); - - let result: String = client.evalsha(&hash, vec!["foo"], ()).await?; - assert_eq!(result, "bar"); - - flush_scripts(&client).await?; - Ok(()) -} - -pub async fn should_function_load_scripts(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - check_redis_7!(client); - - let echo_fn = include_str!("../../scripts/lua/echo.lua"); - let getset_fn = include_str!("../../scripts/lua/getset.lua"); - - let echo: String = client.function_load(true, echo_fn).await?; - assert_eq!(echo, "echolib"); - let getset: String = client.function_load(true, getset_fn).await?; - assert_eq!(getset, "getsetlib"); - client.function_load_cluster(true, echo_fn).await?; - - Ok(()) -} - -pub async fn should_function_dump_and_restore(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - check_redis_7!(client); - - let echo_fn = include_str!("../../scripts/lua/echo.lua"); - client.function_load_cluster(true, echo_fn).await?; - - let fns: Bytes = client.function_dump().await?; - client.function_flush_cluster(false).await?; - client.function_restore_cluster(fns, FnPolicy::default()).await?; - - let mut fns: Vec> = client.function_list(Some("echolib"), false).await?; - assert_eq!(fns.len(), 1); - let fns = fns.pop().expect("Failed to pop function"); - assert_eq!(fns.get("library_name"), Some(&RedisValue::String("echolib".into()))); - - Ok(()) -} - -pub async fn should_function_flush(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - check_redis_7!(client); - - let echo_fn = include_str!("../../scripts/lua/echo.lua"); - client.function_load_cluster(true, echo_fn).await?; - let fns: RedisValue = client.function_list(Some("echolib"), false).await?; - assert!(!fns.is_null()); - - client.function_flush_cluster(false).await?; - let fns: RedisValue = client.function_list(Some("echolib"), false).await?; - assert!(fns.is_null() || fns.array_len() == Some(0)); - - Ok(()) -} - -pub async fn should_function_delete(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - check_redis_7!(client); - - let echo_fn = include_str!("../../scripts/lua/echo.lua"); - client.function_load_cluster(true, echo_fn).await?; - let fns: RedisValue = client.function_list(Some("echolib"), false).await?; - assert!(!fns.is_null()); - - client.function_delete_cluster("echolib").await?; - let fns: RedisValue = client.function_list(Some("echolib"), false).await?; - assert!(fns.is_null() || fns.array_len() == Some(0)); - - Ok(()) -} - -pub async fn should_function_list(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - check_redis_7!(client); - - let echo_fn = include_str!("../../scripts/lua/echo.lua"); - client.function_load_cluster(true, echo_fn).await?; - let getset_fn = include_str!("../../scripts/lua/getset.lua"); - client.function_load_cluster(true, getset_fn).await?; - - let mut fns: Vec> = client.function_list(Some("echolib"), false).await?; - assert_eq!(fns.len(), 1); - let fns = fns.pop().expect("Failed to pop function"); - assert_eq!(fns.get("library_name"), Some(&RedisValue::String("echolib".into()))); - - Ok(()) -} - -pub async fn should_function_list_multiple(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - check_redis_7!(client); - - let echo_fn = include_str!("../../scripts/lua/echo.lua"); - client.function_load_cluster(true, echo_fn).await?; - let getset_fn = include_str!("../../scripts/lua/getset.lua"); - client.function_load_cluster(true, getset_fn).await?; - - let fns: Vec> = client.function_list(None::, false).await?; - - // ordering is not deterministic, so convert to a set of library names - let fns: BTreeSet = fns - .into_iter() - .map(|lib| { - lib - .get("library_name") - .expect("Failed to read library name") - .as_string() - .expect("Failed to convert to string.") - }) - .collect(); - let mut expected = BTreeSet::new(); - expected.insert("echolib".into()); - expected.insert("getsetlib".into()); - - assert_eq!(fns, expected); - Ok(()) -} - -#[cfg(feature = "i-keys")] -pub async fn should_function_fcall_getset(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - check_redis_7!(client); - - let getset_fn = include_str!("../../scripts/lua/getset.lua"); - client.function_load_cluster(true, getset_fn).await?; - - client.set("foo{1}", "bar", None, None, false).await?; - let old: String = client.fcall("getset", vec!["foo{1}"], vec!["baz"]).await?; - assert_eq!(old, "bar"); - let new: String = client.get("foo{1}").await?; - assert_eq!(new, "baz"); - - Ok(()) -} - -pub async fn should_function_fcall_echo(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - check_redis_7!(client); - - let echo_fn = include_str!("../../scripts/lua/echo.lua"); - client.function_load_cluster(true, echo_fn).await?; - - let result: Vec = client - .fcall("echo", vec!["key1{1}", "key2{1}"], vec!["arg1", "arg2"]) - .await?; - assert_eq!(result, vec!["key1{1}", "key2{1}", "arg1", "arg2"]); - - Ok(()) -} - -pub async fn should_function_fcall_ro_echo(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - check_redis_7!(client); - - let echo_fn = include_str!("../../scripts/lua/echo.lua"); - client.function_load_cluster(true, echo_fn).await?; - - let result: Vec = client - .fcall_ro("echo", vec!["key1{1}", "key2{1}"], vec!["arg1", "arg2"]) - .await?; - assert_eq!(result, vec!["key1{1}", "key2{1}", "arg1", "arg2"]); - - Ok(()) -} - -#[cfg(feature = "sha-1")] -pub async fn should_create_lua_script_helper_from_code( - client: RedisClient, - _: RedisConfig, -) -> Result<(), RedisError> { - let script = Script::from_lua(ECHO_SCRIPT); - script.load(&client).await?; - - let result: Vec = script - .evalsha(&client, vec!["foo{1}", "bar{1}"], vec!["3", "4"]) - .await?; - assert_eq!(result, vec!["foo{1}".into(), "bar{1}".into(), "3".into(), "4".into()]); - Ok(()) -} - -#[cfg(feature = "sha-1")] -pub async fn should_create_lua_script_helper_from_hash( - client: RedisClient, - _: RedisConfig, -) -> Result<(), RedisError> { - let hash: String = client.script_load_cluster(ECHO_SCRIPT).await?; - - let script = Script::from_hash(hash); - let result: Vec = script - .evalsha(&client, vec!["foo{1}", "bar{1}"], vec!["3", "4"]) - .await?; - assert_eq!(result, vec!["foo{1}".into(), "bar{1}".into(), "3".into(), "4".into()]); - Ok(()) -} - -pub async fn should_create_function_from_code(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - check_redis_7!(client); - let echo_lib = include_str!("../../scripts/lua/echo.lua"); - - let lib = Library::from_code(&client, echo_lib).await?; - assert_eq!(lib.name().deref(), "echolib"); - let func = lib.functions().get("echo").expect("Failed to read echo function"); - - let result: Vec = func.fcall(&client, vec!["foo{1}", "bar{1}"], vec!["3", "4"]).await?; - assert_eq!(result, vec!["foo{1}".into(), "bar{1}".into(), "3".into(), "4".into()]); - Ok(()) -} - -pub async fn should_create_function_from_name(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - check_redis_7!(client); - let echo_lib = include_str!("../../scripts/lua/echo.lua"); - client.function_load_cluster(true, echo_lib).await?; - - let lib = Library::from_name(&client, "echolib").await?; - let func = lib.functions().get("echo").expect("Failed to read echo function"); - - let result: Vec = func.fcall(&client, vec!["foo{1}", "bar{1}"], vec!["3", "4"]).await?; - assert_eq!(result, vec!["foo{1}".into(), "bar{1}".into(), "3".into(), "4".into()]); - Ok(()) -} diff --git a/tests/integration/memory/mod.rs b/tests/integration/memory/mod.rs deleted file mode 100644 index 071491ef..00000000 --- a/tests/integration/memory/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -use fred::{cmd, prelude::*, types::MemoryStats}; - -pub async fn should_run_memory_doctor(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.memory_doctor().await?; - Ok(()) -} - -pub async fn should_run_memory_malloc_stats(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.memory_malloc_stats().await?; - Ok(()) -} - -pub async fn should_run_memory_purge(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.memory_purge().await?; - Ok(()) -} - -pub async fn should_run_memory_stats(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let stats: MemoryStats = client.memory_stats().await?; - assert!(stats.total_allocated > 0); - - Ok(()) -} - -pub async fn should_run_memory_usage(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.custom(cmd!("SET"), vec!["foo", "bar"]).await?; - assert!(client.memory_usage::("foo", None).await? > 0); - - Ok(()) -} diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs deleted file mode 100644 index 6a734cba..00000000 --- a/tests/integration/mod.rs +++ /dev/null @@ -1,79 +0,0 @@ -#[macro_use] -pub mod utils; -#[cfg(feature = "i-acl")] -mod acl; -#[cfg(feature = "i-client")] -mod client; -#[cfg(feature = "i-cluster")] -mod cluster; -//#[cfg(feature = "i-cluster")] -// pub mod docker; -#[cfg(feature = "i-geo")] -mod geo; -#[cfg(feature = "i-hashes")] -mod hashes; -#[cfg(feature = "i-hyperloglog")] -mod hyperloglog; -#[cfg(feature = "i-keys")] -mod keys; -#[cfg(feature = "i-lists")] -mod lists; -#[cfg(feature = "i-scripts")] -mod lua; -#[cfg(feature = "i-memory")] -mod memory; -#[cfg(feature = "transactions")] -mod multi; -mod other; -mod pool; -#[cfg(feature = "i-pubsub")] -mod pubsub; -#[cfg(feature = "i-redis-json")] -mod redis_json; -#[cfg(feature = "i-redisearch")] -mod redisearch; -mod scanning; -#[cfg(feature = "i-server")] -mod server; -#[cfg(feature = "i-sets")] -mod sets; -#[cfg(feature = "i-slowlog")] -mod slowlog; -#[cfg(feature = "i-sorted-sets")] -mod sorted_sets; -#[cfg(feature = "i-streams")] -mod streams; -#[cfg(feature = "i-time-series")] -mod timeseries; -#[cfg(feature = "i-tracking")] -mod tracking; - -#[cfg(not(feature = "mocks"))] -pub mod centralized; -#[cfg(not(feature = "mocks"))] -pub mod clustered; - -mod macro_tests { - use fred::{cmd, types::ClusterHash}; - use socket2::TcpKeepalive; - - #[test] - fn should_use_cmd_macro() { - let command = cmd!("GET"); - assert_eq!(command.cmd, "GET"); - assert_eq!(command.cluster_hash, ClusterHash::FirstKey); - assert!(!command.blocking); - let command = cmd!("GET", blocking: true); - assert_eq!(command.cmd, "GET"); - assert_eq!(command.cluster_hash, ClusterHash::FirstKey); - assert!(command.blocking); - let command = cmd!("GET", hash: ClusterHash::FirstValue); - assert_eq!(command.cmd, "GET"); - assert_eq!(command.cluster_hash, ClusterHash::FirstValue); - assert!(!command.blocking); - let command = cmd!("GET", hash: ClusterHash::FirstValue, blocking: true); - assert_eq!(command.cmd, "GET"); - assert_eq!(command.cluster_hash, ClusterHash::FirstValue); - assert!(command.blocking); - } -} diff --git a/tests/integration/multi/mod.rs b/tests/integration/multi/mod.rs deleted file mode 100644 index 5358eb32..00000000 --- a/tests/integration/multi/mod.rs +++ /dev/null @@ -1,36 +0,0 @@ -use fred::{ - clients::RedisClient, - error::RedisError, - interfaces::*, - types::{RedisConfig, RedisValue}, -}; - -pub async fn should_run_get_set_trx(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - let trx = client.multi(); - - trx.set("foo", "bar", None, None, false).await?; - trx.get("foo").await?; - let results: Vec = trx.exec(true).await?; - - assert_eq!(results, vec!["OK", "bar"]); - Ok(()) -} - -pub async fn should_run_error_get_set_trx(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - client.set("foo", "bar", None, None, false).await?; - - let trx = client.multi(); - trx.incr("foo").await?; - trx.exec(true).await?; - - Ok(()) -} - -pub async fn should_fail_with_hashslot_error(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - let trx = client.multi(); - trx.set("foo", "bar", None, None, false).await?; - trx.set("bar", "baz", None, None, false).await?; - trx.exec(true).await?; - - Ok(()) -} diff --git a/tests/integration/other/mod.rs b/tests/integration/other/mod.rs deleted file mode 100644 index 99a01c5f..00000000 --- a/tests/integration/other/mod.rs +++ /dev/null @@ -1,799 +0,0 @@ -use super::utils; -use async_trait::async_trait; -use fred::{ - clients::{RedisClient, RedisPool}, - cmd, - error::{RedisError, RedisErrorKind}, - interfaces::*, - prelude::{Blocking, RedisValue}, - types::{ - BackpressureConfig, - Builder, - ClientUnblockFlag, - ClusterHash, - Options, - PerformanceConfig, - RedisConfig, - RedisKey, - RedisMap, - ServerConfig, - }, -}; -use futures::future::try_join; -use parking_lot::RwLock; -use redis_protocol::resp3::types::RespVersion; -use std::{ - collections::{BTreeMap, BTreeSet, HashMap}, - convert::TryInto, - mem, - sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, - }, - time::Duration, -}; -use tokio::time::sleep; - -#[cfg(feature = "subscriber-client")] -use fred::clients::SubscriberClient; -use fred::types::ClusterDiscoveryPolicy; -#[cfg(feature = "replicas")] -use fred::types::ReplicaConfig; -#[cfg(feature = "dns")] -use fred::types::Resolve; -#[cfg(feature = "partial-tracing")] -use fred::types::TracingConfig; -#[cfg(feature = "dns")] -use hickory_resolver::{config::*, TokioAsyncResolver}; -#[cfg(feature = "dns")] -use std::net::{IpAddr, SocketAddr}; - -#[cfg(all(feature = "i-keys", feature = "i-hashes"))] -fn hash_to_btree(vals: &RedisMap) -> BTreeMap { - vals - .iter() - .map(|(key, value)| (key.clone(), value.as_u64().unwrap() as u16)) - .collect() -} - -#[cfg(all(feature = "i-keys", feature = "i-hashes"))] -fn array_to_set(vals: Vec) -> BTreeSet { - vals.into_iter().collect() -} - -#[cfg(feature = "i-keys")] -pub fn incr_atomic(size: &Arc) -> usize { - size.fetch_add(1, Ordering::AcqRel).saturating_add(1) -} - -#[cfg(all(feature = "i-keys", feature = "i-hashes"))] -pub async fn should_smoke_test_from_redis_impl(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let nested_values: RedisMap = vec![("a", 1), ("b", 2)].try_into()?; - client.set("foo", "123", None, None, false).await?; - client.set("baz", "456", None, None, false).await?; - client.hset("bar", &nested_values).await?; - - let foo: usize = client.get("foo").await?; - assert_eq!(foo, 123); - let foo: i64 = client.get("foo").await?; - assert_eq!(foo, 123); - let foo: String = client.get("foo").await?; - assert_eq!(foo, "123"); - let foo: Vec = client.get("foo").await?; - assert_eq!(foo, "123".as_bytes()); - let foo: Vec = client.hvals("bar").await?; - assert_eq!(array_to_set(foo), array_to_set(vec!["1".to_owned(), "2".to_owned()])); - let foo: BTreeSet = client.hvals("bar").await?; - assert_eq!(foo, array_to_set(vec!["1".to_owned(), "2".to_owned()])); - let foo: HashMap = client.hgetall("bar").await?; - assert_eq!(foo, RedisValue::Map(nested_values.clone()).convert()?); - let foo: BTreeMap = client.hgetall("bar").await?; - assert_eq!(foo, hash_to_btree(&nested_values)); - let foo: (String, i64) = client.mget(vec!["foo", "baz"]).await?; - assert_eq!(foo, ("123".into(), 456)); - let foo: Vec<(String, i64)> = client.hgetall("bar").await?; - assert_eq!(array_to_set(foo), array_to_set(vec![("a".into(), 1), ("b".into(), 2)])); - - Ok(()) -} - -#[cfg(all(feature = "i-client", feature = "i-lists"))] -pub async fn should_automatically_unblock(_: RedisClient, mut config: RedisConfig) -> Result<(), RedisError> { - config.blocking = Blocking::Interrupt; - let client = RedisClient::new(config, None, None, None); - client.connect(); - client.wait_for_connect().await?; - - let unblock_client = client.clone(); - tokio::spawn(async move { - sleep(Duration::from_secs(1)).await; - let _: () = unblock_client.ping().await.expect("Failed to ping"); - }); - - let result = client.blpop::<(), _>("foo", 60.0).await; - assert!(result.is_err()); - assert_ne!(*result.unwrap_err().kind(), RedisErrorKind::Timeout); - Ok(()) -} - -#[cfg(all(feature = "i-client", feature = "i-lists"))] -pub async fn should_manually_unblock(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let connections_ids = client.connection_ids().await; - let unblock_client = client.clone(); - - tokio::spawn(async move { - sleep(Duration::from_secs(1)).await; - - for (_, id) in connections_ids.into_iter() { - let _ = unblock_client - .client_unblock::<(), _>(id, Some(ClientUnblockFlag::Error)) - .await; - } - }); - - let result = client.blpop::<(), _>("foo", 60.0).await; - assert!(result.is_err()); - assert_ne!(*result.unwrap_err().kind(), RedisErrorKind::Timeout); - Ok(()) -} - -#[cfg(all(feature = "i-client", feature = "i-lists"))] -pub async fn should_error_when_blocked(_: RedisClient, mut config: RedisConfig) -> Result<(), RedisError> { - config.blocking = Blocking::Error; - let client = RedisClient::new(config, None, None, None); - client.connect(); - client.wait_for_connect().await?; - let error_client = client.clone(); - - tokio::spawn(async move { - sleep(Duration::from_secs(1)).await; - - let result = error_client.ping::<()>().await; - assert!(result.is_err()); - assert_eq!(*result.unwrap_err().kind(), RedisErrorKind::InvalidCommand); - - let _ = error_client.unblock_self(None).await; - }); - - let result = client.blpop::<(), _>("foo", 60.0).await; - assert!(result.is_err()); - Ok(()) -} - -pub async fn should_split_clustered_connection(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - let actual = client - .split_cluster()? - .iter() - .map(|client| client.client_config()) - .fold(BTreeSet::new(), |mut set, config| { - if let ServerConfig::Centralized { server } = config.server { - set.insert(server); - } else { - panic!("expected centralized config"); - } - - set - }); - - assert_eq!(actual.len(), 3); - Ok(()) -} - -#[cfg(feature = "metrics")] -pub async fn should_track_size_stats(client: RedisClient, _config: RedisConfig) -> Result<(), RedisError> { - let _ = client.take_res_size_metrics(); - let _ = client.take_req_size_metrics(); - - let _ = client - .set("foo", "abcdefghijklmnopqrstuvxyz", None, None, false) - .await?; - let req_stats = client.take_req_size_metrics(); - let res_stats = client.take_res_size_metrics(); - - // manually calculated with the redis_protocol crate `encode` function (not shown here) - let expected_req_size = 54; - let expected_res_size = 5; - - assert_eq!(req_stats.sum, expected_req_size); - assert_eq!(req_stats.samples, 1); - assert_eq!(res_stats.sum, expected_res_size); - assert_eq!(res_stats.samples, 1); - - Ok(()) -} - -#[cfg(feature = "i-server")] -pub async fn should_run_flushall_cluster(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let count: i64 = 200; - - for idx in 0 .. count { - client - .custom(cmd!("SET"), vec![format!("foo-{}", idx), idx.to_string()]) - .await?; - } - client.flushall_cluster().await?; - - for idx in 0 .. count { - let value: Option = client.custom(cmd!("GET"), vec![format!("foo-{}", idx)]).await?; - assert!(value.is_none()); - } - - Ok(()) -} - -pub async fn should_safely_change_protocols_repeatedly( - client: RedisClient, - _: RedisConfig, -) -> Result<(), RedisError> { - let done = Arc::new(RwLock::new(false)); - let other = client.clone(); - let other_done = done.clone(); - - let jh = tokio::spawn(async move { - loop { - if *other_done.read() { - return Ok::<_, RedisError>(()); - } - other.ping().await?; - sleep(Duration::from_millis(10)).await; - } - }); - - // switch protocols every half second - for idx in 0 .. 15 { - let version = if idx % 2 == 0 { - RespVersion::RESP2 - } else { - RespVersion::RESP3 - }; - client.hello(version, None, None).await?; - sleep(Duration::from_millis(500)).await; - } - let _ = mem::replace(&mut *done.write(), true); - - let _ = jh.await?; - Ok(()) -} - -// test to repro an intermittent race condition found while stress testing the client -#[allow(dead_code)] -#[cfg(feature = "i-keys")] -pub async fn should_test_high_concurrency_pool(_: RedisClient, mut config: RedisConfig) -> Result<(), RedisError> { - config.blocking = Blocking::Block; - let perf = PerformanceConfig { - auto_pipeline: true, - backpressure: BackpressureConfig { - max_in_flight_commands: 100_000_000, - ..Default::default() - }, - ..Default::default() - }; - let pool = RedisPool::new(config, Some(perf), None, None, 28)?; - pool.connect(); - pool.wait_for_connect().await?; - - let num_tasks = 11641; - let mut tasks = Vec::with_capacity(num_tasks); - let counter = Arc::new(AtomicUsize::new(0)); - - for idx in 0 .. num_tasks { - let client = pool.next().clone(); - let counter = counter.clone(); - - tasks.push(tokio::spawn(async move { - let key = format!("foo-{}", idx); - - let mut expected = 0; - while incr_atomic(&counter) < 50_000_000 { - let actual: i64 = client.incr(&key).await?; - expected += 1; - if actual != expected { - return Err(RedisError::new( - RedisErrorKind::Unknown, - format!("Expected {}, found {}", expected, actual), - )); - } - } - - // println!("Task {} finished.", idx); - Ok::<_, RedisError>(()) - })); - } - let _ = futures::future::try_join_all(tasks).await?; - - Ok(()) -} - -#[cfg(feature = "i-keys")] -pub async fn should_pipeline_all(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let pipeline = client.pipeline(); - - let result: RedisValue = pipeline.set("foo", 1, None, None, false).await?; - assert!(result.is_queued()); - let result: RedisValue = pipeline.set("bar", 2, None, None, false).await?; - assert!(result.is_queued()); - let result: RedisValue = pipeline.incr("foo").await?; - assert!(result.is_queued()); - - let result: ((), (), i64) = pipeline.all().await?; - assert_eq!(result.2, 2); - Ok(()) -} - -#[cfg(all(feature = "i-keys", feature = "i-hashes"))] -pub async fn should_pipeline_all_error_early(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let pipeline = client.pipeline(); - - let result: RedisValue = pipeline.set("foo", 1, None, None, false).await?; - assert!(result.is_queued()); - let result: RedisValue = pipeline.hgetall("foo").await?; - assert!(result.is_queued()); - let result: RedisValue = pipeline.incr("foo").await?; - assert!(result.is_queued()); - - if let Err(e) = pipeline.all::().await { - // make sure we get the expected error from the server rather than a parsing error - assert_eq!(*e.kind(), RedisErrorKind::InvalidArgument); - } else { - panic!("Expected pipeline error."); - } - - Ok(()) -} - -#[cfg(feature = "i-keys")] -pub async fn should_pipeline_last(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let pipeline = client.pipeline(); - - let result: RedisValue = pipeline.set("foo", 1, None, None, false).await?; - assert!(result.is_queued()); - let result: RedisValue = pipeline.set("bar", 2, None, None, false).await?; - assert!(result.is_queued()); - let result: RedisValue = pipeline.incr("foo").await?; - assert!(result.is_queued()); - - let result: i64 = pipeline.last().await?; - assert_eq!(result, 2); - Ok(()) -} - -#[cfg(all(feature = "i-keys", feature = "i-hashes"))] -pub async fn should_pipeline_try_all(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let pipeline = client.pipeline(); - - pipeline.incr("foo").await?; - pipeline.hgetall("foo").await?; - let results = pipeline.try_all::().await; - - assert_eq!(results[0].clone().unwrap(), 1); - assert!(results[1].is_err()); - - Ok(()) -} - -#[cfg(feature = "i-server")] -pub async fn should_use_all_cluster_nodes_repeatedly(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let other = client.clone(); - let jh1 = tokio::spawn(async move { - for _ in 0 .. 200 { - other.flushall_cluster().await?; - } - - Ok::<_, RedisError>(()) - }); - let jh2 = tokio::spawn(async move { - for _ in 0 .. 200 { - client.flushall_cluster().await?; - } - - Ok::<_, RedisError>(()) - }); - - let _ = try_join(jh1, jh2).await?; - Ok(()) -} - -#[cfg(all(feature = "partial-tracing", feature = "i-keys"))] -pub async fn should_use_tracing_get_set(client: RedisClient, mut config: RedisConfig) -> Result<(), RedisError> { - config.tracing = TracingConfig::new(true); - let (perf, policy) = (client.perf_config(), client.client_reconnect_policy()); - let client = RedisClient::new(config, Some(perf), None, policy); - let _ = client.connect(); - let _ = client.wait_for_connect().await?; - - let _: () = client.set("foo", "bar", None, None, false).await?; - assert_eq!(client.get::("foo").await?, "bar"); - Ok(()) -} - -// #[cfg(feature = "dns")] -// pub struct TrustDnsResolver(TokioAsyncResolver); -// -// #[cfg(feature = "dns")] -// impl TrustDnsResolver { -// fn new() -> Self { -// TrustDnsResolver(TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default()).unwrap()) -// } -// } -// -// #[cfg(feature = "dns")] -// #[async_trait] -// impl Resolve for TrustDnsResolver { -// async fn resolve(&self, host: String, port: u16) -> Result { -// println!("Looking up {}", host); -// self.0.lookup_ip(&host).await.map_err(|e| e.into()).and_then(|ips| { -// let ip = match ips.iter().next() { -// Some(ip) => ip, -// None => return Err(RedisError::new(RedisErrorKind::IO, "Failed to lookup IP address.")), -// }; -// -// debug!("Mapped {}:{} to {}:{}", host, port, ip, port); -// Ok(SocketAddr::new(ip, port)) -// }) -// } -// } -// -// #[cfg(feature = "dns")] -// TODO fix the DNS configuration in docker so trustdns works -// pub async fn should_use_trust_dns(client: RedisClient, mut config: RedisConfig) -> Result<(), RedisError> { -// let perf = client.perf_config(); -// let policy = client.client_reconnect_policy(); -// -// if let ServerConfig::Centralized { ref mut host, .. } = config.server { -// host = utils::read_redis_centralized_host().0; -// } -// if let ServerConfig::Clustered { ref mut hosts } = config.server { -// hosts[0].0 = utils::read_redis_cluster_host().0; -// } -// -// println!("Trust DNS host: {:?}", config.server.hosts()); -// let client = RedisClient::new(config, Some(perf), policy); -// client.set_resolver(Arc::new(TrustDnsResolver::new())).await; -// -// let _ = client.connect(); -// let _ = client.wait_for_connect().await?; -// let _: () = client.ping().await?; -// let _ = client.quit().await?; -// Ok(()) -// } - -#[cfg(feature = "subscriber-client")] -pub async fn should_ping_with_subscriber_client(client: RedisClient, config: RedisConfig) -> Result<(), RedisError> { - let (perf, policy) = (client.perf_config(), client.client_reconnect_policy()); - let client = SubscriberClient::new(config, Some(perf), None, policy); - let _ = client.connect(); - let _ = client.wait_for_connect().await?; - - let _: () = client.ping().await?; - let _: () = client.subscribe("foo").await?; - let _: () = client.ping().await?; - let _ = client.quit().await?; - Ok(()) -} - -#[cfg(all(feature = "replicas", feature = "i-keys"))] -pub async fn should_replica_set_and_get(client: RedisClient, config: RedisConfig) -> Result<(), RedisError> { - let policy = client.client_reconnect_policy(); - let mut connection = client.connection_config().clone(); - connection.replica = ReplicaConfig::default(); - let client = RedisClient::new(config, None, Some(connection), policy); - client.init().await?; - - let _: () = client.set("foo", "bar", None, None, false).await?; - let result: String = client.replicas().get("foo").await?; - assert_eq!(result, "bar"); - - Ok(()) -} - -#[cfg(all(feature = "replicas", feature = "i-keys"))] -pub async fn should_replica_set_and_get_not_lazy(client: RedisClient, config: RedisConfig) -> Result<(), RedisError> { - let policy = client.client_reconnect_policy(); - let mut connection = client.connection_config().clone(); - connection.replica.lazy_connections = false; - let client = RedisClient::new(config, None, Some(connection), policy); - client.init().await?; - - let _: () = client.set("foo", "bar", None, None, false).await?; - let result: String = client.replicas().get("foo").await?; - assert_eq!(result, "bar"); - - Ok(()) -} - -#[cfg(all(feature = "replicas", feature = "i-keys"))] -pub async fn should_pipeline_with_replicas(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _: () = client.set("foo", 1, None, None, false).await?; - let _: () = client.set("bar", 2, None, None, false).await?; - - let pipeline = client.replicas().pipeline(); - let _: () = pipeline.get("foo").await?; - let _: () = pipeline.get("bar").await?; - let result: (i64, i64) = pipeline.all().await?; - - assert_eq!(result, (1, 2)); - Ok(()) -} - -#[cfg(all(feature = "replicas", feature = "i-keys"))] -pub async fn should_use_cluster_replica_without_redirection( - client: RedisClient, - config: RedisConfig, -) -> Result<(), RedisError> { - let mut connection = client.connection_config().clone(); - connection.replica = ReplicaConfig { - lazy_connections: true, - primary_fallback: false, - ignore_reconnection_errors: true, - ..ReplicaConfig::default() - }; - connection.max_redirections = 0; - let policy = client.client_reconnect_policy(); - - let client = RedisClient::new(config, None, Some(connection), policy); - let _ = client.connect(); - let _ = client.wait_for_connect().await?; - - let _: () = client.replicas().get("foo").await?; - let _: () = client.incr("foo").await?; - - Ok(()) -} - -pub async fn should_gracefully_quit(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let client = client.clone_new(); - let connection = client.connect(); - client.wait_for_connect().await?; - - client.ping().await?; - client.quit().await?; - let _ = connection.await; - - Ok(()) -} - -#[cfg(feature = "i-lists")] -pub async fn should_support_options_with_pipeline(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let options = Options { - timeout: Some(Duration::from_millis(100)), - max_attempts: Some(42), - max_redirections: Some(43), - ..Default::default() - }; - - let pipeline = client.pipeline().with_options(&options); - pipeline.blpop("foo", 2.0).await?; - let results = pipeline.try_all::().await; - assert_eq!(results[0].clone().unwrap_err().kind(), &RedisErrorKind::Timeout); - - Ok(()) -} - -#[cfg(feature = "i-keys")] -pub async fn should_reuse_pipeline(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let pipeline = client.pipeline(); - pipeline.incr("foo").await?; - pipeline.incr("foo").await?; - assert_eq!(pipeline.last::().await?, 2); - assert_eq!(pipeline.last::().await?, 4); - Ok(()) -} - -#[cfg(all(feature = "transactions", feature = "i-keys"))] -pub async fn should_support_options_with_trx(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let options = Options { - max_attempts: Some(1), - timeout: Some(Duration::from_secs(1)), - ..Default::default() - }; - let trx = client.multi().with_options(&options); - - trx.get("foo{1}").await?; - trx.set("foo{1}", "bar", None, None, false).await?; - trx.get("foo{1}").await?; - let (first, second, third): (Option, bool, String) = trx.exec(true).await?; - - assert_eq!(first, None); - assert!(second); - assert_eq!(third, "bar"); - Ok(()) -} - -#[cfg(all(feature = "i-keys", feature = "i-lists"))] -pub async fn should_manually_connect_twice(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let client = client.clone_new(); - let _old_connection = client.connect(); - client.wait_for_connect().await?; - - let _blpop_jh = tokio::spawn({ - let client = client.clone(); - async move { client.blpop::, _>("foo", 5.0).await } - }); - - sleep(Duration::from_millis(100)).await; - let new_connection = client.connect(); - client.wait_for_connect().await?; - - assert_eq!(client.incr::("bar").await?, 1); - client.quit().await?; - let _ = new_connection.await?; - Ok(()) -} - -pub async fn pool_should_connect_correctly_via_init_interface( - _: RedisClient, - config: RedisConfig, -) -> Result<(), RedisError> { - let pool = Builder::from_config(config).build_pool(5)?; - let task = pool.init().await?; - - pool.ping().await?; - pool.quit().await?; - task.await??; - Ok(()) -} - -pub async fn pool_should_fail_with_bad_host_via_init_interface( - _: RedisClient, - mut config: RedisConfig, -) -> Result<(), RedisError> { - config.fail_fast = true; - config.server = ServerConfig::new_centralized("incorrecthost", 1234); - let pool = Builder::from_config(config).build_pool(5)?; - assert!(pool.init().await.is_err()); - Ok(()) -} - -pub async fn pool_should_connect_correctly_via_wait_interface( - _: RedisClient, - config: RedisConfig, -) -> Result<(), RedisError> { - let pool = Builder::from_config(config).build_pool(5)?; - let task = pool.connect(); - pool.wait_for_connect().await?; - - pool.ping().await?; - pool.quit().await?; - task.await??; - Ok(()) -} - -pub async fn pool_should_fail_with_bad_host_via_wait_interface( - _: RedisClient, - mut config: RedisConfig, -) -> Result<(), RedisError> { - config.fail_fast = true; - config.server = ServerConfig::new_centralized("incorrecthost", 1234); - let pool = Builder::from_config(config).build_pool(5)?; - let task = pool.connect(); - assert!(pool.wait_for_connect().await.is_err()); - - let _ = task.await; - Ok(()) -} - -pub async fn should_connect_correctly_via_init_interface( - _: RedisClient, - config: RedisConfig, -) -> Result<(), RedisError> { - let client = Builder::from_config(config).build()?; - let task = client.init().await?; - - client.ping().await?; - client.quit().await?; - task.await??; - Ok(()) -} - -pub async fn should_fail_with_bad_host_via_init_interface( - _: RedisClient, - mut config: RedisConfig, -) -> Result<(), RedisError> { - config.fail_fast = true; - config.server = ServerConfig::new_centralized("incorrecthost", 1234); - let client = Builder::from_config(config).build()?; - assert!(client.init().await.is_err()); - Ok(()) -} - -pub async fn should_connect_correctly_via_wait_interface( - _: RedisClient, - config: RedisConfig, -) -> Result<(), RedisError> { - let client = Builder::from_config(config).build()?; - let task = client.connect(); - client.wait_for_connect().await?; - - client.ping().await?; - client.quit().await?; - task.await??; - Ok(()) -} - -pub async fn should_fail_with_bad_host_via_wait_interface( - _: RedisClient, - mut config: RedisConfig, -) -> Result<(), RedisError> { - config.fail_fast = true; - config.server = ServerConfig::new_centralized("incorrecthost", 1234); - let client = Builder::from_config(config).build()?; - let task = client.connect(); - assert!(client.wait_for_connect().await.is_err()); - - let _ = task.await; - Ok(()) -} - -// TODO this will require a breaking change to support. The `Replicas` struct assumes that it's operating on a -// `RedisClient` and is not generic for other client or decorator types. `Replicas` must become `Replicas` first. -#[allow(dead_code)] -#[cfg(all(feature = "replicas", feature = "i-keys"))] -pub async fn should_combine_options_and_replicas(client: RedisClient, config: RedisConfig) -> Result<(), RedisError> { - let mut connection = client.connection_config().clone(); - connection.replica = ReplicaConfig { - lazy_connections: true, - primary_fallback: false, - ignore_reconnection_errors: true, - ..ReplicaConfig::default() - }; - connection.max_redirections = 0; - let policy = client.client_reconnect_policy(); - let client = RedisClient::new(config, None, Some(connection), policy); - client.init().await?; - - // change the cluster hash policy such that we get a routing error if both replicas and options are correctly - // applied - let key = RedisKey::from_static_str("foo"); - let (servers, foo_owner) = client - .cached_cluster_state() - .map(|s| { - ( - s.unique_primary_nodes(), - s.get_server(key.cluster_hash()).unwrap().clone(), - ) - }) - .unwrap(); - let wrong_owner = servers.iter().find(|s| foo_owner != **s).unwrap().clone(); - - let options = Options { - max_redirections: Some(0), - max_attempts: Some(1), - cluster_node: Some(wrong_owner), - ..Default::default() - }; - - let error = client - .with_options(&options) - .replicas() - .get::, _>(key) - .await - .err() - .unwrap(); - - // not ideal - assert_eq!(error.details(), "Too many redirections."); - Ok(()) -} - -pub async fn should_fail_on_centralized_connect(_: RedisClient, mut config: RedisConfig) -> Result<(), RedisError> { - if let ServerConfig::Centralized { server } = config.server { - config.server = ServerConfig::Clustered { - hosts: vec![server], - policy: ClusterDiscoveryPolicy::default(), - }; - } else { - // skip for unix socket and sentinel tests - return Ok(()); - } - - let client = RedisClient::new(config, None, None, None); - client.connect(); - - if let Err(err) = client.wait_for_connect().await { - assert_eq!(*err.kind(), RedisErrorKind::Config, "err = {:?}", err); - return Ok(()); - } - - Err(RedisError::new(RedisErrorKind::Unknown, "Expected a config error.")) -} diff --git a/tests/integration/pool/mod.rs b/tests/integration/pool/mod.rs deleted file mode 100644 index 92ca9a3a..00000000 --- a/tests/integration/pool/mod.rs +++ /dev/null @@ -1,110 +0,0 @@ -use fred::{ - clients::{RedisClient, RedisPool}, - error::RedisError, - interfaces::*, - types::RedisConfig, -}; - -#[cfg(feature = "i-keys")] -use fred::types::{Builder, ReconnectPolicy}; -#[cfg(feature = "i-keys")] -use futures::future::try_join_all; - -async fn create_and_ping_pool(config: &RedisConfig, count: usize) -> Result<(), RedisError> { - let pool = RedisPool::new(config.clone(), None, None, None, count)?; - pool.init().await?; - - for client in pool.clients().iter() { - client.ping().await?; - } - - pool.ping().await?; - pool.quit().await?; - Ok(()) -} - -pub async fn should_connect_and_ping_static_pool_single_conn( - _: RedisClient, - config: RedisConfig, -) -> Result<(), RedisError> { - create_and_ping_pool(&config, 1).await -} - -pub async fn should_connect_and_ping_static_pool_two_conn( - _: RedisClient, - config: RedisConfig, -) -> Result<(), RedisError> { - create_and_ping_pool(&config, 2).await -} - -#[cfg(feature = "i-keys")] -pub async fn should_incr_exclusive_pool(client: RedisClient, config: RedisConfig) -> Result<(), RedisError> { - let perf = client.perf_config(); - let policy = client - .client_reconnect_policy() - .unwrap_or(ReconnectPolicy::new_linear(0, 1000, 100)); - let connection = client.connection_config().clone(); - let pool = Builder::from_config(config) - .set_performance_config(perf) - .set_policy(policy) - .set_connection_config(connection) - .build_exclusive_pool(5)?; - pool.init().await?; - - for _ in 0 .. 10 { - let client = pool.acquire().await; - client.incr("foo").await?; - } - assert_eq!(client.get::("foo").await?, 10); - client.del("foo").await?; - - let mut fts = Vec::with_capacity(10); - for _ in 0 .. 10 { - let pool = pool.clone(); - fts.push(async move { - let client = pool.acquire().await; - client.incr::("foo").await - }); - } - try_join_all(fts).await?; - assert_eq!(client.get::("foo").await?, 10); - - Ok(()) -} - -#[cfg(all(feature = "i-keys", feature = "transactions"))] -pub async fn should_watch_and_trx_exclusive_pool(client: RedisClient, config: RedisConfig) -> Result<(), RedisError> { - let perf = client.perf_config(); - let policy = client - .client_reconnect_policy() - .unwrap_or(ReconnectPolicy::new_linear(0, 1000, 100)); - let connection = client.connection_config().clone(); - let pool = Builder::from_config(config) - .set_performance_config(perf) - .set_policy(policy) - .set_connection_config(connection) - .build_exclusive_pool(5)?; - pool.init().await?; - - client.set("foo{1}", 1, None, None, false).await?; - - let results: Option<(i64, i64, i64)> = { - let client = pool.acquire().await; - - client.watch("foo").await?; - if let Some(1) = client.get::, _>("foo{1}").await? { - let trx = client.multi(); - trx.incr("foo{1}").await?; - trx.incr("bar{1}").await?; - trx.incr("baz{1}").await?; - Some(trx.exec(true).await?) - } else { - None - } - }; - assert_eq!(results, Some((2, 1, 1))); - assert_eq!(client.get::("bar{1}").await?, 1); - assert_eq!(client.get::("baz{1}").await?, 1); - - Ok(()) -} diff --git a/tests/integration/pubsub/mod.rs b/tests/integration/pubsub/mod.rs deleted file mode 100644 index 09ecf316..00000000 --- a/tests/integration/pubsub/mod.rs +++ /dev/null @@ -1,264 +0,0 @@ -use super::utils::should_use_sentinel_config; -use fred::{interfaces::PubsubInterface, prelude::*}; -use futures::{Stream, StreamExt}; -use std::{collections::HashMap, time::Duration}; -use tokio::time::sleep; - -const CHANNEL1: &str = "foo"; -const CHANNEL2: &str = "bar"; -const CHANNEL3: &str = "baz"; -const FAKE_MESSAGE: &str = "wibble"; -const NUM_MESSAGES: i64 = 20; - -async fn wait_a_sec() { - // pubsub command responses arrive out of band therefore it's hard to synchronize calls across clients. in CI the - // machines are pretty small and subscribe-then-check commands sometimes give strange results due to these timing - // issues. - tokio::time::sleep(Duration::from_millis(20)).await; -} - -pub async fn should_publish_and_recv_messages(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let subscriber_client = client.clone_new(); - subscriber_client.connect(); - subscriber_client.wait_for_connect().await?; - subscriber_client.subscribe(CHANNEL1).await?; - - let subscriber_jh = tokio::spawn(async move { - let mut message_stream = subscriber_client.message_rx(); - - let mut count = 0; - while count < NUM_MESSAGES { - if let Ok(message) = message_stream.recv().await { - assert_eq!(CHANNEL1, message.channel); - assert_eq!(format!("{}-{}", FAKE_MESSAGE, count), message.value.as_str().unwrap()); - count += 1; - } - } - - Ok::<_, RedisError>(()) - }); - - sleep(Duration::from_secs(1)).await; - for idx in 0 .. NUM_MESSAGES { - // https://redis.io/commands/publish#return-value - client.publish(CHANNEL1, format!("{}-{}", FAKE_MESSAGE, idx)).await?; - - // pubsub messages may arrive out of order due to cross-cluster broadcasting - sleep(Duration::from_millis(50)).await; - } - let _ = subscriber_jh.await?; - - Ok(()) -} - -pub async fn should_ssubscribe_and_recv_messages(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let subscriber_client = client.clone_new(); - subscriber_client.connect(); - subscriber_client.wait_for_connect().await?; - subscriber_client.ssubscribe(CHANNEL1).await?; - - let subscriber_jh = tokio::spawn(async move { - let mut message_stream = subscriber_client.message_rx(); - - let mut count = 0; - while count < NUM_MESSAGES { - if let Ok(message) = message_stream.recv().await { - assert_eq!(CHANNEL1, message.channel); - assert_eq!(format!("{}-{}", FAKE_MESSAGE, count), message.value.as_str().unwrap()); - count += 1; - } - } - - Ok::<_, RedisError>(()) - }); - - sleep(Duration::from_secs(1)).await; - for idx in 0 .. NUM_MESSAGES { - // https://redis.io/commands/publish#return-value - client.spublish(CHANNEL1, format!("{}-{}", FAKE_MESSAGE, idx)).await?; - - // pubsub messages may arrive out of order due to cross-cluster broadcasting - sleep(Duration::from_millis(50)).await; - } - let _ = subscriber_jh.await?; - - Ok(()) -} - -pub async fn should_psubscribe_and_recv_messages(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let channels = vec![CHANNEL1, CHANNEL2, CHANNEL3]; - let subscriber_channels = channels.clone(); - - let subscriber_client = client.clone_new(); - subscriber_client.connect(); - subscriber_client.wait_for_connect().await?; - subscriber_client.psubscribe(channels.clone()).await?; - - let subscriber_jh = tokio::spawn(async move { - let mut message_stream = subscriber_client.message_rx(); - - let mut count = 0; - while count < NUM_MESSAGES { - if let Ok(message) = message_stream.recv().await { - assert!(subscriber_channels.contains(&&*message.channel)); - assert_eq!(format!("{}-{}", FAKE_MESSAGE, count), message.value.as_str().unwrap()); - count += 1; - } - } - - Ok::<_, RedisError>(()) - }); - - sleep(Duration::from_secs(1)).await; - for idx in 0 .. NUM_MESSAGES { - let channel = channels[idx as usize % channels.len()]; - - // https://redis.io/commands/publish#return-value - client.publish(channel, format!("{}-{}", FAKE_MESSAGE, idx)).await?; - - // pubsub messages may arrive out of order due to cross-cluster broadcasting - sleep(Duration::from_millis(50)).await; - } - let _ = subscriber_jh.await?; - - Ok(()) -} - -pub async fn should_unsubscribe_from_all(publisher: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let subscriber = publisher.clone_new(); - let connection = subscriber.connect(); - subscriber.wait_for_connect().await?; - subscriber.subscribe(vec![CHANNEL1, CHANNEL2, CHANNEL3]).await?; - let mut message_stream = subscriber.message_rx(); - - tokio::spawn(async move { - if let Ok(message) = message_stream.recv().await { - // unsubscribe without args will result in 3 messages in this case, and none should show up here - panic!("Recv unexpected pubsub message: {:?}", message); - } - - Ok::<_, RedisError>(()) - }); - - subscriber.unsubscribe(()).await?; - sleep(Duration::from_secs(1)).await; - - // make sure the response buffer is flushed correctly by this point - assert_eq!(subscriber.ping::().await?, "PONG"); - assert_eq!(subscriber.ping::().await?, "PONG"); - assert_eq!(subscriber.ping::().await?, "PONG"); - - subscriber.quit().await?; - let _ = connection.await?; - Ok(()) -} - -pub async fn should_get_pubsub_channels(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let subscriber = client.clone_new(); - subscriber.connect(); - subscriber.wait_for_connect().await?; - - let channels: Vec = client.pubsub_channels("*").await?; - let expected_len = if should_use_sentinel_config() { - // "__sentinel__:hello" is always there - 1 - } else { - 0 - }; - assert_eq!(channels.len(), expected_len); - - subscriber.subscribe("foo").await?; - subscriber.subscribe("bar").await?; - wait_a_sec().await; - let mut channels: Vec = client.pubsub_channels("*").await?; - channels.sort(); - - let expected = if should_use_sentinel_config() { - vec!["__sentinel__:hello".into(), "bar".to_string(), "foo".to_string()] - } else { - vec!["bar".to_string(), "foo".to_string()] - }; - assert_eq!(channels, expected); - Ok(()) -} - -pub async fn should_get_pubsub_numpat(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let subscriber = client.clone_new(); - subscriber.connect(); - subscriber.wait_for_connect().await?; - - assert_eq!(client.pubsub_numpat::().await?, 0); - subscriber.psubscribe("foo*").await?; - subscriber.psubscribe("bar*").await?; - wait_a_sec().await; - assert_eq!(client.pubsub_numpat::().await?, 2); - - Ok(()) -} - -pub async fn should_get_pubsub_nunmsub(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let subscriber = client.clone_new(); - subscriber.connect(); - subscriber.wait_for_connect().await?; - - let mut expected: HashMap = HashMap::new(); - expected.insert("foo".into(), 0); - expected.insert("bar".into(), 0); - let channels: HashMap = client.pubsub_numsub(vec!["foo", "bar"]).await?; - assert_eq!(channels, expected); - - subscriber.subscribe("foo").await?; - subscriber.subscribe("bar").await?; - wait_a_sec().await; - let channels: HashMap = client.pubsub_numsub(vec!["foo", "bar"]).await?; - - let mut expected: HashMap = HashMap::new(); - expected.insert("foo".into(), 1); - expected.insert("bar".into(), 1); - assert_eq!(channels, expected); - - Ok(()) -} - -pub async fn should_get_pubsub_shard_channels(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let subscriber = client.clone_new(); - subscriber.connect(); - subscriber.wait_for_connect().await?; - - let channels: Vec = client.pubsub_shardchannels("{1}*").await?; - assert!(channels.is_empty()); - - subscriber.ssubscribe("{1}foo").await?; - subscriber.ssubscribe("{1}bar").await?; - wait_a_sec().await; - - let mut channels: Vec = client.pubsub_shardchannels("{1}*").await?; - channels.sort(); - assert_eq!(channels, vec!["{1}bar".to_string(), "{1}foo".to_string()]); - - Ok(()) -} - -pub async fn should_get_pubsub_shard_numsub(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let subscriber = client.clone_new(); - subscriber.connect(); - subscriber.wait_for_connect().await?; - - let mut expected: HashMap = HashMap::new(); - expected.insert("foo{1}".into(), 0); - expected.insert("bar{1}".into(), 0); - let channels: HashMap = client.pubsub_shardnumsub(vec!["foo{1}", "bar{1}"]).await?; - assert_eq!(channels, expected); - - subscriber.ssubscribe("foo{1}").await?; - subscriber.ssubscribe("bar{1}").await?; - wait_a_sec().await; - let channels: HashMap = client.pubsub_shardnumsub(vec!["foo{1}", "bar{1}"]).await?; - - let mut expected: HashMap = HashMap::new(); - expected.insert("foo{1}".into(), 1); - expected.insert("bar{1}".into(), 1); - assert_eq!(channels, expected); - - Ok(()) -} diff --git a/tests/integration/redis_json/mod.rs b/tests/integration/redis_json/mod.rs deleted file mode 100644 index 952106c0..00000000 --- a/tests/integration/redis_json/mod.rs +++ /dev/null @@ -1,199 +0,0 @@ -use fred::{ - clients::RedisClient, - error::RedisError, - interfaces::RedisJsonInterface, - json_quote, - types::{RedisConfig, RedisValue}, - util::NONE, -}; -use serde_json::{json, Value}; - -pub async fn should_get_and_set_basic_obj(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let value: Value = client.json_get("foo", NONE, NONE, NONE, "$").await?; - assert_eq!(value, Value::Null); - - let value = json!({ - "a": "b", - "c": 1 - }); - let _: () = client.json_set("foo", "$", value.clone(), None).await?; - let result: Value = client.json_get("foo", NONE, NONE, NONE, "$").await?; - assert_eq!(value, result[0]); - - Ok(()) -} - -pub async fn should_get_and_set_stringified_obj(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let value: Value = client.json_get("foo", NONE, NONE, NONE, "$").await?; - assert_eq!(value, Value::Null); - - let value = json!({ - "a": "b", - "c": 1 - }); - let _: () = client - .json_set("foo", "$", serde_json::to_string(&value)?, None) - .await?; - let result: Value = client.json_get("foo", NONE, NONE, NONE, "$").await?; - assert_eq!(value, result[0]); - - Ok(()) -} - -pub async fn should_array_append(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _: () = client.json_set("foo", "$", json!(["a", "b"]), None).await?; - - // need to double quote string values - let size: i64 = client - .json_arrappend("foo", "$", vec![json_quote!("c"), json_quote!("d")]) - .await?; - assert_eq!(size, 4); - let size: i64 = client.json_arrappend("foo", "$", vec![json!({"e": "f"})]).await?; - assert_eq!(size, 5); - let len: i64 = client.json_arrlen("foo", NONE).await?; - assert_eq!(len, 5); - - let result: Value = client.json_get("foo", NONE, NONE, NONE, "$").await?; - assert_eq!(result[0], json!(["a", "b", "c", "d", {"e": "f"}])); - - Ok(()) -} - -pub async fn should_modify_arrays(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _: () = client.json_set("foo", "$", json!(["a", "d"]), None).await?; - let len: i64 = client - .json_arrinsert("foo", "$", 1, vec![json_quote!("b"), json_quote!("c")]) - .await?; - assert_eq!(len, 4); - let idx: usize = client.json_arrindex("foo", "$", json_quote!("b"), None, None).await?; - assert_eq!(idx, 1); - let len: usize = client.json_arrlen("foo", NONE).await?; - assert_eq!(len, 4); - - Ok(()) -} - -pub async fn should_pop_and_trim_arrays(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _: () = client.json_set("foo", "$", json!(["a", "b"]), None).await?; - let val: Value = client.json_arrpop("foo", NONE, None).await?; - assert_eq!(val, json!("b")); - - let _: () = client.json_set("foo", "$", json!(["a", "b", "c", "d"]), None).await?; - let len: usize = client.json_arrtrim("foo", "$", 0, -2).await?; - assert_eq!(len, 3); - - let vals: Value = client.json_get("foo", NONE, NONE, NONE, "$").await?; - assert_eq!(vals[0], json!(["a", "b", "c"])); - - Ok(()) -} - -pub async fn should_get_set_del_obj(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let value = json!({ - "a": "b", - "c": 1, - "d": true - }); - let _: () = client.json_set("foo", "$", value.clone(), None).await?; - let result: Value = client.json_get("foo", NONE, NONE, NONE, "$").await?; - assert_eq!(value, result[0]); - - let count: i64 = client.json_del("foo", "$..c").await?; - assert_eq!(count, 1); - - let result: Value = client.json_get("foo", NONE, NONE, NONE, "$").await?; - assert_eq!(result[0], json!({ "a": "b", "d": true })); - - Ok(()) -} - -pub async fn should_merge_objects(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let foo = json!({ "a": "b", "c": { "d": "e" } }); - let bar = json!({ "a": "b1", "c": { "d1": "e1" }, "y": "z" }); - let expected = json!({ "a": "b1", "c": {"d": "e", "d1": "e1"}, "y": "z" }); - - let _: () = client.json_set("foo", "$", foo.clone(), None).await?; - let _: () = client.json_merge("foo", "$", bar.clone()).await?; - let merged: Value = client.json_get("foo", NONE, NONE, NONE, "$").await?; - assert_eq!(merged[0], expected); - - Ok(()) -} - -pub async fn should_mset_and_mget(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let values = [json!({ "a": "b" }), json!({ "c": "d" })]; - let args = vec![("foo{1}", "$", values[0].clone()), ("bar{1}", "$", values[1].clone())]; - let _: () = client.json_mset(args).await?; - - let result: Value = client.json_mget(vec!["foo{1}", "bar{1}"], "$").await?; - // response is nested: Array [Array [Object {"a": String("b")}], Array [Object {"c": String("d")}]] - assert_eq!(result, json!([[values[0]], [values[1]]])); - - Ok(()) -} - -pub async fn should_incr_numbers(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _: () = client.json_set("foo", "$", json!({ "a": 1 }), None).await?; - let vals: Value = client.json_numincrby("foo", "$.a", 2).await?; - assert_eq!(vals[0], 3); - - Ok(()) -} - -pub async fn should_inspect_objects(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let value = json!({ - "a": "b", - "e": { - "f": "g", - "h": "i", - "j": [{ "k": "l" }] - } - }); - let _: () = client.json_set("foo", "$", value.clone(), None).await?; - let keys: Vec> = client.json_objkeys("foo", Some("$")).await?; - assert_eq!(keys[0], vec!["a".to_string(), "e".to_string()]); - let keys: Vec> = client.json_objkeys("foo", Some("$.e")).await?; - assert_eq!(keys[0], vec!["f".to_string(), "h".to_string(), "j".to_string()]); - - let len: usize = client.json_objlen("foo", NONE).await?; - assert_eq!(len, 2); - let len: usize = client.json_objlen("foo", Some("$.e")).await?; - assert_eq!(len, 3); - - Ok(()) -} - -pub async fn should_modify_strings(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _: () = client.json_set("foo", "$", json!({ "a": "abc123" }), None).await?; - let len: usize = client.json_strlen("foo", Some("$.a")).await?; - assert_eq!(len, 6); - - let len: usize = client.json_strappend("foo", Some("$.a"), json_quote!("456")).await?; - assert_eq!(len, 9); - let len: usize = client.json_strlen("foo", Some("$.a")).await?; - assert_eq!(len, 9); - let value: Value = client.json_get("foo", NONE, NONE, NONE, "$").await?; - assert_eq!(value[0], json!({ "a": "abc123456" })); - - Ok(()) -} - -pub async fn should_toggle_boolean(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _: () = client.json_set("foo", "$", json!({ "a": 1, "b": true }), None).await?; - let new_val: bool = client.json_toggle("foo", "$.b").await?; - assert!(!new_val); - - Ok(()) -} - -pub async fn should_get_value_type(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _: () = client.json_set("foo", "$", json!({ "a": 1, "b": true }), None).await?; - let val: String = client.json_type("foo", NONE).await?; - assert_eq!(val, "object"); - let val: String = client.json_type("foo", Some("$.a")).await?; - assert_eq!(val, "integer"); - let val: String = client.json_type("foo", Some("$.b")).await?; - assert_eq!(val, "boolean"); - - Ok(()) -} diff --git a/tests/integration/redisearch/mod.rs b/tests/integration/redisearch/mod.rs deleted file mode 100644 index 75331fdd..00000000 --- a/tests/integration/redisearch/mod.rs +++ /dev/null @@ -1,289 +0,0 @@ -use fred::{ - error::RedisError, - prelude::*, - types::{ - AggregateOperation, - FtAggregateOptions, - FtCreateOptions, - FtSearchOptions, - IndexKind, - Load, - RedisMap, - SearchSchema, - SearchSchemaKind, - }, - util::NONE, -}; -use maplit::hashmap; -use rand::{thread_rng, Rng}; -use redis_protocol::resp3::types::RespVersion; -use std::{collections::HashMap, time::Duration}; - -pub async fn should_list_indexes(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - assert!(client.ft_list::>().await?.is_empty()); - - client - .ft_create("foo", FtCreateOptions::default(), vec![SearchSchema { - field_name: "bar".into(), - alias: Some("baz".into()), - kind: SearchSchemaKind::Numeric { - sortable: false, - unf: false, - noindex: false, - }, - }]) - .await?; - - assert_eq!(client.ft_list::>().await?, vec!["foo".to_string()]); - Ok(()) -} - -pub async fn should_index_and_info_basic_hash(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - assert!(client.ft_list::>().await?.is_empty()); - - client - .ft_create( - "foo_idx", - FtCreateOptions { - on: Some(IndexKind::Hash), - ..Default::default() - }, - vec![SearchSchema { - field_name: "bar".into(), - alias: Some("baz".into()), - kind: SearchSchemaKind::Text { - sortable: false, - unf: false, - noindex: false, - phonetic: None, - weight: None, - withsuffixtrie: false, - nostem: false, - }, - }], - ) - .await?; - - client.hset("foo", ("bar", "abc123")).await?; - tokio::time::sleep(Duration::from_millis(100)).await; - - let mut info: HashMap = client.ft_info("foo_idx").await?; - assert_eq!(info.remove("num_docs").unwrap_or(RedisValue::Null).convert::()?, 1); - - Ok(()) -} - -pub async fn should_index_and_search_hash(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - assert!(client.ft_list::>().await?.is_empty()); - - client - .ft_create( - "foo_idx", - FtCreateOptions { - on: Some(IndexKind::Hash), - prefixes: vec!["record:".into()], - ..Default::default() - }, - vec![SearchSchema { - field_name: "bar".into(), - alias: None, - kind: SearchSchemaKind::Text { - sortable: false, - unf: false, - noindex: false, - phonetic: None, - weight: None, - withsuffixtrie: false, - nostem: false, - }, - }], - ) - .await?; - - client.hset("record:1", ("bar", "abc 123")).await?; - client.hset("record:2", ("bar", "abc 345")).await?; - client.hset("record:3", ("bar", "def 678")).await?; - tokio::time::sleep(Duration::from_millis(100)).await; - - if client.protocol_version() == RespVersion::RESP3 { - // RESP3 uses maps and includes extra metadata fields - let mut results: HashMap = - client.ft_search("foo_idx", "*", FtSearchOptions::default()).await?; - assert_eq!( - results - .get("total_results") - .cloned() - .unwrap_or(RedisValue::Null) - .convert::()?, - 3 - ); - - // {"attributes":[],"format":"STRING","results":[{"extra_attributes":{"bar":"abc - // 123"},"id":"record:1","values":[]},{"extra_attributes":{"bar":"abc - // 345"},"id":"record:2","values":[]},{"extra_attributes":{"bar":"def - // 678"},"id":"record:3","values":[]}],"total_results":3,"warning":[]} - let results: Vec> = results.remove("results").unwrap().convert()?; - let expected = vec![ - hashmap! { - "id" => "record:1".into(), - "values" => RedisValue::Array(vec![]), - "extra_attributes" => hashmap! { - "bar" => "abc 123" - }.try_into()? - }, - hashmap! { - "id" => "record:2".into(), - "values" => RedisValue::Array(vec![]), - "extra_attributes" => hashmap! { - "bar" => "abc 345" - }.try_into()? - }, - hashmap! { - "id" => "record:3".into(), - "values" => RedisValue::Array(vec![]), - "extra_attributes" => hashmap! { - "bar" => "def 678" - } - .try_into()? - }, - ] - .into_iter() - .map(|m| { - m.into_iter() - .map(|(k, v)| (k.to_string(), v)) - .collect::>() - }) - .collect::>(); - assert_eq!(results, expected); - } else { - // RESP2 uses an array format w/o extra metadata - let results: (usize, RedisKey, RedisKey, RedisKey) = client - .ft_search("foo_idx", "*", FtSearchOptions { - nocontent: true, - ..Default::default() - }) - .await?; - assert_eq!(results, (3, "record:1".into(), "record:2".into(), "record:3".into())); - let results: (usize, RedisKey, RedisKey) = client - .ft_search("foo_idx", "@bar:(abc)", FtSearchOptions { - nocontent: true, - ..Default::default() - }) - .await?; - assert_eq!(results, (2, "record:1".into(), "record:2".into())); - let results: (usize, RedisKey, (String, String)) = client - .ft_search("foo_idx", "@bar:(def)", FtSearchOptions::default()) - .await?; - assert_eq!(results, (1, "record:3".into(), ("bar".into(), "def 678".into()))); - } - - Ok(()) -} - -pub async fn should_index_and_aggregate_timestamps(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - assert!(client.ft_list::>().await?.is_empty()); - - // https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/aggregations/ - client - .ft_create( - "timestamp_idx", - FtCreateOptions { - on: Some(IndexKind::Hash), - prefixes: vec!["record:".into()], - ..Default::default() - }, - vec![SearchSchema { - field_name: "timestamp".into(), - alias: None, - kind: SearchSchemaKind::Numeric { - sortable: true, - unf: false, - noindex: false, - }, - }], - ) - .await?; - - for idx in 0 .. 100 { - let rand: u64 = thread_rng().gen_range(0 .. 10000); - client - .hset(format!("record:{}", idx), [ - ("timestamp", idx), - ("user_id", idx + 1000), - ("rand", rand), - ]) - .await?; - } - tokio::time::sleep(Duration::from_millis(100)).await; - - if client.protocol_version() == RespVersion::RESP3 { - // RESP3 uses maps and includes extra metadata fields - - // FT.AGGREGATE myIndex "*" - // APPLY "@timestamp - (@timestamp % 3600)" AS hour - let mut result: HashMap = client - .ft_aggregate("timestamp_idx", "*", FtAggregateOptions { - load: Some(Load::All), - pipeline: vec![AggregateOperation::Apply { - expression: "@timestamp - (@timestamp % 3600)".into(), - name: "hour".into(), - }], - ..Default::default() - }) - .await?; - - let results: Vec = result.remove("results").unwrap().convert()?; - for (idx, val) in results.into_iter().enumerate() { - let mut val: HashMap = val.convert()?; - let mut val: HashMap = val.remove("extra_attributes").unwrap().convert()?; - assert_eq!(val.remove("timestamp").unwrap(), idx); - assert_eq!(val.remove("hour").unwrap(), 0); - assert_eq!(val.remove("user_id").unwrap(), 1000 + idx); - } - } else { - // FT.AGGREGATE myIndex "*" - // APPLY "@timestamp - (@timestamp % 3600)" AS hour - let result: Vec = client - .ft_aggregate("timestamp_idx", "*", FtAggregateOptions { - load: Some(Load::All), - pipeline: vec![AggregateOperation::Apply { - expression: "@timestamp - (@timestamp % 3600)".into(), - name: "hour".into(), - }], - ..Default::default() - }) - .await?; - - for (idx, val) in result.into_iter().enumerate() { - if idx == 0 { - assert_eq!(val.convert::()?, 1); - } else { - let mut val: HashMap = val.convert()?; - assert_eq!(val.remove("timestamp").unwrap(), idx - 1); - assert_eq!(val.remove("hour").unwrap(), 0); - assert_eq!(val.remove("user_id").unwrap(), 1000 + idx - 1); - } - } - } - - // TODO - // FT.AGGREGATE myIndex "*" - // APPLY "@timestamp - (@timestamp % 3600)" AS hour - // GROUPBY 1 @hour - // REDUCE COUNT_DISTINCT 1 @user_id AS num_users - - // FT.AGGREGATE myIndex "*" - // APPLY "@timestamp - (@timestamp % 3600)" AS hour - // GROUPBY 1 @hour - // REDUCE COUNT_DISTINCT 1 @user_id AS num_users - // SORTBY 2 @hour ASC - - // FT.AGGREGATE myIndex "*" - // APPLY "@timestamp - (@timestamp % 3600)" AS hour - // GROUPBY 1 @hour - // REDUCE COUNT_DISTINCT 1 @user_id AS num_users - // SORTBY 2 @hour ASC - // APPLY timefmt(@hour) AS hour - - Ok(()) -} diff --git a/tests/integration/scanning/mod.rs b/tests/integration/scanning/mod.rs deleted file mode 100644 index 450e06a2..00000000 --- a/tests/integration/scanning/mod.rs +++ /dev/null @@ -1,148 +0,0 @@ -#![allow(dead_code)] -use fred::{prelude::*, types::Scanner}; -use futures::TryStreamExt; -use tokio_stream::StreamExt; - -const SCAN_KEYS: i64 = 100; - -#[cfg(feature = "i-keys")] -pub async fn should_scan_keyspace(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - for idx in 0 .. SCAN_KEYS { - client - .set(format!("foo-{}-{}", idx, "{1}"), idx, None, None, false) - .await?; - } - - let count = client - .scan("foo*{1}", Some(10), None) - .try_fold(0, |mut count, mut result| async move { - if let Some(results) = result.take_results() { - count += results.len() as i64; - // scanning wont return results in any particular order, so we just check the format of the key - - for key in results.into_iter() { - let parts: Vec<&str> = key.as_str().unwrap().split('-').collect(); - assert!(parts[1].parse::().is_ok()); - } - } else { - panic!("Empty results in scan."); - } - - result.next()?; - Ok(count) - }) - .await?; - - assert_eq!(count, SCAN_KEYS); - Ok(()) -} - -#[cfg(feature = "i-hashes")] -pub async fn should_hscan_hash(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - for idx in 0 .. SCAN_KEYS { - let value = (format!("bar-{}", idx), idx); - client.hset("foo", value).await?; - } - - let count = client - .hscan("foo", "bar*", Some(10)) - .try_fold(0_i64, |mut count, mut result| async move { - if let Some(results) = result.take_results() { - count += results.len() as i64; - - // scanning wont return results in any particular order, so we just check the format of the key - for (key, _) in results.iter() { - let parts: Vec<&str> = key.as_str().unwrap().split('-').collect(); - assert!(parts[1].parse::().is_ok()); - } - } else { - panic!("Empty results in hscan."); - } - - result.next()?; - Ok(count) - }) - .await?; - - assert_eq!(count, SCAN_KEYS); - Ok(()) -} - -#[cfg(feature = "i-sets")] -pub async fn should_sscan_set(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - for idx in 0 .. SCAN_KEYS { - client.sadd("foo", idx).await?; - } - - let count = client - .sscan("foo", "*", Some(10)) - .try_fold(0_i64, |mut count, mut result| async move { - if let Some(results) = result.take_results() { - count += results.len() as i64; - - for value in results.into_iter() { - assert!(value.as_i64().is_some()); - } - } else { - panic!("Empty sscan result"); - } - - result.next()?; - Ok(count) - }) - .await?; - - assert_eq!(count, SCAN_KEYS); - Ok(()) -} - -#[cfg(feature = "i-sorted-sets")] -pub async fn should_zscan_sorted_set(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - for idx in 0 .. SCAN_KEYS { - let (score, value) = (idx as f64, format!("foo-{}", idx)); - client.zadd("foo", None, None, false, false, (score, value)).await?; - } - - let count = client - .zscan("foo", "*", Some(10)) - .try_fold(0_i64, |mut count, mut result| async move { - if let Some(results) = result.take_results() { - count += results.len() as i64; - - for (value, score) in results.into_iter() { - let value_str = value.as_str().unwrap(); - let parts: Vec<&str> = value_str.split('-').collect(); - let value_suffix = parts[1].parse::().unwrap(); - - assert_eq!(value_suffix, score); - } - } else { - panic!("Empty zscan result"); - } - - result.next()?; - Ok(count) - }) - .await?; - - assert_eq!(count, SCAN_KEYS); - Ok(()) -} - -#[cfg(feature = "i-keys")] -pub async fn should_scan_cluster(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - for idx in 0 .. 2000 { - client.set(idx, idx, None, None, false).await?; - } - - let mut count = 0; - let mut scan_stream = client.scan_cluster("*", Some(10), None); - while let Some(Ok(mut page)) = scan_stream.next().await { - let results = page.take_results(); - count += results.unwrap().len(); - let _ = page.next(); - } - - assert_eq!(count, 2000); - Ok(()) -} diff --git a/tests/integration/server/mod.rs b/tests/integration/server/mod.rs deleted file mode 100644 index 8b312c2a..00000000 --- a/tests/integration/server/mod.rs +++ /dev/null @@ -1,76 +0,0 @@ -use fred::{cmd, prelude::*}; -use std::time::Duration; -use tokio::time::sleep; - -pub async fn should_flushall(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.custom(cmd!("SET"), vec!["foo{1}", "bar"]).await?; - if client.is_clustered() { - client.flushall_cluster().await?; - } else { - client.flushall(false).await?; - }; - - let result: Option = client.custom(cmd!("GET"), vec!["foo{1}"]).await?; - assert!(result.is_none()); - - Ok(()) -} - -pub async fn should_read_server_info(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let info: Option = client.info(None).await?; - assert!(info.is_some()); - - Ok(()) -} - -pub async fn should_ping_server(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.ping().await?; - - Ok(()) -} - -pub async fn should_run_custom_command(_client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - // TODO find a good third party module to test - - Ok(()) -} - -pub async fn should_read_last_save(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let lastsave: Option = client.lastsave().await?; - assert!(lastsave.is_some()); - - Ok(()) -} - -pub async fn should_read_db_size(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - for idx in 0 .. 50 { - client - .custom(cmd!("SET"), vec![format!("foo-{}", idx), idx.to_string()]) - .await?; - } - - // this is tricky to assert b/c the dbsize command isnt linked to a specific server in the cluster, hence the loop - // above - let db_size: i64 = client.dbsize().await?; - assert!(db_size > 0); - - Ok(()) -} - -pub async fn should_start_bgsave(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let save_result: String = client.bgsave().await?; - assert_eq!(save_result, "Background saving started"); - - // need to ensure this finishes before it runs again or it'll return an error - sleep(Duration::from_millis(1000)).await; - Ok(()) -} - -pub async fn should_do_bgrewriteaof(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.bgrewriteaof().await?; - // not much we can assert here aside from the command not failing - - // need to ensure this finishes before it runs again or it'll return an error - sleep(Duration::from_millis(1000)).await; - Ok(()) -} diff --git a/tests/integration/sets/mod.rs b/tests/integration/sets/mod.rs deleted file mode 100644 index 55c4dd6f..00000000 --- a/tests/integration/sets/mod.rs +++ /dev/null @@ -1,247 +0,0 @@ -use fred::prelude::*; -use std::collections::HashSet; - -fn vec_to_set(data: Vec) -> HashSet { - let mut out = HashSet::with_capacity(data.len()); - for value in data.into_iter() { - out.insert(value); - } - out -} - -// #[cfg(feature = "index-map")] -// fn sets_eq(lhs: &IndexSet, rhs: &HashSet) -> bool { -// let lhs: HashSet = lhs.iter().map(|v| v.clone()).collect(); -// &lhs == rhs -// } - -fn sets_eq(lhs: &HashSet, rhs: &HashSet) -> bool { - lhs == rhs -} - -pub async fn should_sadd_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: i64 = client.sadd("foo", "a").await?; - assert_eq!(result, 1); - let result: i64 = client.sadd("foo", vec!["b", "c"]).await?; - assert_eq!(result, 2); - let result: i64 = client.sadd("foo", vec!["c", "d"]).await?; - assert_eq!(result, 1); - - Ok(()) -} - -pub async fn should_scard_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: i64 = client.scard("foo").await?; - assert_eq!(result, 0); - - let result: i64 = client.sadd("foo", vec!["1", "2", "3", "4", "5"]).await?; - assert_eq!(result, 5); - let result: i64 = client.scard("foo").await?; - assert_eq!(result, 5); - - Ok(()) -} - -pub async fn should_sdiff_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.sadd("foo{1}", vec![1, 2, 3, 4, 5, 6]).await?; - client.sadd("bar{1}", vec![3, 4, 5, 6, 7, 8]).await?; - let result: HashSet = client.sdiff(vec!["foo{1}", "bar{1}"]).await?; - - assert!(sets_eq(&result, &vec_to_set(vec!["1".into(), "2".into()]))); - Ok(()) -} - -pub async fn should_sdiffstore_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.sadd("foo{1}", vec![1, 2, 3, 4, 5, 6]).await?; - client.sadd("bar{1}", vec![3, 4, 5, 6, 7, 8]).await?; - let result: i64 = client.sdiffstore("baz{1}", vec!["foo{1}", "bar{1}"]).await?; - assert_eq!(result, 2); - let result: HashSet = client.smembers("baz{1}").await?; - - assert!(sets_eq(&result, &vec_to_set(vec!["1".into(), "2".into()]))); - Ok(()) -} - -pub async fn should_sinter_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.sadd("foo{1}", vec![1, 2, 3, 4, 5, 6]).await?; - client.sadd("bar{1}", vec![3, 4, 5, 6, 7, 8]).await?; - let result: HashSet = client.sinter(vec!["foo{1}", "bar{1}"]).await?; - - assert!(sets_eq( - &result, - &vec_to_set(vec!["3".into(), "4".into(), "5".into(), "6".into()]) - )); - - Ok(()) -} - -pub async fn should_sinterstore_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.sadd("foo{1}", vec![1, 2, 3, 4, 5, 6]).await?; - client.sadd("bar{1}", vec![3, 4, 5, 6, 7, 8]).await?; - let result: i64 = client.sinterstore("baz{1}", vec!["foo{1}", "bar{1}"]).await?; - assert_eq!(result, 4); - let result: HashSet = client.smembers("baz{1}").await?; - - assert!(sets_eq( - &result, - &vec_to_set(vec!["3".into(), "4".into(), "5".into(), "6".into()]) - )); - - Ok(()) -} - -pub async fn should_check_sismember(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.sadd("foo", vec![1, 2, 3, 4, 5, 6]).await?; - - let result: bool = client.sismember("foo", 1).await?; - assert!(result); - let result: bool = client.sismember("foo", 7).await?; - assert!(!result); - - Ok(()) -} - -pub async fn should_check_smismember(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.sadd("foo", vec![1, 2, 3, 4, 5, 6]).await?; - - let result: Vec = client.smismember("foo", vec![1, 2, 7]).await?; - assert!(result[0]); - assert!(result[1]); - assert!(!result[2]); - - let result: bool = client.sismember("foo", 7).await?; - assert!(!result); - - Ok(()) -} - -pub async fn should_read_smembers(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.sadd("foo", vec![1, 2, 3, 4, 5, 6]).await?; - let result: HashSet = client.smembers("foo").await?; - assert!(sets_eq( - &result, - &vec_to_set(vec![ - "1".into(), - "2".into(), - "3".into(), - "4".into(), - "5".into(), - "6".into() - ]) - )); - - Ok(()) -} - -pub async fn should_smove_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.sadd("foo{1}", vec![1, 2, 3, 4, 5, 6]).await?; - client.sadd("bar{1}", 5).await?; - - let result: i64 = client.smove("foo{1}", "bar{1}", 7).await?; - assert_eq!(result, 0); - let result: i64 = client.smove("foo{1}", "bar{1}", 5).await?; - assert_eq!(result, 1); - let result: i64 = client.smove("foo{1}", "bar{1}", 1).await?; - assert_eq!(result, 1); - - let foo: HashSet = client.smembers("foo{1}").await?; - let bar: HashSet = client.smembers("bar{1}").await?; - assert!(sets_eq( - &foo, - &vec_to_set(vec!["2".into(), "3".into(), "4".into(), "6".into()]) - )); - assert!(sets_eq(&bar, &vec_to_set(vec!["5".into(), "1".into()]))); - - Ok(()) -} - -pub async fn should_spop_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = vec_to_set(vec!["1".into(), "2".into(), "3".into()]); - client.sadd("foo", vec![1, 2, 3]).await?; - - let result = client.spop("foo", None).await?; - assert!(expected.contains(&result)); - - let result: Vec = client.spop("foo", Some(3)).await?; - for value in result.into_iter() { - assert!(expected.contains(&value)); - } - - Ok(()) -} - -pub async fn should_get_random_member(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = vec_to_set(vec!["1".into(), "2".into(), "3".into()]); - client.sadd("foo", vec![1, 2, 3]).await?; - - let result = client.srandmember("foo", None).await?; - assert!(expected.contains(&result)); - let result: Vec = client.srandmember("foo", Some(3)).await?; - for value in result.into_iter() { - assert!(expected.contains(&value)); - } - - Ok(()) -} - -pub async fn should_remove_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: i64 = client.srem("foo", 1).await?; - assert_eq!(result, 0); - - client.sadd("foo", vec![1, 2, 3, 4, 5, 6]).await?; - let result: i64 = client.srem("foo", 1).await?; - assert_eq!(result, 1); - let result: i64 = client.srem("foo", vec![2, 3, 4, 7]).await?; - assert_eq!(result, 3); - - let result: HashSet = client.smembers("foo").await?; - assert!(sets_eq(&result, &vec_to_set(vec!["5".into(), "6".into()]))); - - Ok(()) -} - -pub async fn should_sunion_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.sadd("foo{1}", vec![1, 2, 3, 4, 5, 6]).await?; - client.sadd("bar{1}", vec![3, 4, 5, 6, 7, 8]).await?; - let result: HashSet = client.sunion(vec!["foo{1}", "bar{1}"]).await?; - - assert!(sets_eq( - &result, - &vec_to_set(vec![ - "1".into(), - "2".into(), - "3".into(), - "4".into(), - "5".into(), - "6".into(), - "7".into(), - "8".into() - ]) - )); - - Ok(()) -} - -pub async fn should_sunionstore_elements(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.sadd("foo{1}", vec![1, 2, 3, 4, 5, 6]).await?; - client.sadd("bar{1}", vec![3, 4, 5, 6, 7, 8]).await?; - let result: i64 = client.sunionstore("baz{1}", vec!["foo{1}", "bar{1}"]).await?; - assert_eq!(result, 8); - let result: HashSet = client.smembers("baz{1}").await?; - - assert!(sets_eq( - &result, - &vec_to_set(vec![ - "1".into(), - "2".into(), - "3".into(), - "4".into(), - "5".into(), - "6".into(), - "7".into(), - "8".into() - ]) - )); - - Ok(()) -} diff --git a/tests/integration/slowlog/mod.rs b/tests/integration/slowlog/mod.rs deleted file mode 100644 index 4195fa3e..00000000 --- a/tests/integration/slowlog/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -use fred::{prelude::*, types::SlowlogEntry}; - -pub async fn should_read_slowlog_length(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.slowlog_length().await?; - // cant assert much here since the tests run in any order, and the call to reset the slowlog might run just before - // this - - Ok(()) -} - -pub async fn should_read_slowlog_entries(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let entries: Vec = client.slowlog_get(Some(10)).await?; - - for entry in entries.into_iter() { - assert!(!entry.duration.is_zero()); - assert!(entry.name.is_some()); - } - - Ok(()) -} - -pub async fn should_reset_slowlog(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.slowlog_reset().await?; - let len: i64 = client.slowlog_length().await?; - // the slowlog length call might show up here - assert!(len < 2); - - Ok(()) -} diff --git a/tests/integration/sorted_sets/mod.rs b/tests/integration/sorted_sets/mod.rs deleted file mode 100644 index 2601abf1..00000000 --- a/tests/integration/sorted_sets/mod.rs +++ /dev/null @@ -1,801 +0,0 @@ -use float_cmp::approx_eq; -use fred::{ - prelude::*, - types::{Ordering, ZRange, ZRangeBound, ZRangeKind, ZSort}, -}; -use std::{cmp::Ordering as CmpOrdering, convert::TryInto, time::Duration}; -use tokio::time::sleep; - -const COUNT: i64 = 10; - -fn f64_cmp(lhs: f64, rhs: f64) -> CmpOrdering { - if approx_eq!(f64, lhs, rhs, ulps = 2) { - CmpOrdering::Equal - } else if lhs < rhs { - CmpOrdering::Less - } else { - CmpOrdering::Greater - } -} - -async fn create_lex_data(client: &RedisClient, key: &str) -> Result, RedisError> { - let values: Vec<(f64, String)> = "abcdefghijklmnopqrstuvwxyz" - .chars() - .map(|c| (0.0, c.to_string())) - .collect(); - - client.zadd(key, None, None, false, false, values.clone()).await?; - Ok(values.into_iter().map(|(f, v)| (f, v.into())).collect()) -} - -async fn create_count_data(client: &RedisClient, key: &str) -> Result, RedisError> { - let values: Vec<(f64, RedisValue)> = (0 .. COUNT).map(|idx| (idx as f64, idx.to_string().into())).collect(); - - client.zadd(key, None, None, false, false, values.clone()).await?; - Ok(values) -} - -pub async fn should_bzpopmin(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let publisher_client = client.clone_new(); - publisher_client.connect(); - publisher_client.wait_for_connect().await?; - - let jh = tokio::task::spawn(async move { - for idx in 0 .. COUNT { - let result: (String, i64, f64) = client.bzpopmin("foo", 60.0).await?; - assert_eq!(result, ("foo".into(), idx, idx as f64)); - } - - Ok::<(), RedisError>(()) - }); - - for idx in 0 .. COUNT { - let result: i64 = publisher_client - .zadd("foo", None, None, false, false, (idx as f64, idx)) - .await?; - assert_eq!(result, 1); - } - - let _ = jh.await?; - Ok(()) -} - -pub async fn should_bzpopmax(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let publisher_client = client.clone_new(); - publisher_client.connect(); - publisher_client.wait_for_connect().await?; - - let jh = tokio::task::spawn(async move { - for idx in 0 .. COUNT { - let result: (String, i64, f64) = client.bzpopmax("foo", 60.0).await?; - assert_eq!(result, ("foo".into(), idx, idx as f64)); - } - - Ok::<(), RedisError>(()) - }); - - for idx in 0 .. COUNT { - sleep(Duration::from_millis(50)).await; - - let result: i64 = publisher_client - .zadd("foo", None, None, false, false, (idx as f64, idx)) - .await?; - assert_eq!(result, 1); - } - - let _ = jh.await?; - Ok(()) -} - -pub async fn should_zadd_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: i64 = client - .zadd("foo", None, None, false, false, vec![(0.0, 0), (1.0, 1)]) - .await?; - assert_eq!(result, 2); - - for idx in 2 .. COUNT { - let value: i64 = client.zadd("foo", None, None, false, false, (idx as f64, idx)).await?; - assert_eq!(value, 1); - } - - let result: i64 = client.zcard("foo").await?; - assert_eq!(result, COUNT); - - let result: f64 = client.zadd("foo", None, None, true, true, (0.1_f64, 0_i64)).await?; - assert!(approx_eq!(f64, result, 0.1, ulps = 2)); - - let result: i64 = client - .zadd( - "foo", - Some(SetOptions::NX), - None, - true, - false, - ((COUNT + 1) as f64, COUNT + 1), - ) - .await?; - assert_eq!(result, 1); - let result: i64 = client - .zadd( - "foo", - Some(SetOptions::XX), - None, - true, - false, - ((COUNT + 2) as f64, COUNT + 2), - ) - .await?; - assert_eq!(result, 0); - - let result: i64 = client - .zadd( - "foo", - None, - Some(Ordering::GreaterThan), - true, - false, - (COUNT as f64, COUNT + 1), - ) - .await?; - assert_eq!(result, 0); - let result: i64 = client - .zadd( - "foo", - None, - Some(Ordering::LessThan), - true, - false, - ((COUNT + 2) as f64, COUNT + 1), - ) - .await?; - assert_eq!(result, 0); - let result: i64 = client - .zadd( - "foo", - None, - Some(Ordering::GreaterThan), - true, - false, - ((COUNT + 2) as f64, COUNT + 1), - ) - .await?; - assert_eq!(result, 1); - let result: i64 = client - .zadd( - "foo", - None, - Some(Ordering::LessThan), - true, - false, - ((COUNT + 1) as f64, COUNT + 1), - ) - .await?; - assert_eq!(result, 1); - - Ok(()) -} - -pub async fn should_zcard_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - for idx in 0 .. COUNT { - let values = vec![(idx as f64, idx), ((idx + COUNT) as f64, idx + COUNT)]; - let result: i64 = client.zadd("foo", None, None, false, false, values).await?; - assert_eq!(result, 2); - } - - let result: i64 = client.zcard("foo").await?; - assert_eq!(result, COUNT * 2); - let result: i64 = client.zcard("bar").await?; - assert_eq!(result, 0); - - Ok(()) -} - -pub async fn should_zcount_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - for idx in 0 .. COUNT { - let values = vec![(idx as f64, idx), ((idx + COUNT) as f64, idx + COUNT)]; - let result: i64 = client.zadd("foo", None, None, false, false, values).await?; - assert_eq!(result, 2); - } - - let result: i64 = client.zcount("foo", 0.0, (COUNT * 2) as f64).await?; - assert_eq!(result, COUNT * 2); - let result: i64 = client.zcount("foo", 0.0, COUNT as f64 - 0.1).await?; - assert_eq!(result, COUNT); - let result: i64 = client.zcount("foo", -1.0, -0.1).await?; - assert_eq!(result, 0); - - Ok(()) -} - -pub async fn should_zdiff_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let mut expected: Vec<(f64, RedisValue)> = Vec::with_capacity(COUNT as usize); - for idx in 0 .. COUNT { - expected.push((idx as f64, idx.to_string().into())); - let result: i64 = client - .zadd("foo{1}", None, None, false, false, (idx as f64, idx)) - .await?; - assert_eq!(result, 1); - } - - let result: Vec = client.zdiff(vec!["foo{1}", "bar{1}"], false).await?; - let _expected: Vec = expected.iter().map(|(_, v)| v.clone()).collect(); - assert_eq!(result, _expected); - - client - .zadd( - "bar{1}", - None, - None, - false, - false, - expected[0 .. expected.len() - 1].to_vec(), - ) - .await?; - let result: RedisValue = client.zdiff(vec!["foo{1}", "bar{1}"], true).await?; - let expected: Vec<(RedisValue, f64)> = expected.into_iter().map(|(s, v)| (v, s)).collect(); - assert_eq!( - result.into_zset_result().unwrap(), - expected[expected.len() - 1 ..].to_vec() - ); - - Ok(()) -} - -pub async fn should_zdiffstore_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let mut expected: Vec<(f64, RedisValue)> = Vec::with_capacity(COUNT as usize); - for idx in 0 .. COUNT { - expected.push((idx as f64, idx.to_string().into())); - let result: i64 = client - .zadd("foo{1}", None, None, false, false, (idx as f64, idx)) - .await?; - assert_eq!(result, 1); - } - - let result: i64 = client.zdiffstore("baz{1}", vec!["foo{1}", "bar{1}"]).await?; - assert_eq!(result, COUNT); - - client - .zadd( - "bar{1}", - None, - None, - false, - false, - expected[0 .. expected.len() - 1].to_vec(), - ) - .await?; - let result: i64 = client.zdiffstore("baz{1}", vec!["foo{1}", "bar{1}"]).await?; - assert_eq!(result, 1); - - Ok(()) -} - -pub async fn should_zincrby_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: f64 = client.zincrby("foo", 1.0, "a").await?; - assert_eq!(result, 1.0); - let result: f64 = client.zincrby("foo", 2.5, "a").await?; - assert_eq!(result, 3.5); - let result: f64 = client.zincrby("foo", 1.2, "b").await?; - assert_eq!(result, 1.2); - - Ok(()) -} - -pub async fn should_zinter_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let mut expected: Vec<(f64, RedisValue)> = Vec::with_capacity(COUNT as usize); - for idx in 0 .. COUNT { - expected.push((idx as f64, idx.to_string().into())); - let result: i64 = client - .zadd("foo{1}", None, None, false, false, (idx as f64, idx)) - .await?; - assert_eq!(result, 1); - } - - let result: Vec = client.zinter(vec!["foo{1}", "bar{1}"], None, None, false).await?; - assert!(result.is_empty()); - - client - .zadd( - "bar{1}", - None, - None, - false, - false, - expected[0 .. expected.len() - 1].to_vec(), - ) - .await?; - let result: RedisValue = client.zinter(vec!["foo{1}", "bar{1}"], None, None, true).await?; - // scores are added together with a weight of 1 in this example - let mut expected: Vec<(RedisValue, f64)> = expected.into_iter().map(|(s, v)| (v, s * 2.0)).collect(); - // zinter returns results in descending order based on score - expected.reverse(); - - assert_eq!( - result.into_zset_result().unwrap(), - expected[1 .. expected.len()].to_vec() - ); - Ok(()) -} - -pub async fn should_zinterstore_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let mut expected: Vec<(f64, RedisValue)> = Vec::with_capacity(COUNT as usize); - for idx in 0 .. COUNT { - expected.push((idx as f64, idx.to_string().into())); - let result: i64 = client - .zadd("foo{1}", None, None, false, false, (idx as f64, idx)) - .await?; - assert_eq!(result, 1); - } - - let result: i64 = client - .zinterstore("baz{1}", vec!["foo{1}", "bar{1}"], None, None) - .await?; - assert_eq!(result, 0); - - client - .zadd( - "bar{1}", - None, - None, - false, - false, - expected[0 .. expected.len() - 1].to_vec(), - ) - .await?; - let result: i64 = client - .zinterstore("baz{1}", vec!["foo{1}", "bar{1}"], None, None) - .await?; - assert_eq!(result, COUNT - 1); - - Ok(()) -} - -pub async fn should_zlexcount(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_lex_data(&client, "foo").await?; - - let result: i64 = client.zlexcount("foo", "-", "+").await?; - assert_eq!(result, 26); - let result: i64 = client.zlexcount("foo", "a", "b").await?; - assert_eq!(result, 2); - let result: i64 = client.zlexcount("foo", "a", "(b").await?; - assert_eq!(result, 1); - - Ok(()) -} - -pub async fn should_zpopmax(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_count_data(&client, "foo").await?; - - for idx in 0 .. COUNT { - let result: RedisValue = client.zpopmax("foo", None).await?; - let (member, score) = result.into_zset_result().unwrap().pop().unwrap(); - assert_eq!(score, (COUNT - idx - 1) as f64); - assert_eq!(member, (COUNT - idx - 1).to_string().into()); - } - let result: i64 = client.zcard("foo").await?; - assert_eq!(result, 0); - - Ok(()) -} - -pub async fn should_zpopmin(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_count_data(&client, "foo").await?; - - for idx in 0 .. COUNT { - let result: RedisValue = client.zpopmin("foo", None).await?; - let (member, score) = result.into_zset_result().unwrap().pop().unwrap(); - assert_eq!(score, idx as f64); - assert_eq!(member, idx.to_string().into()); - } - let result: i64 = client.zcard("foo").await?; - assert_eq!(result, 0); - - Ok(()) -} - -pub async fn should_zrandmember(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_count_data(&client, "foo").await?; - - for _ in 0 .. COUNT * 2 { - let result: RedisValue = client.zrandmember("foo", Some((1, true))).await?; - let (member, score) = result.into_zset_result().unwrap().pop().unwrap(); - assert!(score >= 0.0 && score < COUNT as f64); - assert_eq!(member.into_string().unwrap(), score.to_string()); - } - - let result: RedisValue = client.zrandmember("foo", Some((COUNT, true))).await?; - let result = result.into_zset_result().unwrap(); - for (member, score) in result.into_iter() { - assert!(score >= 0.0 && score < COUNT as f64); - assert_eq!(member.into_string().unwrap(), score.to_string()) - } - - Ok(()) -} - -pub async fn should_zrangestore_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_count_data(&client, "foo{1}").await?; - - let result: i64 = client - .zrangestore("bar{1}", "foo{1}", 0, COUNT, None, false, None) - .await?; - assert_eq!(result, COUNT); - let result: i64 = client.zcard("bar{1}").await?; - assert_eq!(result, COUNT); - - Ok(()) -} - -pub async fn should_zrangebylex(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = create_lex_data(&client, "foo").await?; - let expected_values: Vec = expected.iter().map(|(_, v)| v.clone()).collect(); - - let old_result: RedisValue = client.zrangebylex("foo", "-", "+", None).await?; - let new_result = client - .zrange("foo", "-", "+", Some(ZSort::ByLex), false, None, false) - .await?; - assert_eq!(old_result, new_result); - assert_eq!(old_result.into_array(), expected_values); - - let old_result: RedisValue = client.zrangebylex("foo", "a", "[c", None).await?; - let new_result = client - .zrange("foo", "a", "[c", Some(ZSort::ByLex), false, None, false) - .await?; - assert_eq!(old_result, new_result); - assert_eq!(old_result.into_array(), expected_values[0 .. 3]); - - Ok(()) -} - -pub async fn should_zrevrangebylex(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = create_lex_data(&client, "foo").await?; - let mut expected_values: Vec = expected.iter().map(|(_, v)| v.clone()).collect(); - expected_values.reverse(); - - let old_result: RedisValue = client.zrevrangebylex("foo", "+", "-", None).await?; - let new_result = client - .zrange("foo", "+", "-", Some(ZSort::ByLex), true, None, false) - .await?; - assert_eq!(old_result, new_result); - assert_eq!(old_result.into_array(), expected_values); - - let old_result: RedisValue = client.zrevrangebylex("foo", "c", "[a", None).await?; - let new_result = client - .zrange("foo", "[c", "a", Some(ZSort::ByLex), true, None, false) - .await?; - assert_eq!(old_result, new_result); - assert_eq!(old_result.into_array(), expected_values[expected_values.len() - 3 ..]); - - Ok(()) -} - -pub async fn should_zrangebyscore(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = create_count_data(&client, "foo").await?; - let expected_values: Vec = expected.iter().map(|(_, v)| v.clone()).collect(); - - let old_result: RedisValue = client.zrangebyscore("foo", "-inf", "+inf", false, None).await?; - let new_result = client - .zrange("foo", "-inf", "+inf", Some(ZSort::ByScore), false, None, false) - .await?; - assert_eq!(old_result, new_result); - assert_eq!(old_result.into_array(), expected_values); - - let old_result: RedisValue = client - .zrangebyscore("foo", (COUNT / 2) as f64, COUNT as f64, false, None) - .await?; - let new_result = client - .zrange( - "foo", - (COUNT / 2) as f64, - COUNT as f64, - Some(ZSort::ByScore), - false, - None, - false, - ) - .await?; - assert_eq!(old_result, new_result); - assert_eq!(old_result.into_array(), expected_values[(COUNT / 2) as usize ..]); - - let lower = ZRange { - kind: ZRangeKind::Inclusive, - range: ((COUNT / 2) as f64).try_into()?, - }; - let upper = ZRange { - kind: ZRangeKind::Inclusive, - range: (COUNT as f64).try_into()?, - }; - let old_result: RedisValue = client.zrangebyscore("foo", &lower, &upper, false, None).await?; - let new_result = client - .zrange("foo", &lower, &upper, Some(ZSort::ByScore), false, None, false) - .await?; - assert_eq!(old_result, new_result); - assert_eq!(old_result.into_array(), expected_values[(COUNT / 2) as usize ..]); - - Ok(()) -} - -pub async fn should_zrevrangebyscore(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = create_count_data(&client, "foo").await?; - let mut expected_values: Vec = expected.iter().map(|(_, v)| v.clone()).collect(); - expected_values.reverse(); - - let old_result: RedisValue = client.zrevrangebyscore("foo", "+inf", "-inf", false, None).await?; - let new_result = client - .zrange("foo", "+inf", "-inf", Some(ZSort::ByScore), true, None, false) - .await?; - assert_eq!(old_result, new_result); - assert_eq!(old_result.into_array(), expected_values); - - let old_result: RedisValue = client - .zrevrangebyscore("foo", COUNT as f64, (COUNT / 2) as f64, false, None) - .await?; - let new_result = client - .zrange( - "foo", - COUNT as f64, - (COUNT / 2) as f64, - Some(ZSort::ByScore), - true, - None, - false, - ) - .await?; - assert_eq!(old_result, new_result); - assert_eq!(old_result.into_array(), expected_values[0 .. (COUNT / 2) as usize]); - - let lower = ZRange { - kind: ZRangeKind::Inclusive, - range: ((COUNT / 2) as f64).try_into()?, - }; - let upper = ZRange { - kind: ZRangeKind::Inclusive, - range: (COUNT as f64).try_into()?, - }; - let old_result: RedisValue = client.zrevrangebyscore("foo", &upper, &lower, false, None).await?; - let new_result = client - .zrange("foo", &upper, &lower, Some(ZSort::ByScore), true, None, false) - .await?; - assert_eq!(old_result, new_result); - assert_eq!(old_result.into_array(), expected_values[0 .. (COUNT / 2) as usize]); - - Ok(()) -} - -pub async fn should_zrank_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_count_data(&client, "foo").await?; - - for idx in 0 .. COUNT { - let result: i64 = client.zrank("foo", idx).await?; - assert_eq!(result, idx); - } - - let result: Option = client.zrank("foo", COUNT + 1).await?; - assert!(result.is_none()); - - Ok(()) -} - -pub async fn should_zrem_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_count_data(&client, "foo").await?; - - let result: i64 = client.zrem("foo", COUNT + 1).await?; - assert_eq!(result, 0); - - for idx in 0 .. COUNT { - let result: i64 = client.zrem("foo", idx).await?; - assert_eq!(result, 1); - let result: i64 = client.zcard("foo").await?; - assert_eq!(result, COUNT - (idx + 1)); - } - - let result: i64 = client.zrem("foo", 0).await?; - assert_eq!(result, 0); - let result: i64 = client.zcard("foo").await?; - assert_eq!(result, 0); - - Ok(()) -} - -pub async fn should_zremrangebylex(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = create_lex_data(&client, "foo").await?; - let result: usize = client.zremrangebylex("foo", "-", "+").await?; - assert_eq!(result, expected.len()); - let result: i64 = client.zcard("foo").await?; - assert_eq!(result, 0); - - let _ = create_lex_data(&client, "foo").await?; - for (_, value) in expected.iter() { - let value_str = value.as_str().unwrap().to_string(); - - let result: i64 = client.zremrangebylex("foo", &value_str, &value_str).await?; - assert_eq!(result, 1); - } - let result: usize = client.zcard("foo").await?; - assert_eq!(result, 0); - - Ok(()) -} - -pub async fn should_zremrangebyrank(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = create_count_data(&client, "foo").await?; - let result: usize = client.zremrangebyrank("foo", 0, COUNT).await?; - assert_eq!(result, expected.len()); - let result: i64 = client.zcard("foo").await?; - assert_eq!(result, 0); - - let _ = create_count_data(&client, "foo").await?; - for _ in 0 .. COUNT { - // this modifies the set so the idx cant change - let result: usize = client.zremrangebyrank("foo", 0, 0).await?; - assert_eq!(result, 1); - } - let result: usize = client.zcard("foo").await?; - assert_eq!(result, 0); - - Ok(()) -} - -pub async fn should_zremrangebyscore(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected = create_count_data(&client, "foo").await?; - let result: usize = client.zremrangebyscore("foo", 0 as f64, COUNT as f64).await?; - assert_eq!(result, expected.len()); - let result: i64 = client.zcard("foo").await?; - assert_eq!(result, 0); - - let _ = create_count_data(&client, "foo").await?; - for idx in 0 .. COUNT { - let result: usize = client.zremrangebyscore("foo", idx as f64, idx as f64).await?; - assert_eq!(result, 1); - } - let result: usize = client.zcard("foo").await?; - assert_eq!(result, 0); - - Ok(()) -} - -pub async fn should_zrevrank_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_count_data(&client, "foo").await?; - - let result: Option = client.zrevrank("foo", COUNT + 1).await?; - assert!(result.is_none()); - - for idx in 0 .. COUNT { - let result: i64 = client.zrevrank("foo", idx).await?; - assert_eq!(result, COUNT - (idx + 1)); - } - - Ok(()) -} - -pub async fn should_zscore_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let _ = create_count_data(&client, "foo").await?; - - for idx in 0 .. COUNT { - let result: f64 = client.zscore("foo", idx).await?; - assert_eq!(result, idx as f64); - } - - let result: Option = client.zscore("foo", COUNT + 1).await?; - assert!(result.is_none()); - - Ok(()) -} - -pub async fn should_zunion_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let mut expected: Vec<(f64, RedisValue)> = Vec::with_capacity(COUNT as usize); - for idx in 0 .. COUNT { - expected.push((idx as f64, idx.to_string().into())); - let result: i64 = client - .zadd("foo{1}", None, None, false, false, (idx as f64, idx)) - .await?; - assert_eq!(result, 1); - } - - let result: RedisValue = client.zunion(vec!["foo{1}", "bar{1}"], None, None, false).await?; - let _expected: Vec = expected.iter().map(|(_, v)| v.clone()).collect(); - assert_eq!(result.into_array(), _expected); - - client - .zadd( - "bar{1}", - None, - None, - false, - false, - expected[0 .. expected.len() - 1].to_vec(), - ) - .await?; - let result: RedisValue = client.zunion(vec!["foo{1}", "bar{1}"], None, None, true).await?; - // scores are added together with a weight of 1 in this example - let mut _expected: Vec<(RedisValue, f64)> = expected[0 .. expected.len() - 1] - .iter() - .map(|(s, v)| (v.clone(), s * 2.0)) - .collect(); - - let (score, value) = expected.last().unwrap().clone(); - _expected.push((value, score)); - - // zinter returns results in descending order based on score - _expected.sort_by(|(_, a), (_, b)| f64_cmp(*b, *a)); - - assert_eq!(result.into_zset_result().unwrap(), _expected.to_vec()); - Ok(()) -} - -pub async fn should_zunionstore_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let mut expected: Vec<(f64, RedisValue)> = Vec::with_capacity(COUNT as usize); - for idx in 0 .. COUNT { - expected.push((idx as f64, idx.to_string().into())); - let result: i64 = client - .zadd("foo{1}", None, None, false, false, (idx as f64, idx)) - .await?; - assert_eq!(result, 1); - } - - let result: i64 = client - .zunionstore("baz{1}", vec!["foo{1}", "bar{1}"], None, None) - .await?; - assert_eq!(result, COUNT); - - client - .zadd( - "bar{1}", - None, - None, - false, - false, - expected[0 .. expected.len() - 1].to_vec(), - ) - .await?; - let result: i64 = client - .zunionstore("baz{1}", vec!["foo{1}", "bar{1}"], None, None) - .await?; - assert_eq!(result, COUNT); - - Ok(()) -} - -pub async fn should_zmscore_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - for idx in 0 .. COUNT { - client.zadd("foo", None, None, false, false, (idx as f64, idx)).await?; - } - - let result: Vec = client.zmscore("foo", vec![0, 1]).await?; - assert_eq!(result, vec![0.0, 1.0]); - let result: [Option; 1] = client.zmscore("foo", vec![11]).await?; - assert!(result[0].is_none()); - - Ok(()) -} - -pub async fn should_zrangebyscore_neg_infinity(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client - .zadd("foo", None, None, false, false, vec![ - (-10.0, "a"), - (-5.0, "b"), - (0.0, "c"), - (2.0, "d"), - (4.0, "e"), - ]) - .await?; - - let vals: Vec = client.zrangebyscore("foo", "-inf", 0, false, None).await?; - assert_eq!(vals, ["a", "b", "c"]); - - let vals: Vec = client - .zrangebyscore( - "foo", - ZRange { - kind: ZRangeKind::Inclusive, - range: ZRangeBound::NegInfiniteScore, - }, - 0, - false, - None, - ) - .await?; - assert_eq!(vals, vec!["a", "b", "c"]); - - Ok(()) -} diff --git a/tests/integration/streams/mod.rs b/tests/integration/streams/mod.rs deleted file mode 100644 index 4c111884..00000000 --- a/tests/integration/streams/mod.rs +++ /dev/null @@ -1,703 +0,0 @@ -use fred::{ - cmd, - prelude::*, - types::{XCapKind, XCapTrim, XReadResponse, XReadValue, XID}, -}; -use std::{collections::HashMap, hash::Hash, time::Duration}; -use tokio::time::sleep; - -type FakeExpectedValues = Vec>>; - -async fn create_fake_group_and_stream(client: &RedisClient, key: &str) -> Result<(), RedisError> { - client.xgroup_create(key, "group1", "$", true).await -} - -async fn add_stream_entries( - client: &RedisClient, - key: &str, - count: usize, -) -> Result<(Vec, FakeExpectedValues), RedisError> { - let mut ids = Vec::with_capacity(count); - let mut expected = Vec::with_capacity(count); - for idx in 0 .. count { - let id: String = client.xadd(key, false, None, "*", ("count", idx)).await?; - ids.push(id.clone()); - - let mut outer = HashMap::with_capacity(1); - let mut inner = HashMap::with_capacity(1); - inner.insert("count".into(), idx); - outer.insert(id, inner); - expected.push(outer); - } - - Ok((ids, expected)) -} - -fn has_expected_value(expected: &FakeExpectedValues, actual: &FakeExpectedValues) -> bool { - actual.iter().enumerate().fold(true, |b, (i, v)| b && v == &expected[i]) -} - -pub async fn should_xinfo_consumers(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: Result<(), RedisError> = client.xinfo_consumers("foo{1}", "group1").await; - assert!(result.is_err()); - - create_fake_group_and_stream(&client, "foo{1}").await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer1").await?; - let consumers: Vec> = client.xinfo_consumers("foo{1}", "group1").await?; - assert_eq!(consumers.len(), 1); - assert_eq!(consumers[0].get("name"), Some(&"consumer1".to_owned())); - - client.xgroup_createconsumer("foo{1}", "group1", "consumer2").await?; - let consumers: Vec> = client.xinfo_consumers("foo{1}", "group1").await?; - assert_eq!(consumers.len(), 2); - assert_eq!(consumers[0].get("name"), Some(&"consumer1".to_owned())); - assert_eq!(consumers[1].get("name"), Some(&"consumer2".to_owned())); - - Ok(()) -} - -pub async fn should_xinfo_groups(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: Result<(), RedisError> = client.xinfo_groups("foo{1}").await; - assert!(result.is_err()); - - create_fake_group_and_stream(&client, "foo{1}").await?; - let result: Vec> = client.xinfo_groups("foo{1}").await?; - assert_eq!(result.len(), 1); - assert_eq!(result[0].get("name"), Some(&"group1".into())); - - client.xgroup_create("foo{1}", "group2", "$", true).await?; - let result: Vec> = client.xinfo_groups("foo{1}").await?; - assert_eq!(result.len(), 2); - assert_eq!(result[0].get("name"), Some(&"group1".into())); - assert_eq!(result[1].get("name"), Some(&"group2".into())); - - Ok(()) -} - -pub async fn should_xinfo_streams(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: Result<(), RedisError> = client.xinfo_stream("foo{1}", true, None).await; - assert!(result.is_err()); - - create_fake_group_and_stream(&client, "foo{1}").await?; - let mut result: HashMap = client.xinfo_stream("foo{1}", true, None).await?; - assert!(result.len() >= 6); - assert_eq!(result.get("length"), Some(&RedisValue::Integer(0))); - - let groups: [HashMap; 1] = result.remove("groups").unwrap().convert()?; - assert_eq!(groups[0].get("name"), Some(&RedisValue::from("group1"))); - - Ok(()) -} - -pub async fn should_xadd_auto_id_to_a_stream(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: String = client.xadd("foo{1}", false, None, "*", ("a", "b")).await?; - assert!(!result.is_empty()); - - let len: usize = client.xlen("foo{1}").await?; - assert_eq!(len, 1); - Ok(()) -} - -pub async fn should_xadd_manual_id_to_a_stream(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: String = client.xadd("foo{1}", false, None, "1-0", ("a", "b")).await?; - assert_eq!(result, "1-0"); - - let len: usize = client.xlen("foo{1}").await?; - assert_eq!(len, 1); - Ok(()) -} - -pub async fn should_xadd_with_cap_to_a_stream(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client - .xadd("foo{1}", false, ("MAXLEN", "=", 1), "*", ("a", "b")) - .await?; - - let len: usize = client.xlen("foo{1}").await?; - assert_eq!(len, 1); - Ok(()) -} - -pub async fn should_xadd_nomkstream_to_a_stream(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: Option = client.xadd("foo{1}", true, None, "*", ("a", "b")).await?; - assert!(result.is_none()); - - create_fake_group_and_stream(&client, "foo{1}").await?; - client.xadd("foo{1}", true, None, "*", ("a", "b")).await?; - let len: usize = client.xlen("foo{1}").await?; - assert_eq!(len, 1); - Ok(()) -} - -pub async fn should_xtrim_a_stream_approx_cap(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let _ = add_stream_entries(&client, "foo{1}", 3).await?; - - let deleted: usize = client.xtrim("foo{1}", ("MAXLEN", "~", 1)).await?; - assert!(deleted < 3); - let len: usize = client.xlen("foo{1}").await?; - assert_eq!(len, 3 - deleted); - - client.custom(cmd!("DEL"), vec!["foo{1}"]).await?; - create_fake_group_and_stream(&client, "foo{1}").await?; - let _ = add_stream_entries(&client, "foo{1}", 3).await?; - let deleted: usize = client - .xtrim("foo{1}", (XCapKind::MaxLen, XCapTrim::AlmostExact, 1)) - .await?; - assert!(deleted < 3); - let len: usize = client.xlen("foo{1}").await?; - assert_eq!(len, 3 - deleted); - - Ok(()) -} - -pub async fn should_xtrim_a_stream_eq_cap(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let _ = add_stream_entries(&client, "foo{1}", 3).await?; - - let deleted: usize = client.xtrim("foo{1}", ("MAXLEN", "=", 1)).await?; - assert_eq!(deleted, 2); - let len: usize = client.xlen("foo{1}").await?; - assert_eq!(len, 1); - - client.custom(cmd!("DEL"), vec!["foo{1}"]).await?; - create_fake_group_and_stream(&client, "foo{1}").await?; - let _ = add_stream_entries(&client, "foo{1}", 3).await?; - let deleted: usize = client.xtrim("foo{1}", (XCapKind::MaxLen, XCapTrim::Exact, 1)).await?; - assert_eq!(deleted, 2); - let len: usize = client.xlen("foo{1}").await?; - assert_eq!(len, 1); - - Ok(()) -} - -pub async fn should_xdel_one_id_in_a_stream(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let (ids, _) = add_stream_entries(&client, "foo{1}", 2).await?; - - let deleted: usize = client.xdel("foo{1}", &ids[0]).await?; - assert_eq!(deleted, 1); - let len: usize = client.xlen("foo{1}").await?; - assert_eq!(len, 1); - Ok(()) -} - -pub async fn should_xdel_multiple_ids_in_a_stream(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let (ids, _) = add_stream_entries(&client, "foo{1}", 3).await?; - - let deleted: usize = client.xdel("foo{1}", ids[0 .. 2].to_vec()).await?; - assert_eq!(deleted, 2); - let len: usize = client.xlen("foo{1}").await?; - assert_eq!(len, 1); - Ok(()) -} - -pub async fn should_xrange_no_count(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let (_, expected) = add_stream_entries(&client, "foo{1}", 3).await?; - - let result: FakeExpectedValues = client.xrange("foo{1}", "-", "+", None).await?; - assert_eq!(result, expected); - Ok(()) -} - -pub async fn should_xrange_values_no_count(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let (ids, _) = add_stream_entries(&client, "foo{1}", 3).await?; - - let result: Vec> = client.xrange_values("foo{1}", "-", "+", None).await?; - let actual_ids: Vec = result.iter().map(|(id, _)| id.clone()).collect(); - assert_eq!(ids, actual_ids); - Ok(()) -} - -pub async fn should_xrevrange_values_no_count(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let (mut ids, _) = add_stream_entries(&client, "foo{1}", 3).await?; - ids.reverse(); - - let result: Vec> = client.xrevrange_values("foo{1}", "+", "-", None).await?; - let actual_ids: Vec = result.iter().map(|(id, _)| id.clone()).collect(); - assert_eq!(ids, actual_ids); - Ok(()) -} - -pub async fn should_xrange_with_count(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let (_, expected) = add_stream_entries(&client, "foo{1}", 3).await?; - - let result: FakeExpectedValues = client.xrange("foo{1}", "-", "+", Some(1)).await?; - assert!(has_expected_value(&expected, &result)); - Ok(()) -} - -pub async fn should_xrevrange_no_count(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let (_, mut expected) = add_stream_entries(&client, "foo{1}", 3).await?; - expected.reverse(); - - let result: FakeExpectedValues = client.xrevrange("foo{1}", "+", "-", None).await?; - assert_eq!(result, expected); - Ok(()) -} - -pub async fn should_xrevrange_with_count(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let (_, mut expected) = add_stream_entries(&client, "foo{1}", 3).await?; - expected.reverse(); - - let result: FakeExpectedValues = client.xrevrange("foo{1}", "-", "+", Some(1)).await?; - assert!(has_expected_value(&expected, &result)); - Ok(()) -} - -pub async fn should_run_xlen_on_stream(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let len: usize = client.xlen("foo{1}").await?; - assert_eq!(len, 0); - - let _ = add_stream_entries(&client, "foo{1}", 3).await?; - let len: usize = client.xlen("foo{1}").await?; - assert_eq!(len, 3); - Ok(()) -} - -pub async fn should_xread_map_one_key(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let _ = add_stream_entries(&client, "foo{1}", 3).await?; - - let result: XReadResponse = client.xread_map(None, None, "foo{1}", "0").await?; - - for (idx, (_, record)) in result.get("foo{1}").unwrap().iter().enumerate() { - let count = record.get("count").expect("Failed to read count"); - assert_eq!(*count, idx); - } - - Ok(()) -} - -pub async fn should_xread_one_key_count_1(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let (mut ids, mut expected) = add_stream_entries(&client, "foo{1}", 3).await?; - let _ = ids.pop().unwrap(); - let most_recent_expected = expected.pop().unwrap(); - let second_recent_id = ids.pop().unwrap(); - - let mut expected = HashMap::new(); - expected.insert("foo{1}".into(), vec![most_recent_expected]); - - let result: HashMap>>> = client - .xread::(Some(1), None, "foo{1}", second_recent_id) - .await? - .flatten_array_values(1) - .convert()?; - assert_eq!(result, expected); - - Ok(()) -} - -pub async fn should_xread_multiple_keys_count_2(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - create_fake_group_and_stream(&client, "bar{1}").await?; - let (foo_ids, foo_inner) = add_stream_entries(&client, "foo{1}", 3).await?; - let (bar_ids, bar_inner) = add_stream_entries(&client, "bar{1}", 3).await?; - - let mut expected = HashMap::new(); - expected.insert("foo{1}".into(), foo_inner[1 ..].to_vec()); - expected.insert("bar{1}".into(), bar_inner[1 ..].to_vec()); - - let ids: Vec = vec![foo_ids[0].as_str().into(), bar_ids[0].as_str().into()]; - let result: HashMap>>> = client - .xread::(Some(2), None, vec!["foo{1}", "bar{1}"], ids) - .await? - .flatten_array_values(1) - .convert()?; - assert_eq!(result, expected); - - Ok(()) -} - -pub async fn should_xread_with_blocking(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let expected_id = "123456789-0"; - create_fake_group_and_stream(&client, "foo{1}").await?; - - let mut expected = HashMap::new(); - let mut inner = HashMap::new(); - let mut fields = HashMap::new(); - fields.insert("count".into(), 100); - inner.insert(expected_id.into(), fields); - expected.insert("foo{1}".into(), vec![inner]); - - let add_client = client.clone_new(); - tokio::spawn(async move { - add_client.connect(); - add_client.wait_for_connect().await?; - sleep(Duration::from_millis(500)).await; - - add_client - .xadd("foo{1}", false, None, expected_id, ("count", 100)) - .await?; - add_client.quit().await?; - Ok::<(), RedisError>(()) - }); - - let result: HashMap>>> = client - .xread::(None, Some(5000), "foo{1}", XID::Max) - .await? - .flatten_array_values(1) - .convert()?; - assert_eq!(result, expected); - - Ok(()) -} - -pub async fn should_xgroup_create_no_mkstream(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let result: Result = client.xgroup_create("foo{1}", "group1", "$", false).await; - assert!(result.is_err()); - client.xadd("foo{1}", false, None, "*", ("count", 1)).await?; - client.xgroup_create("foo{1}", "group1", "$", false).await?; - - Ok(()) -} - -pub async fn should_xgroup_create_mkstream(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.xgroup_create("foo{1}", "group1", "$", true).await?; - let len: usize = client.xlen("foo{1}").await?; - assert_eq!(len, 0); - - Ok(()) -} - -pub async fn should_xgroup_createconsumer(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let len: usize = client.xgroup_createconsumer("foo{1}", "group1", "consumer1").await?; - assert_eq!(len, 1); - - let consumers: Vec> = client.xinfo_consumers("foo{1}", "group1").await?; - assert_eq!(consumers[0].get("name").unwrap(), &RedisValue::from("consumer1")); - assert_eq!(consumers[0].get("pending").unwrap(), &RedisValue::from(0)); - - Ok(()) -} - -pub async fn should_xgroup_delconsumer(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let len: usize = client.xgroup_createconsumer("foo{1}", "group1", "consumer1").await?; - assert_eq!(len, 1); - - let len: usize = client.xgroup_delconsumer("foo{1}", "group1", "consumer1").await?; - assert_eq!(len, 0); - - let consumers: Vec> = client.xinfo_consumers("foo{1}", "group1").await?; - assert!(consumers.is_empty()); - Ok(()) -} - -pub async fn should_xgroup_destroy(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let len: usize = client.xgroup_destroy("foo{1}", "group1").await?; - assert_eq!(len, 1); - - Ok(()) -} - -pub async fn should_xgroup_setid(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - client.xgroup_setid("foo{1}", "group1", "12345-0").await?; - - Ok(()) -} - -pub async fn should_xreadgroup_one_stream(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let _ = add_stream_entries(&client, "foo{1}", 3).await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer1").await?; - - let result: XReadResponse = client - .xreadgroup_map("group1", "consumer1", None, None, false, "foo{1}", ">") - .await?; - - assert_eq!(result.len(), 1); - for (idx, (_, record)) in result.get("foo{1}").unwrap().iter().enumerate() { - let value = record.get("count").expect("Failed to read count"); - assert_eq!(idx, *value); - } - - Ok(()) -} - -pub async fn should_xreadgroup_multiple_stream(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - create_fake_group_and_stream(&client, "bar{1}").await?; - let _ = add_stream_entries(&client, "foo{1}", 3).await?; - let _ = add_stream_entries(&client, "bar{1}", 1).await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer1").await?; - client.xgroup_createconsumer("bar{1}", "group1", "consumer1").await?; - - let result: XReadResponse = client - .xreadgroup_map( - "group1", - "consumer1", - None, - None, - false, - vec!["foo{1}", "bar{1}"], - vec![">", ">"], - ) - .await?; - - assert_eq!(result.len(), 2); - for (idx, (_, record)) in result.get("foo{1}").unwrap().iter().enumerate() { - let value = record.get("count").expect("Failed to read count"); - assert_eq!(idx, *value); - } - let bar_records = result.get("bar{1}").unwrap(); - assert_eq!(bar_records.len(), 1); - assert_eq!(*bar_records[0].1.get("count").unwrap(), 0); - - Ok(()) -} - -pub async fn should_xreadgroup_block(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer1").await?; - - let add_client = client.clone_new(); - tokio::spawn(async move { - add_client.connect(); - add_client.wait_for_connect().await?; - sleep(Duration::from_secs(1)).await; - - add_client.xadd("foo{1}", false, None, "*", ("count", 100)).await?; - add_client.quit().await?; - Ok::<_, RedisError>(()) - }); - - let mut result: XReadResponse = client - .xreadgroup_map("group1", "consumer1", None, Some(10_000), false, "foo{1}", ">") - .await?; - - assert_eq!(result.len(), 1); - let records = result.remove("foo{1}").unwrap(); - assert_eq!(records.len(), 1); - let count = records[0].1.get("count").unwrap(); - assert_eq!(*count, 100); - - Ok(()) -} - -pub async fn should_xack_one_id(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let _ = add_stream_entries(&client, "foo{1}", 1).await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer1").await?; - - let result: XReadResponse = client - .xreadgroup_map("group1", "consumer1", None, None, false, "foo{1}", ">") - .await?; - assert_eq!(result.len(), 1); - let records = result.get("foo{1}").unwrap(); - let id = records[0].0.clone(); - - let result: i64 = client.xack("foo{1}", "group1", id).await?; - assert_eq!(result, 1); - Ok(()) -} - -pub async fn should_xack_multiple_ids(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let _ = add_stream_entries(&client, "foo{1}", 3).await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer1").await?; - - let result: XReadResponse = client - .xreadgroup_map("group1", "consumer1", None, None, false, "foo{1}", ">") - .await?; - assert_eq!(result.len(), 1); - let records = result.get("foo{1}").unwrap(); - let ids: Vec = records.iter().map(|(id, _)| id.clone()).collect(); - - let result: i64 = client.xack("foo{1}", "group1", ids).await?; - assert_eq!(result, 3); - Ok(()) -} - -pub async fn should_xclaim_one_id(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let _ = add_stream_entries(&client, "foo{1}", 3).await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer1").await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer2").await?; - - let mut result: XReadResponse = client - .xreadgroup_map("group1", "consumer1", Some(1), None, false, "foo{1}", ">") - .await?; - assert_eq!(result.len(), 1); - assert_eq!(result.get("foo{1}").unwrap().len(), 1); - let first_read_id = result.get_mut("foo{1}").unwrap().pop().unwrap().0; - sleep(Duration::from_secs(1)).await; - - let (total_count, min_id, max_id, consumers): (u64, String, String, Vec<(String, u64)>) = - client.xpending("foo{1}", "group1", ()).await?; - assert_eq!(total_count, 1); - assert_eq!(min_id, first_read_id); - assert_eq!(max_id, first_read_id); - assert_eq!(consumers[0], ("consumer1".into(), 1)); - - let mut result: Vec<(String, HashMap)> = client - .xclaim_values( - "foo{1}", - "group1", - "consumer2", - 1000, - &first_read_id, - None, - None, - None, - false, - false, - ) - .await?; - - assert_eq!(result.len(), 1); - assert_eq!(result[0].0.as_str(), first_read_id); - let value = result[0].1.remove("count").unwrap(); - assert_eq!(value, 0); - - let acked: i64 = client.xack("foo{1}", "group1", first_read_id).await?; - assert_eq!(acked, 1); - Ok(()) -} - -pub async fn should_xclaim_multiple_ids(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let _ = add_stream_entries(&client, "foo{1}", 3).await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer1").await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer2").await?; - - let mut result: XReadResponse = client - .xreadgroup_map("group1", "consumer1", Some(2), None, false, "foo{1}", ">") - .await?; - assert_eq!(result.len(), 1); - assert_eq!(result.get("foo{1}").unwrap().len(), 2); - let second_read_id = result.get_mut("foo{1}").unwrap().pop().unwrap().0; - let first_read_id = result.get_mut("foo{1}").unwrap().pop().unwrap().0; - sleep(Duration::from_secs(1)).await; - - let (total_count, min_id, max_id, consumers): (u64, String, String, Vec<(String, u64)>) = - client.xpending("foo{1}", "group1", ()).await?; - assert_eq!(total_count, 2); - assert_eq!(min_id, first_read_id); - assert_eq!(max_id, second_read_id); - assert_eq!(consumers[0], ("consumer1".into(), 2)); - - let mut result: Vec<(String, HashMap)> = client - .xclaim_values( - "foo{1}", - "group1", - "consumer2", - 1000, - vec![&first_read_id, &second_read_id], - None, - None, - None, - false, - false, - ) - .await?; - - assert_eq!(result.len(), 2); - assert_eq!(result[0].0.as_str(), first_read_id); - assert_eq!(result[1].0.as_str(), second_read_id); - let first_value = result[0].1.remove("count").unwrap(); - let second_value = result[1].1.remove("count").unwrap(); - assert_eq!(first_value, 0); - assert_eq!(second_value, 1); - - let acked: i64 = client - .xack("foo{1}", "group1", vec![first_read_id, second_read_id]) - .await?; - assert_eq!(acked, 2); - Ok(()) -} - -pub async fn should_xclaim_with_justid(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let _ = add_stream_entries(&client, "foo{1}", 3).await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer1").await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer2").await?; - - let mut result: XReadResponse = client - .xreadgroup_map("group1", "consumer1", Some(2), None, false, "foo{1}", ">") - .await?; - assert_eq!(result.len(), 1); - assert_eq!(result.get("foo{1}").unwrap().len(), 2); - let second_read_id = result.get_mut("foo{1}").unwrap().pop().unwrap().0; - let first_read_id = result.get_mut("foo{1}").unwrap().pop().unwrap().0; - sleep(Duration::from_secs(1)).await; - - let (total_count, min_id, max_id, consumers): (u64, String, String, Vec<(String, u64)>) = - client.xpending("foo{1}", "group1", ()).await?; - assert_eq!(total_count, 2); - assert_eq!(min_id, first_read_id); - assert_eq!(max_id, second_read_id); - assert_eq!(consumers[0], ("consumer1".into(), 2)); - - let result: Vec = client - .xclaim( - "foo{1}", - "group1", - "consumer2", - 1000, - vec![&first_read_id, &second_read_id], - None, - None, - None, - false, - true, - ) - .await?; - assert_eq!(result, vec![first_read_id.clone(), second_read_id.clone()]); - - let acked: i64 = client - .xack("foo{1}", "group1", vec![first_read_id, second_read_id]) - .await?; - assert_eq!(acked, 2); - Ok(()) -} - -pub async fn should_xautoclaim_default(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - create_fake_group_and_stream(&client, "foo{1}").await?; - let _ = add_stream_entries(&client, "foo{1}", 3).await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer1").await?; - client.xgroup_createconsumer("foo{1}", "group1", "consumer2").await?; - - let mut result: XReadResponse = client - .xreadgroup_map("group1", "consumer1", Some(2), None, false, "foo{1}", ">") - .await?; - assert_eq!(result.len(), 1); - assert_eq!(result.get("foo{1}").unwrap().len(), 2); - let second_read_id = result.get_mut("foo{1}").unwrap().pop().unwrap().0; - let first_read_id = result.get_mut("foo{1}").unwrap().pop().unwrap().0; - sleep(Duration::from_secs(1)).await; - - let (total_count, min_id, max_id, consumers): (u64, String, String, Vec<(String, u64)>) = - client.xpending("foo{1}", "group1", ()).await?; - assert_eq!(total_count, 2); - assert_eq!(min_id, first_read_id); - assert_eq!(max_id, second_read_id); - assert_eq!(consumers[0], ("consumer1".into(), 2)); - - let (cursor, values): (String, Vec>) = client - .xautoclaim_values("foo{1}", "group1", "consumer2", 1000, "0-0", None, false) - .await?; - - assert_eq!(cursor, "0-0"); - assert_eq!(values.len(), 2); - - let mut first_expected: HashMap = HashMap::new(); - first_expected.insert("count".into(), 0); - let mut second_expected: HashMap = HashMap::new(); - second_expected.insert("count".into(), 1); - assert_eq!(values[0], (first_read_id, first_expected)); - assert_eq!(values[1], (second_read_id, second_expected)); - - Ok(()) -} diff --git a/tests/integration/timeseries/mod.rs b/tests/integration/timeseries/mod.rs deleted file mode 100644 index 648c49b8..00000000 --- a/tests/integration/timeseries/mod.rs +++ /dev/null @@ -1,379 +0,0 @@ -use bytes_utils::Str; -use fred::{ - clients::RedisClient, - error::RedisError, - interfaces::*, - prelude::RedisResult, - types::{ - Aggregator, - GetLabels, - RedisConfig, - RedisKey, - RedisValue, - Resp2TimeSeriesValues, - Resp3TimeSeriesValues, - Timestamp, - }, -}; -use redis_protocol::resp3::types::RespVersion; -use std::{collections::HashMap, time::Duration}; -use tokio::time::sleep; - -pub async fn should_ts_add_get_and_range(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - let first_timestamp: i64 = client.ts_add("foo", "*", 41.0, None, None, None, None, ()).await?; - assert!(first_timestamp > 0); - sleep(Duration::from_millis(5)).await; - let second_timestamp: i64 = client.ts_add("foo", "*", 42.0, None, None, None, None, ()).await?; - sleep(Duration::from_millis(5)).await; - assert!(second_timestamp > 0); - assert!(second_timestamp > first_timestamp); - let (timestamp, latest): (i64, f64) = client.ts_get("foo", true).await?; - assert_eq!(latest, 42.0); - assert_eq!(timestamp, second_timestamp); - - let range: Vec<(i64, f64)> = client.ts_range("foo", "-", "+", true, [], None, None, None).await?; - assert_eq!(range, vec![(first_timestamp, 41.0), (second_timestamp, 42.0)]); - Ok(()) -} - -pub async fn should_create_alter_and_del_timeseries(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.ts_create("foo{1}", None, None, None, None, ("a", "b")).await?; - client.ts_alter("foo{1}", None, None, None, ("b", "c")).await?; - - Ok(()) -} - -pub async fn should_madd_and_mget(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.ts_create("foo{1}", None, None, None, None, ("a", "b")).await?; - client.ts_create("bar{1}", None, None, None, None, ("a", "b")).await?; - - let values = vec![ - ("foo{1}", 1, 1.1), - ("foo{1}", 2, 2.2), - ("foo{1}", 3, 3.3), - ("bar{1}", 1, 1.2), - ("bar{1}", 2, 2.3), - ]; - - let args: Vec<_> = values.clone().into_iter().map(|(k, t, v)| (k, t.into(), v)).collect(); - let timestamps: Vec = client.ts_madd(args).await?; - assert_eq!(timestamps, vec![1, 2, 3, 1, 2]); - - let mut keys: Vec = client.ts_queryindex(["a=b"]).await?; - keys.sort(); - assert_eq!(keys, vec!["bar{1}", "foo{1}"]); - - if client.protocol_version() == RespVersion::RESP2 { - let mut values: Resp2TimeSeriesValues = - client.ts_mget(false, Some(GetLabels::WithLabels), ["a=b"]).await?; - values.sort_by(|(lhs_key, _, _), (rhs_key, _, _)| lhs_key.cmp(rhs_key)); - - let expected = vec![ - ("bar{1}".to_string(), vec![("a".to_string(), "b".to_string())], vec![( - 2, 2.3, - )]), - ("foo{1}".to_string(), vec![("a".to_string(), "b".to_string())], vec![( - 3, 3.3, - )]), - ]; - assert_eq!(values, expected); - } else { - let values: Resp3TimeSeriesValues = - client.ts_mget(false, Some(GetLabels::WithLabels), ["a=b"]).await?; - - let mut expected = HashMap::new(); - expected.insert( - "foo{1}".to_string(), - (vec![("a".to_string(), "b".to_string())], vec![(3, 3.3)]), - ); - expected.insert( - "bar{1}".to_string(), - (vec![("a".to_string(), "b".to_string())], vec![(2, 2.3)]), - ); - assert_eq!(values, expected); - } - Ok(()) -} - -pub async fn should_incr_and_decr(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - // taken from the docs - let timestamp: i64 = client - .ts_incrby( - "foo", - 232.0, - Some(Timestamp::Custom(1657811829000)), - None, - false, - None, - (), - ) - .await?; - assert_eq!(timestamp, 1657811829000); - let timestamp: i64 = client - .ts_incrby( - "foo", - 157.0, - Some(Timestamp::Custom(1657811829000)), - None, - false, - None, - (), - ) - .await?; - assert_eq!(timestamp, 1657811829000); - let timestamp: i64 = client - .ts_decrby( - "foo", - 157.0, - Some(Timestamp::Custom(1657811829000)), - None, - false, - None, - (), - ) - .await?; - assert_eq!(timestamp, 1657811829000); - - Ok(()) -} - -pub async fn should_create_and_delete_rules(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client - .ts_create("temp:TLV", None, None, None, None, [ - ("type", "temp"), - ("location", "TLV"), - ]) - .await?; - client - .ts_create("dailyAvgTemp:TLV", None, None, None, None, [ - ("type", "temp"), - ("location", "TLV"), - ]) - .await?; - client - .ts_createrule("temp:TLV", "dailyAvgTemp:TLV", (Aggregator::TWA, 86400000), None) - .await?; - client.ts_deleterule("temp:TLV", "dailyAvgTemp:TLV").await?; - - Ok(()) -} - -pub async fn should_madd_and_mrange(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.ts_create("foo{1}", None, None, None, None, ("a", "b")).await?; - client.ts_create("bar{1}", None, None, None, None, ("a", "b")).await?; - - let values = vec![ - ("foo{1}", 1, 1.1), - ("foo{1}", 2, 2.2), - ("foo{1}", 3, 3.3), - ("bar{1}", 1, 1.2), - ("bar{1}", 2, 2.3), - ]; - let args: Vec<_> = values.clone().into_iter().map(|(k, t, v)| (k, t.into(), v)).collect(); - let timestamps: Vec = client.ts_madd(args).await?; - assert_eq!(timestamps, vec![1, 2, 3, 1, 2]); - - if client.protocol_version() == RespVersion::RESP2 { - let mut samples: Resp2TimeSeriesValues = client - .ts_mrange( - "-", - "+", - false, - None, - None, - Some(GetLabels::WithLabels), - None, - None, - ["a=b"], - None, - ) - .await?; - samples.sort_by(|(l, _, _), (r, _, _)| l.cmp(r)); - - let expected = vec![ - ("bar{1}".to_string(), vec![("a".to_string(), "b".to_string())], vec![ - (1, 1.2), - (2, 2.3), - ]), - ("foo{1}".to_string(), vec![("a".to_string(), "b".to_string())], vec![ - (1, 1.1), - (2, 2.2), - (3, 3.3), - ]), - ]; - assert_eq!(samples, expected) - } else { - // RESP3 has an additional (undocumented?) aggregators section - // Array([ - // String("bar{1}"), - // Array([ - // Array([ - // String("a"), - // String("b") - // ]), - // Array([ - // String("aggregators"), - // Array([]) - // ]), - // Array([ - // Array([ - // Integer(1), - // Double(1.2) - // ]), - // Array([ - // Integer(2), - // Double(2.3) - // ]) - // ]) - // ]), - // String("foo{1}"), - // Array([ - // Array([ - // String("a"), - // String("b") - // ]), - // Array([ - // String("aggregators"), - // Array([]) - // ]), - // Array([ - // Array([ - // Integer(1), - // Double(1.1) - // ]), - // Array([ - // Integer(2), - // Double(2.2) - // ]), - // Array([ - // Integer(3), - // Double(3.3) - // ]) - // ]) - // ]) - // ]) - // - // TODO add another TimeSeriesValues type alias for this? - - let samples: HashMap, Vec, Vec<(i64, f64)>)> = client - .ts_mrange( - "-", - "+", - false, - None, - None, - Some(GetLabels::WithLabels), - None, - None, - ["a=b"], - None, - ) - .await?; - - let mut expected = HashMap::new(); - expected.insert( - "foo{1}".to_string(), - ( - vec![("a".to_string(), "b".to_string())], - vec!["aggregators".as_bytes().into(), RedisValue::Array(vec![])], - vec![(1, 1.1), (2, 2.2), (3, 3.3)], - ), - ); - expected.insert( - "bar{1}".to_string(), - ( - vec![("a".to_string(), "b".to_string())], - vec!["aggregators".as_bytes().into(), RedisValue::Array(vec![])], - vec![(1, 1.2), (2, 2.3)], - ), - ); - assert_eq!(samples, expected) - } - - Ok(()) -} - -pub async fn should_madd_and_mrevrange(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - client.ts_create("foo{1}", None, None, None, None, ("a", "b")).await?; - client.ts_create("bar{1}", None, None, None, None, ("a", "b")).await?; - - let values = vec![ - ("foo{1}", 1, 1.1), - ("foo{1}", 2, 2.2), - ("foo{1}", 3, 3.3), - ("bar{1}", 1, 1.2), - ("bar{1}", 2, 2.3), - ]; - let args: Vec<_> = values.clone().into_iter().map(|(k, t, v)| (k, t.into(), v)).collect(); - let timestamps: Vec = client.ts_madd(args).await?; - assert_eq!(timestamps, vec![1, 2, 3, 1, 2]); - - if client.protocol_version() == RespVersion::RESP2 { - let mut samples: Resp2TimeSeriesValues = client - .ts_mrevrange( - "-", - "+", - false, - None, - None, - Some(GetLabels::WithLabels), - None, - None, - ["a=b"], - None, - ) - .await?; - samples.sort_by(|(l, _, _), (r, _, _)| l.cmp(r)); - - let expected = vec![ - ("bar{1}".to_string(), vec![("a".to_string(), "b".to_string())], vec![ - (2, 2.3), - (1, 1.2), - ]), - ("foo{1}".to_string(), vec![("a".to_string(), "b".to_string())], vec![ - (3, 3.3), - (2, 2.2), - (1, 1.1), - ]), - ]; - assert_eq!(samples, expected) - } else { - // see the mrange test above for more info on this section - - let samples: HashMap, Vec, Vec<(i64, f64)>)> = client - .ts_mrevrange( - "-", - "+", - false, - None, - None, - Some(GetLabels::WithLabels), - None, - None, - ["a=b"], - None, - ) - .await?; - - let mut expected = HashMap::new(); - expected.insert( - "foo{1}".to_string(), - ( - vec![("a".to_string(), "b".to_string())], - vec!["aggregators".as_bytes().into(), RedisValue::Array(vec![])], - vec![(3, 3.3), (2, 2.2), (1, 1.1)], - ), - ); - expected.insert( - "bar{1}".to_string(), - ( - vec![("a".to_string(), "b".to_string())], - vec!["aggregators".as_bytes().into(), RedisValue::Array(vec![])], - vec![(2, 2.3), (1, 1.2)], - ), - ); - assert_eq!(samples, expected) - } - - Ok(()) -} diff --git a/tests/integration/tracking/mod.rs b/tests/integration/tracking/mod.rs deleted file mode 100644 index ba2b458c..00000000 --- a/tests/integration/tracking/mod.rs +++ /dev/null @@ -1,105 +0,0 @@ -use fred::{ - prelude::*, - types::{RedisKey, RespVersion}, -}; -use std::{ - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, - time::Duration, -}; -use tokio::time::sleep; - -#[allow(dead_code)] -#[cfg(feature = "i-keys")] -pub async fn should_invalidate_foo_resp3(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - if client.protocol_version() == RespVersion::RESP2 { - return Ok(()); - } - - let key: RedisKey = "foo{1}".into(); - let invalidated = Arc::new(AtomicBool::new(false)); - let _invalidated = invalidated.clone(); - - let mut invalidations = client.invalidation_rx(); - tokio::spawn(async move { - while let Ok(invalidation) = invalidations.recv().await { - if invalidation.keys.contains(&key) { - _invalidated.swap(true, Ordering::SeqCst); - } - } - }); - - client.start_tracking(None, false, false, false, false).await?; - client.get("foo{1}").await?; - client.incr("foo{1}").await?; - - client.mget(vec!["bar{1}", "baz{1}"]).await?; - client.mset(vec![("bar{1}", 1), ("baz{1}", 1)]).await?; - client.flushall(false).await?; - - sleep(Duration::from_secs(1)).await; - if invalidated.load(Ordering::Acquire) { - Ok(()) - } else { - panic!("Failed to invalidate foo"); - } -} - -#[allow(dead_code)] -#[cfg(feature = "i-keys")] -pub async fn should_invalidate_foo_resp2_centralized(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> { - if client.protocol_version() == RespVersion::RESP3 || client.is_clustered() { - return Ok(()); - } - - let key: RedisKey = "foo{1}".into(); - let subscriber = client.clone_new(); - subscriber.connect(); - subscriber.wait_for_connect().await?; - - let invalidated = Arc::new(AtomicBool::new(false)); - let _invalidated = invalidated.clone(); - - let mut invalidations = subscriber.invalidation_rx(); - tokio::spawn(async move { - while let Ok(invalidation) = invalidations.recv().await { - if invalidation.keys.contains(&key) { - _invalidated.swap(true, Ordering::SeqCst); - } - } - }); - subscriber.subscribe("__redis__:invalidate").await?; - - let (_, subscriber_id) = subscriber - .connection_ids() - .await - .into_iter() - .next() - .expect("Failed to read subscriber connection ID"); - - client - .client_tracking("on", Some(subscriber_id), None, false, false, false, false) - .await?; - - // verify that we get 2 keys in the invalidation message, or at least make sure that doesnt panic - // in resp2 this might take some changes to the pubsub parser if it doesn't work with an array as the message type - - // check pubsub messages with one key - client.get("foo{1}").await?; - client.incr("foo{1}").await?; - - // check pubsub messages with an array of keys - client.mget(vec!["bar{1}", "baz{1}"]).await?; - client.mset(vec![("bar{1}", 1), ("baz{1}", 1)]).await?; - // check pubsub messages with a null key - client.flushall(false).await?; - - sleep(Duration::from_secs(1)).await; - if invalidated.load(Ordering::Acquire) { - Ok(()) - } else { - panic!("Failed to invalidate foo"); - } -} diff --git a/tests/integration/utils.rs b/tests/integration/utils.rs deleted file mode 100644 index 5ac7f1e7..00000000 --- a/tests/integration/utils.rs +++ /dev/null @@ -1,733 +0,0 @@ -#![allow(unused_macros)] -#![allow(unused_imports)] -#![allow(dead_code)] -#![allow(unused_variables)] -#![allow(clippy::match_like_matches_macro)] - -use fred::{ - clients::RedisClient, - error::RedisError, - interfaces::*, - types::{ - Builder, - ConnectionConfig, - PerformanceConfig, - ReconnectPolicy, - RedisConfig, - Server, - ServerConfig, - UnresponsiveConfig, - }, -}; -use redis_protocol::resp3::types::RespVersion; -use std::{convert::TryInto, default::Default, env, fmt, fmt::Formatter, fs, future::Future, time::Duration}; - -const RECONNECT_DELAY: u32 = 1000; - -use fred::types::ClusterDiscoveryPolicy; -#[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" -))] -use fred::types::{TlsConfig, TlsConnector, TlsHostMapping}; -#[cfg(feature = "enable-native-tls")] -use tokio_native_tls::native_tls::{ - Certificate as NativeTlsCertificate, - Identity, - TlsConnector as NativeTlsConnector, -}; -#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] -use tokio_rustls::rustls::{ClientConfig, ConfigBuilder, RootCertStore, WantsVerifier}; - -pub fn read_env_var(name: &str) -> Option { - env::var_os(name).and_then(|s| s.into_string().ok()) -} - -pub fn should_use_sentinel_config() -> bool { - read_env_var("FRED_SENTINEL_TESTS") - .map(|s| match s.as_ref() { - "1" | "t" | "true" | "yes" => true, - _ => false, - }) - .unwrap_or(false) -} - -pub fn should_flushall_between_tests() -> bool { - read_env_var("FRED_NO_FLUSHALL_DURING_TESTS") - .map(|s| match s.as_ref() { - "1" | "t" | "true" | "yes" => false, - _ => true, - }) - .unwrap_or(true) -} - -pub fn read_ci_tls_env() -> bool { - match env::var_os("FRED_CI_TLS") { - Some(s) => match s.into_string() { - Ok(s) => match s.as_ref() { - "t" | "true" | "TRUE" | "1" => true, - _ => false, - }, - Err(_) => false, - }, - None => false, - } -} - -fn read_fail_fast_env() -> bool { - match env::var_os("FRED_FAIL_FAST") { - Some(s) => match s.into_string() { - Ok(s) => match s.as_ref() { - "f" | "false" | "FALSE" | "0" => false, - _ => true, - }, - Err(_) => true, - }, - None => true, - } -} - -#[cfg(feature = "i-redis-stack")] -pub fn read_redis_centralized_host() -> (String, u16) { - let host = read_env_var("FRED_REDIS_STACK_HOST").unwrap_or("redis-main".into()); - let port = read_env_var("FRED_REDIS_STACK_PORT") - .and_then(|s| s.parse::().ok()) - .unwrap_or(6379); - - (host, port) -} - -#[cfg(not(feature = "i-redis-stack"))] -pub fn read_redis_centralized_host() -> (String, u16) { - let host = read_env_var("FRED_REDIS_CENTRALIZED_HOST").unwrap_or("redis-main".into()); - let port = read_env_var("FRED_REDIS_CENTRALIZED_PORT") - .and_then(|s| s.parse::().ok()) - .unwrap_or(6379); - - (host, port) -} - -#[cfg(not(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" -)))] -pub fn read_redis_cluster_host() -> (String, u16) { - let host = read_env_var("FRED_REDIS_CLUSTER_HOST").unwrap_or("redis-cluster-1".into()); - let port = read_env_var("FRED_REDIS_CLUSTER_PORT") - .and_then(|s| s.parse::().ok()) - .unwrap_or(30001); - - (host, port) -} - -#[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" -))] -pub fn read_redis_cluster_host() -> (String, u16) { - let host = read_env_var("FRED_REDIS_CLUSTER_TLS_HOST").unwrap_or("redis-cluster-tls-1".into()); - let port = read_env_var("FRED_REDIS_CLUSTER_TLS_PORT") - .and_then(|s| s.parse::().ok()) - .unwrap_or(40001); - - (host, port) -} - -pub fn read_redis_password() -> String { - read_env_var("REDIS_PASSWORD").expect("Failed to read REDIS_PASSWORD env") -} - -#[cfg(not(feature = "i-redis-stack"))] -pub fn read_redis_username() -> String { - read_env_var("REDIS_USERNAME").expect("Failed to read REDIS_USERNAME env") -} - -// the CI settings for redis-stack don't set up custom ACL rules -#[cfg(feature = "i-redis-stack")] -pub fn read_redis_username() -> String { - read_env_var("REDIS_USERNAME").unwrap_or("default".into()) -} - -#[cfg(feature = "sentinel-auth")] -pub fn read_sentinel_password() -> String { - read_env_var("REDIS_SENTINEL_PASSWORD").expect("Failed to read REDIS_SENTINEL_PASSWORD env") -} - -#[cfg(feature = "unix-sockets")] -pub fn read_unix_socket_path() -> String { - let dir = read_env_var("REDIS_UNIX_SOCK_CONTAINER_DIR").expect("Failed to read REDIS_UNIX_SOCK_CONTAINER_DIR"); - let sock = read_env_var("REDIS_UNIX_SOCK").expect("Failed to read REDIS_UNIX_SOCK"); - format!("{}/{}", dir, sock) -} - -pub fn read_sentinel_server() -> (String, u16) { - let host = read_env_var("FRED_REDIS_SENTINEL_HOST").unwrap_or("127.0.0.1".into()); - let port = read_env_var("FRED_REDIS_SENTINEL_PORT") - .and_then(|s| s.parse::().ok()) - .unwrap_or(26379); - - (host, port) -} - -#[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" -))] -#[allow(dead_code)] -struct TlsCreds { - root_cert_der: Vec, - root_cert_pem: Vec, - client_cert_der: Vec, - client_cert_pem: Vec, - client_key_der: Vec, - client_key_pem: Vec, -} - -#[cfg(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" -))] -fn check_file_contents(value: &Vec, msg: &str) { - if value.is_empty() { - panic!("Invalid empty TLS file: {}", msg); - } -} - -/// Read the (root cert.pem, root cert.der, client cert.pem, client cert.der, client key.pem, client key.der) tuple -/// from the test/tmp/creds directory. -#[cfg(any( - feature = "enable-native-tls", - feature = "enable-rustls", - feature = "enable-rustls-ring" -))] -fn read_tls_creds() -> TlsCreds { - let creds_path = read_env_var("FRED_TEST_TLS_CREDS").expect("Failed to read TLS path from env"); - let root_cert_pem_path = format!("{}/ca.pem", creds_path); - let root_cert_der_path = format!("{}/ca.crt", creds_path); - let client_cert_pem_path = format!("{}/client.pem", creds_path); - let client_cert_der_path = format!("{}/client.crt", creds_path); - let client_key_der_path = format!("{}/client_key.der", creds_path); - let client_key_pem_path = format!("{}/client.key8", creds_path); - - let root_cert_pem = fs::read(&root_cert_pem_path).expect("Failed to read root cert pem"); - let root_cert_der = fs::read(&root_cert_der_path).expect("Failed to read root cert der"); - let client_cert_pem = fs::read(&client_cert_pem_path).expect("Failed to read client cert pem"); - let client_cert_der = fs::read(&client_cert_der_path).expect("Failed to read client cert der"); - let client_key_der = fs::read(&client_key_der_path).expect("Failed to read client key der"); - let client_key_pem = fs::read(&client_key_pem_path).expect("Failed to read client key pem"); - - check_file_contents(&root_cert_pem, "root cert pem"); - check_file_contents(&root_cert_der, "root cert der"); - check_file_contents(&client_cert_pem, "client cert pem"); - check_file_contents(&client_cert_der, "client cert der"); - check_file_contents(&client_key_pem, "client key pem"); - check_file_contents(&client_key_der, "client key der"); - - TlsCreds { - root_cert_pem, - root_cert_der, - client_cert_der, - client_cert_pem, - client_key_pem, - client_key_der, - } -} - -#[cfg(any(feature = "enable-rustls", feature = "enable-rustls-ring"))] -fn create_rustls_config() -> TlsConnector { - use rustls::pki_types::PrivatePkcs8KeyDer; - - let creds = read_tls_creds(); - let mut root_store = RootCertStore::empty(); - let _ = root_store - .add(creds.root_cert_der.clone().into()) - .expect("Failed adding to rustls root cert store"); - - let cert_chain = vec![creds.client_cert_der.into(), creds.root_cert_der.into()]; - - ClientConfig::builder() - .with_root_certificates(root_store) - .with_client_auth_cert(cert_chain, PrivatePkcs8KeyDer::from(creds.client_key_der).into()) - .expect("Failed to build rustls client config") - .into() -} - -#[cfg(feature = "enable-native-tls")] -fn create_native_tls_config() -> TlsConnector { - let creds = read_tls_creds(); - - let root_cert = NativeTlsCertificate::from_pem(&creds.root_cert_pem).expect("Failed to parse root cert"); - let mut builder = NativeTlsConnector::builder(); - builder.add_root_certificate(root_cert); - - let mut client_cert_chain = Vec::with_capacity(creds.client_cert_pem.len() + creds.root_cert_pem.len()); - client_cert_chain.extend(&creds.client_cert_pem); - client_cert_chain.extend(&creds.root_cert_pem); - - let identity = - Identity::from_pkcs8(&client_cert_chain, &creds.client_key_pem).expect("Failed to create client identity"); - builder.identity(identity); - - builder.try_into().expect("Failed to build native-tls connector") -} - -fn reconnect_settings() -> (Option, u32, bool) { - (Some(ReconnectPolicy::new_constant(300, RECONNECT_DELAY)), 3, true) -} - -#[cfg(feature = "unix-sockets")] -fn create_server_config(cluster: bool) -> ServerConfig { - ServerConfig::Unix { - path: read_unix_socket_path().into(), - } -} - -#[cfg(not(feature = "unix-sockets"))] -fn create_server_config(cluster: bool) -> ServerConfig { - if cluster { - let (host, port) = read_redis_cluster_host(); - ServerConfig::Clustered { - hosts: vec![Server::new(host, port)], - policy: ClusterDiscoveryPolicy::default(), - } - } else { - let (host, port) = read_redis_centralized_host(); - ServerConfig::Centralized { - server: Server::new(host, port), - } - } -} - -fn create_normal_redis_config(cluster: bool, pipeline: bool, resp3: bool) -> (RedisConfig, PerformanceConfig) { - let config = RedisConfig { - fail_fast: read_fail_fast_env(), - server: create_server_config(cluster), - version: if resp3 { RespVersion::RESP3 } else { RespVersion::RESP2 }, - username: Some(read_redis_username()), - password: Some(read_redis_password()), - ..Default::default() - }; - let perf = PerformanceConfig { - auto_pipeline: pipeline, - default_command_timeout: Duration::from_secs(20), - ..Default::default() - }; - - (config, perf) -} - -#[cfg(not(any( - feature = "enable-rustls", - feature = "enable-native-tls", - feature = "enable-rustls-ring" -)))] -fn create_redis_config(cluster: bool, pipeline: bool, resp3: bool) -> (RedisConfig, PerformanceConfig) { - create_normal_redis_config(cluster, pipeline, resp3) -} - -#[cfg(all( - feature = "enable-native-tls", - any(feature = "enable-rustls", feature = "enable-rustls-ring") -))] -fn create_redis_config(cluster: bool, pipeline: bool, resp3: bool) -> (RedisConfig, PerformanceConfig) { - // if both are enabled then don't use either since all the tests assume one or the other - create_normal_redis_config(cluster, pipeline, resp3) -} - -#[cfg(all( - any(feature = "enable-rustls", feature = "enable-rustls-ring"), - not(feature = "enable-native-tls") -))] -fn create_redis_config(cluster: bool, pipeline: bool, resp3: bool) -> (RedisConfig, PerformanceConfig) { - if !read_ci_tls_env() { - return create_normal_redis_config(cluster, pipeline, resp3); - } - - debug!("Creating rustls test config..."); - let config = RedisConfig { - fail_fast: read_fail_fast_env(), - server: create_server_config(cluster), - version: if resp3 { RespVersion::RESP3 } else { RespVersion::RESP2 }, - tls: Some(TlsConfig { - connector: create_rustls_config(), - hostnames: TlsHostMapping::DefaultHost, - }), - username: Some(read_redis_username()), - password: Some(read_redis_password()), - ..Default::default() - }; - let perf = PerformanceConfig { - auto_pipeline: pipeline, - default_command_timeout: Duration::from_secs(20), - ..Default::default() - }; - - (config, perf) -} - -#[cfg(all( - feature = "enable-native-tls", - not(any(feature = "enable-rustls", feature = "enable-rustls-ring")) -))] -fn create_redis_config(cluster: bool, pipeline: bool, resp3: bool) -> (RedisConfig, PerformanceConfig) { - if !read_ci_tls_env() { - return create_normal_redis_config(cluster, pipeline, resp3); - } - - debug!("Creating native-tls test config..."); - let config = RedisConfig { - fail_fast: read_fail_fast_env(), - server: create_server_config(cluster), - version: if resp3 { RespVersion::RESP3 } else { RespVersion::RESP2 }, - tls: Some(TlsConfig { - connector: create_native_tls_config(), - hostnames: TlsHostMapping::DefaultHost, - }), - username: Some(read_redis_username()), - password: Some(read_redis_password()), - ..Default::default() - }; - let perf = PerformanceConfig { - auto_pipeline: pipeline, - default_command_timeout: Duration::from_secs(20), - ..Default::default() - }; - - (config, perf) -} - -async fn flushall_between_tests(client: &RedisClient) -> Result<(), RedisError> { - if should_flushall_between_tests() { - client.flushall_cluster().await - } else { - Ok(()) - } -} - -pub async fn run_sentinel(func: F, pipeline: bool, resp3: bool) -where - F: Fn(RedisClient, RedisConfig) -> Fut, - Fut: Future>, -{ - let policy = ReconnectPolicy::new_constant(300, RECONNECT_DELAY); - let connection = ConnectionConfig::default(); - let config = RedisConfig { - fail_fast: read_fail_fast_env(), - version: if resp3 { RespVersion::RESP3 } else { RespVersion::RESP2 }, - server: ServerConfig::Sentinel { - hosts: vec![read_sentinel_server().into()], - service_name: "redis-sentinel-main".into(), - #[cfg(feature = "sentinel-auth")] - username: None, - #[cfg(feature = "sentinel-auth")] - password: Some(read_sentinel_password()), - }, - password: Some(read_redis_password()), - ..Default::default() - }; - let perf = PerformanceConfig { - auto_pipeline: pipeline, - ..Default::default() - }; - let client = RedisClient::new(config.clone(), Some(perf), Some(connection), Some(policy)); - let _client = client.clone(); - - let _jh = client.connect(); - client.wait_for_connect().await.expect("Failed to connect client"); - - flushall_between_tests(&client).await.expect("Failed to flushall"); - func(_client, config.clone()).await.expect("Failed to run test"); - let _ = client.quit().await; -} - -pub async fn run_cluster(func: F, pipeline: bool, resp3: bool) -where - F: Fn(RedisClient, RedisConfig) -> Fut, - Fut: Future>, -{ - let (policy, cmd_attempts, fail_fast) = reconnect_settings(); - let mut connection = ConnectionConfig::default(); - let (mut config, perf) = create_redis_config(true, pipeline, resp3); - connection.max_command_attempts = cmd_attempts; - connection.max_redirections = 10; - connection.unresponsive = UnresponsiveConfig { - max_timeout: Some(Duration::from_secs(10)), - interval: Duration::from_millis(400), - }; - config.fail_fast = fail_fast; - - let client = RedisClient::new(config.clone(), Some(perf), Some(connection), policy); - let _client = client.clone(); - - let _jh = client.connect(); - client.wait_for_connect().await.expect("Failed to connect client"); - - flushall_between_tests(&client).await.expect("Failed to flushall"); - func(_client, config.clone()).await.expect("Failed to run test"); - let _ = client.quit().await; -} - -pub async fn run_centralized(func: F, pipeline: bool, resp3: bool) -where - F: Fn(RedisClient, RedisConfig) -> Fut, - Fut: Future>, -{ - if should_use_sentinel_config() { - return run_sentinel(func, pipeline, resp3).await; - } - - let (policy, cmd_attempts, fail_fast) = reconnect_settings(); - let mut connection = ConnectionConfig::default(); - let (mut config, perf) = create_redis_config(false, pipeline, resp3); - connection.max_command_attempts = cmd_attempts; - connection.unresponsive = UnresponsiveConfig { - max_timeout: Some(Duration::from_secs(10)), - interval: Duration::from_millis(400), - }; - config.fail_fast = fail_fast; - - let client = RedisClient::new(config.clone(), Some(perf), Some(connection), policy); - let _client = client.clone(); - - let _jh = client.connect(); - client.wait_for_connect().await.expect("Failed to connect client"); - - flushall_between_tests(&client).await.expect("Failed to flushall"); - func(_client, config.clone()).await.expect("Failed to run test"); - let _ = client.quit().await; -} - -macro_rules! centralized_test_panic( - ($module:tt, $name:tt) => { - #[cfg(not(any(feature = "enable-rustls", feature = "enable-native-tls", feature = "enable-rustls-ring")))] - mod $name { - mod resp2 { - #[tokio::test(flavor = "multi_thread")] - #[should_panic] - async fn pipelined() { - if crate::integration::utils::read_ci_tls_env() { - panic!(""); - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_centralized(crate::integration::$module::$name, true, false).await; - } - - #[tokio::test(flavor = "multi_thread")] - #[should_panic] - async fn no_pipeline() { - if crate::integration::utils::read_ci_tls_env() { - panic!(""); - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_centralized(crate::integration::$module::$name, false, false).await; - } - } - - mod resp3 { - #[tokio::test(flavor = "multi_thread")] - #[should_panic] - async fn pipelined() { - if crate::integration::utils::read_ci_tls_env() { - panic!(""); - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_centralized(crate::integration::$module::$name, true, true).await; - } - - #[tokio::test(flavor = "multi_thread")] - #[should_panic] - async fn no_pipeline() { - if crate::integration::utils::read_ci_tls_env() { - panic!(""); - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_centralized(crate::integration::$module::$name, false, true).await; - } - } - } - } -); - -macro_rules! cluster_test_panic( - ($module:tt, $name:tt) => { - mod $name { - #[cfg(not(any(feature = "i-redis-stack", feature = "unix-sockets")))] - mod resp2 { - #[tokio::test(flavor = "multi_thread")] - #[should_panic] - async fn pipelined() { - if crate::integration::utils::should_use_sentinel_config() { - panic!(""); - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_cluster(crate::integration::$module::$name, true, false).await; - } - - #[tokio::test(flavor = "multi_thread")] - #[should_panic] - async fn no_pipeline() { - if crate::integration::utils::should_use_sentinel_config() { - panic!(""); - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_cluster(crate::integration::$module::$name, false, false).await; - } - } - - #[cfg(not(any(feature = "i-redis-stack", feature = "unix-sockets")))] - mod resp3 { - #[tokio::test(flavor = "multi_thread")] - #[should_panic] - async fn pipelined() { - if crate::integration::utils::should_use_sentinel_config() { - panic!(""); - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_cluster(crate::integration::$module::$name, true, true).await; - } - - #[tokio::test(flavor = "multi_thread")] - #[should_panic] - async fn no_pipeline() { - if crate::integration::utils::should_use_sentinel_config() { - panic!(""); - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_cluster(crate::integration::$module::$name, false, true).await; - } - } - } - } -); - -macro_rules! centralized_test( - ($module:tt, $name:tt) => { - #[cfg(not(any(feature = "enable-rustls", feature = "enable-native-tls", feature = "enable-rustls-ring")))] - mod $name { - mod resp2 { - #[tokio::test(flavor = "multi_thread")] - async fn pipelined() { - if crate::integration::utils::read_ci_tls_env() { - return; - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_centralized(crate::integration::$module::$name, true, false).await; - } - - #[tokio::test(flavor = "multi_thread")] - async fn no_pipeline() { - if crate::integration::utils::read_ci_tls_env() { - return; - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_centralized(crate::integration::$module::$name, false, false).await; - } - } - - mod resp3 { - #[tokio::test(flavor = "multi_thread")] - async fn pipelined() { - if crate::integration::utils::read_ci_tls_env() { - return; - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_centralized(crate::integration::$module::$name, true, true).await; - } - - #[tokio::test(flavor = "multi_thread")] - async fn no_pipeline() { - if crate::integration::utils::read_ci_tls_env() { - return; - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_centralized(crate::integration::$module::$name, false, true).await; - } - } - } - } -); - -macro_rules! cluster_test( - ($module:tt, $name:tt) => { - mod $name { - #[cfg(not(any(feature = "i-redis-stack", feature = "unix-sockets")))] - mod resp2 { - #[tokio::test(flavor = "multi_thread")] - async fn pipelined() { - if crate::integration::utils::should_use_sentinel_config() { - return; - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_cluster(crate::integration::$module::$name, true, false).await; - } - - #[tokio::test(flavor = "multi_thread")] - async fn no_pipeline() { - if crate::integration::utils::should_use_sentinel_config() { - return; - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_cluster(crate::integration::$module::$name, false, false).await; - } - } - - #[cfg(not(any(feature = "i-redis-stack", feature = "unix-sockets")))] - mod resp3 { - #[tokio::test(flavor = "multi_thread")] - async fn pipelined() { - if crate::integration::utils::should_use_sentinel_config() { - return; - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_cluster(crate::integration::$module::$name, true, true).await; - } - - #[tokio::test(flavor = "multi_thread")] - async fn no_pipeline() { - if crate::integration::utils::should_use_sentinel_config() { - return; - } - - let _ = pretty_env_logger::try_init(); - crate::integration::utils::run_cluster(crate::integration::$module::$name, false, true).await; - } - } - } - } -); - -macro_rules! return_err( - ($($arg:tt)*) => { { - return Err(fred::error::RedisError::new( - fred::error::RedisErrorKind::Unknown, format!($($arg)*) - )); - } } -); - -macro_rules! check_redis_7 ( - ($client:ident) => { - if $client.server_version().unwrap().major < 7 { - return Ok(()); - } - } -); diff --git a/tests/jaeger-2024.jpg b/tests/jaeger-2024.jpg deleted file mode 100644 index e9608e7d..00000000 Binary files a/tests/jaeger-2024.jpg and /dev/null differ diff --git a/tests/lib.rs b/tests/lib.rs deleted file mode 100644 index 91f967fd..00000000 --- a/tests/lib.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![allow(clippy::unnecessary_fallible_conversions)] -#![allow(clippy::redundant_pattern_matching)] -#![allow(clippy::mutable_key_type)] -#![allow(clippy::derivable_impls)] -#![allow(clippy::enum_variant_names)] -#![allow(clippy::iter_kv_map)] -#![allow(clippy::len_without_is_empty)] -#![allow(clippy::vec_init_then_push)] -#![allow(clippy::while_let_on_iterator)] -#![allow(clippy::type_complexity)] -#![allow(clippy::too_many_arguments)] -#![allow(clippy::disallowed_names)] -#![allow(unused_imports)] - -#[macro_use] -extern crate log; -extern crate core; -extern crate pretty_env_logger; - -mod integration; diff --git a/tests/runners/all-features.sh b/tests/runners/all-features.sh deleted file mode 100755 index 669a82d2..00000000 --- a/tests/runners/all-features.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -TEST_ARGV="$1" docker-compose -f tests/docker/compose/centralized.yml \ - -f tests/docker/compose/cluster.yml \ - -f tests/docker/runners/compose/all-features.yml run -u $(id -u ${USER}):$(id -g ${USER}) --rm all-features-tests \ No newline at end of file diff --git a/tests/runners/check-glommio.sh b/tests/runners/check-glommio.sh deleted file mode 100755 index 4ffab163..00000000 --- a/tests/runners/check-glommio.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker-compose -f tests/docker/compose/glommio.yml run -u $(id -u ${USER}):$(id -g ${USER}) --rm glommio - diff --git a/tests/runners/cluster-native-tls.sh b/tests/runners/cluster-native-tls.sh deleted file mode 100755 index 5f2ba9ca..00000000 --- a/tests/runners/cluster-native-tls.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -TEST_ARGV="$1" docker-compose -f tests/docker/compose/cluster-tls.yml \ - -f tests/docker/runners/compose/cluster-native-tls.yml run -u $(id -u ${USER}):$(id -g ${USER}) --rm cluster-native-tls-tests \ No newline at end of file diff --git a/tests/runners/cluster-rustls-ring.sh b/tests/runners/cluster-rustls-ring.sh deleted file mode 100755 index c98e605f..00000000 --- a/tests/runners/cluster-rustls-ring.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -TEST_ARGV="$1" docker-compose -f tests/docker/compose/cluster-tls.yml \ - -f tests/docker/runners/compose/cluster-rustls-ring.yml run -u $(id -u ${USER}):$(id -g ${USER}) --rm cluster-rustls-ring-tests \ No newline at end of file diff --git a/tests/runners/cluster-rustls.sh b/tests/runners/cluster-rustls.sh deleted file mode 100755 index d9551342..00000000 --- a/tests/runners/cluster-rustls.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -TEST_ARGV="$1" docker-compose -f tests/docker/compose/cluster-tls.yml \ - -f tests/docker/runners/compose/cluster-rustls.yml run -u $(id -u ${USER}):$(id -g ${USER}) --rm cluster-rustls-tests \ No newline at end of file diff --git a/tests/runners/default-features.sh b/tests/runners/default-features.sh deleted file mode 100755 index 4b907be9..00000000 --- a/tests/runners/default-features.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -TEST_ARGV="$1" docker-compose -f tests/docker/compose/centralized.yml \ - -f tests/docker/compose/cluster.yml \ - -f tests/docker/runners/compose/default-features.yml run \ - -u $(id -u ${USER}):$(id -g ${USER}) --rm default-features-tests \ No newline at end of file diff --git a/tests/runners/default-nil-types.sh b/tests/runners/default-nil-types.sh deleted file mode 100755 index 8ec1daa8..00000000 --- a/tests/runners/default-nil-types.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -TEST_ARGV="$1" docker-compose -f tests/docker/compose/centralized.yml \ - -f tests/docker/compose/cluster.yml \ - -f tests/docker/runners/compose/default-nil-types.yml run \ - -u $(id -u ${USER}):$(id -g ${USER}) --rm \ - default-nil-types-tests \ No newline at end of file diff --git a/tests/runners/docker-bash.sh b/tests/runners/docker-bash.sh deleted file mode 100755 index 680b2d26..00000000 --- a/tests/runners/docker-bash.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# boot all the redis servers and start a bash shell on a new container -# This uses redis-stack instead of `-f tests/docker/compose/centralized.yml` since -# they compete for a port and it's not easy to change the redis-stack port. -docker-compose -f tests/docker/compose/cluster-tls.yml \ - -f tests/docker/compose/cluster.yml \ - -f tests/docker/compose/sentinel.yml \ - -f tests/docker/compose/redis-stack.yml \ - -f tests/docker/compose/valkey-centralized.yml \ - -f tests/docker/compose/valkey-cluster.yml \ - -f tests/docker/compose/base.yml run -u $(id -u ${USER}):$(id -g ${USER}) --rm debug - diff --git a/tests/runners/everything.sh b/tests/runners/everything.sh deleted file mode 100755 index 55bfeeb1..00000000 --- a/tests/runners/everything.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -tests/runners/no-features.sh "$1"\ - && tests/runners/default-features.sh "$1"\ - && tests/runners/all-features.sh "$1"\ - && tests/runners/sentinel-features.sh "$1"\ - && tests/runners/cluster-native-tls.sh "$1"\ - && tests/runners/cluster-rustls.sh "$1" \ - && tests/runners/default-nil-types.sh "$1" \ - && tests/runners/redis-stack.sh "$1" \ No newline at end of file diff --git a/tests/runners/mocks.sh b/tests/runners/mocks.sh deleted file mode 100755 index 1c3ae2aa..00000000 --- a/tests/runners/mocks.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -tests/docker/runners/bash/mocks.sh "$0" \ No newline at end of file diff --git a/tests/runners/no-features.sh b/tests/runners/no-features.sh deleted file mode 100755 index 0259be34..00000000 --- a/tests/runners/no-features.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -TEST_ARGV="$1" docker-compose -f tests/docker/compose/centralized.yml \ - -f tests/docker/compose/cluster.yml \ - -f tests/docker/runners/compose/no-features.yml run -u $(id -u ${USER}):$(id -g ${USER}) --rm no-features-tests \ No newline at end of file diff --git a/tests/runners/redis-stack.sh b/tests/runners/redis-stack.sh deleted file mode 100755 index b61d44db..00000000 --- a/tests/runners/redis-stack.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -TEST_ARGV="$1" docker-compose -f tests/docker/compose/redis-stack.yml \ - -f tests/docker/runners/compose/redis-stack.yml run \ - -u $(id -u ${USER}):$(id -g ${USER}) --rm redis-stack-tests \ No newline at end of file diff --git a/tests/runners/sentinel-features.sh b/tests/runners/sentinel-features.sh deleted file mode 100755 index 0f717bb9..00000000 --- a/tests/runners/sentinel-features.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -TEST_ARGV="$1" docker-compose -f tests/docker/compose/sentinel.yml \ - -f tests/docker/runners/compose/sentinel-features.yml \ - run -u $(id -u ${USER}):$(id -g ${USER}) --rm sentinel-tests \ No newline at end of file diff --git a/tests/runners/unix-socket.sh b/tests/runners/unix-socket.sh deleted file mode 100755 index 7dd459a0..00000000 --- a/tests/runners/unix-socket.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -TEST_ARGV="$1" docker-compose -f tests/docker/compose/unix-socket.yml \ - -f tests/docker/runners/compose/unix-socket.yml run \ - -u $(id -u ${USER}):$(id -g ${USER}) --rm unix-socket-tests \ No newline at end of file diff --git a/tests/runners/valkey-all-features.sh b/tests/runners/valkey-all-features.sh deleted file mode 100755 index fd2b9546..00000000 --- a/tests/runners/valkey-all-features.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -TEST_ARGV="$1" docker-compose -f tests/docker/compose/valkey-centralized.yml \ - -f tests/docker/compose/valkey-cluster.yml \ - -f tests/docker/runners/compose/valkey-all-features.yml \ - run -u $(id -u ${USER}):$(id -g ${USER}) --rm valkey-all-features-tests \ No newline at end of file diff --git a/tests/scripts/check_features.sh b/tests/scripts/check_features.sh deleted file mode 100755 index 33ca1404..00000000 --- a/tests/scripts/check_features.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -e - -all_features=`yq -oy '.features["i-all"]' Cargo.toml | tr -d '\n' | sed -e 's/- / /g' | cut -c 2-` -redis_stack_features=`yq -oy '.features["i-redis-stack"]' Cargo.toml | tr -d '\n' | sed -e 's/- / /g' | cut -c 2-` - -for feature in $all_features; do - echo "Checking $feature" - cargo clippy --tests --lib -p fred --no-default-features --features "$feature" -- -Dwarnings -done - -for feature in $redis_stack_features; do - echo "Checking $feature" - cargo clippy --tests --lib -p fred --no-default-features --features "$feature" -- -Dwarnings -done \ No newline at end of file diff --git a/tests/scripts/check_glommio_features.sh b/tests/scripts/check_glommio_features.sh deleted file mode 100755 index 08a0d591..00000000 --- a/tests/scripts/check_glommio_features.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -e - -all_features=`yq -oy '.features["i-all"]' Cargo.toml | tr -d '\n' | sed -e 's/- / /g' | cut -c 2-` -redis_stack_features=`yq -oy '.features["i-redis-stack"]' Cargo.toml | tr -d '\n' | sed -e 's/- / /g' | cut -c 2-` - -for feature in $all_features; do - echo "Checking $feature" - cargo clippy --lib -p fred --no-default-features --features "glommio $feature" -- -Dwarnings -done - -for feature in $redis_stack_features; do - echo "Checking $feature" - cargo clippy --lib -p fred --no-default-features --features "glommio $feature" -- -Dwarnings -done \ No newline at end of file diff --git a/tests/scripts/clean-docker.sh b/tests/scripts/clean-docker.sh deleted file mode 100755 index 4e121d62..00000000 --- a/tests/scripts/clean-docker.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm -vf $(docker ps -aq) -docker rmi -f $(docker images -aq) \ No newline at end of file diff --git a/tests/scripts/create-cluster-tls.sh b/tests/scripts/create-cluster-tls.sh deleted file mode 100755 index feb23fd1..00000000 --- a/tests/scripts/create-cluster-tls.sh +++ /dev/null @@ -1,139 +0,0 @@ -#!/bin/bash - -# Settings -BIN_PATH="../../src/" -CREDS_PATH="../../../../creds" -CLUSTER_HOST=127.0.0.1 -PORT=30000 -TIMEOUT=2000 -NODES=6 -REPLICAS=1 -PROTECTED_MODE=yes -ADDITIONAL_OPTIONS="" - -# You may want to put the above config parameters into config.sh in order to -# override the defaults without modifying this script. - -if [ -a config.sh ] -then - source "config.sh" -fi - -# Computed vars -ENDPORT=$((PORT+NODES)) -CLIENT_TLS_ARGS="--tls --cert $CREDS_PATH/client.pem --key $CREDS_PATH/client.key --cacert $CREDS_PATH/ca.pem" - -if [ "$1" == "start" ] -then - while [ $((PORT < ENDPORT)) != "0" ]; do - PORT=$((PORT+1)) - echo "Starting $PORT" - $BIN_PATH/redis-server --port 0 \ - --protected-mode $PROTECTED_MODE \ - --cluster-enabled yes \ - --cluster-config-file nodes-${PORT}.conf \ - --cluster-node-timeout $TIMEOUT \ - --appendonly yes \ - --appendfilename appendonly-${PORT}.aof \ - --dbfilename dump-${PORT}.rdb \ - --logfile ${PORT}.log \ - --tls-port $PORT \ - --tls-cluster yes \ - --tls-replication yes \ - --tls-ca-cert-file ${CREDS_PATH}/ca.pem \ - --tls-key-file ${CREDS_PATH}/node-${PORT}.key \ - --tls-cert-file ${CREDS_PATH}/node-${PORT}.pem \ - --tls-client-cert-file ${CREDS_PATH}/client.pem \ - --tls-client-key-file ${CREDS_PATH}/client.key \ - --tls-auth-clients optional \ - --daemonize yes ${ADDITIONAL_OPTIONS} - done - exit 0 -fi - -if [ "$1" == "create" ] -then - HOSTS="" - while [ $((PORT < ENDPORT)) != "0" ]; do - PORT=$((PORT+1)) - HOSTS="$HOSTS $CLUSTER_HOST:$PORT" - done - OPT_ARG="" - if [ "$2" == "-f" ]; then - OPT_ARG="--cluster-yes" - fi - echo "$BIN_PATH/redis-cli $CLIENT_TLS_ARGS --cluster create $HOSTS --cluster-replicas $REPLICAS $OPT_ARG" - $BIN_PATH/redis-cli $CLIENT_TLS_ARGS --cluster create $HOSTS --cluster-replicas $REPLICAS $OPT_ARG - exit 0 -fi - -if [ "$1" == "stop" ] -then - while [ $((PORT < ENDPORT)) != "0" ]; do - PORT=$((PORT+1)) - echo "Stopping $PORT" - $BIN_PATH/redis-cli $CLIENT_TLS_ARGS -p $PORT shutdown nosave - done - exit 0 -fi - -if [ "$1" == "watch" ] -then - PORT=$((PORT+1)) - while [ 1 ]; do - clear - date - $BIN_PATH/redis-cli $CLIENT_TLS_ARGS -p $PORT cluster nodes | head -30 - sleep 1 - done - exit 0 -fi - -if [ "$1" == "tail" ] -then - INSTANCE=$2 - PORT=$((PORT+INSTANCE)) - tail -f ${PORT}.log - exit 0 -fi - -if [ "$1" == "tailall" ] -then - tail -f *.log - exit 0 -fi - -if [ "$1" == "call" ] -then - while [ $((PORT < ENDPORT)) != "0" ]; do - PORT=$((PORT+1)) - $BIN_PATH/redis-cli $CLIENT_TLS_ARGS -p $PORT $2 $3 $4 $5 $6 $7 $8 $9 - done - exit 0 -fi - -if [ "$1" == "clean" ] -then - rm -rf *.log - rm -rf appendonly*.aof - rm -rf dump*.rdb - rm -rf nodes*.conf - exit 0 -fi - -if [ "$1" == "clean-logs" ] -then - rm -rf *.log - exit 0 -fi - -echo "Usage: $0 [start|create|stop|watch|tail|clean|call]" -echo "start -- Launch Redis Cluster instances." -echo "create [-f] -- Create a cluster using redis-cli --cluster create." -echo "stop -- Stop Redis Cluster instances." -echo "watch -- Show CLUSTER NODES output (first 30 lines) of first node." -echo "tail -- Run tail -f of instance at base port + ID." -echo "tailall -- Run tail -f for all the log files at once." -echo "clean -- Remove all instances data, logs, configs." -echo "clean-logs -- Remove just instances logs." -echo "call -- Call a command (up to 7 arguments) on all nodes." diff --git a/tests/scripts/docker-install-redis-sentinel.sh b/tests/scripts/docker-install-redis-sentinel.sh deleted file mode 100755 index c0ca2888..00000000 --- a/tests/scripts/docker-install-redis-sentinel.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -if [ ! -d "./tests/tmp" ]; then - echo "Must be in application root for redis installation scripts to work." - exit 1 -fi - -declare -a arr=("REDIS_VERSION" "REDIS_USERNAME" "REDIS_PASSWORD" "REDIS_SENTINEL_PASSWORD") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -echo "Note: this requires docker, docker-compose, and redis >=6.2 to work reliably." -docker-compose -f ./tests/sentinel-docker-compose.yml up -d diff --git a/tests/scripts/failover-cluster-node.sh b/tests/scripts/failover-cluster-node.sh deleted file mode 100755 index eb0fadf1..00000000 --- a/tests/scripts/failover-cluster-node.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -. tests/scripts/utils.sh -check_root_dir - -argv=("$@") - -echo "Putting ${argv[0]} in fail state for ${argv[1]} seconds..." -./tests/tmp/redis_$REDIS_VERSION/redis-$REDIS_VERSION/src/redis-cli -p ${argv[0]} DEBUG sleep ${argv[1]} diff --git a/tests/scripts/full_install.sh b/tests/scripts/full_install.sh deleted file mode 100755 index 66499d4a..00000000 --- a/tests/scripts/full_install.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -declare -a arr=("REDIS_VERSION" "REDIS_USERNAME" "REDIS_PASSWORD" "REDIS_SENTINEL_PASSWORD") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -if [ ! -d "./tests/tmp" ]; then - echo "Must be in application root for installation script to work." - exit 1 -fi - -./tests/scripts/stop_all_redis.sh -rm -rf ./tests/tmp/redis* -./tests/scripts/install_redis_centralized.sh -./tests/scripts/install_redis_clustered.sh -./tests/scripts/docker-install-redis-sentinel.sh \ No newline at end of file diff --git a/tests/scripts/install_cargo_nextest.sh b/tests/scripts/install_cargo_nextest.sh deleted file mode 100755 index d2b12235..00000000 --- a/tests/scripts/install_cargo_nextest.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -if [ -z "$FRED_CI_NEXTEST" ]; then - echo "Skip installing cargo-nextest" -else - cargo install cargo-nextest -fi \ No newline at end of file diff --git a/tests/scripts/install_redis_centralized.sh b/tests/scripts/install_redis_centralized.sh deleted file mode 100755 index 0769a4bc..00000000 --- a/tests/scripts/install_redis_centralized.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -. tests/scripts/utils.sh - - -check_root_dir -check_redis -if [[ "$?" -eq 0 ]]; then - install_redis -fi -configure_centralized_acl -start_centralized - -echo "Finished installing centralized redis server." \ No newline at end of file diff --git a/tests/scripts/install_redis_clustered.sh b/tests/scripts/install_redis_clustered.sh deleted file mode 100755 index 654e7d2e..00000000 --- a/tests/scripts/install_redis_clustered.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -. tests/scripts/utils.sh - -check_root_dir -check_redis -if [[ "$?" -eq 0 ]]; then - install_redis -fi -start_cluster -echo "Finished installing clustered redis server." \ No newline at end of file diff --git a/tests/scripts/install_tls_cluster.sh b/tests/scripts/install_tls_cluster.sh deleted file mode 100755 index a4ca9b5f..00000000 --- a/tests/scripts/install_tls_cluster.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -. tests/scripts/utils.sh - -check_root_dir -check_redis -if [[ "$?" -eq 0 ]]; then - install_redis -fi - -modify_etc_hosts -generate_cluster_credentials -create_tls_cluster_config -start_cluster_tls -echo "Finished installing clustered redis server." \ No newline at end of file diff --git a/tests/scripts/lua/echo.lua b/tests/scripts/lua/echo.lua deleted file mode 100644 index e65f5aa7..00000000 --- a/tests/scripts/lua/echo.lua +++ /dev/null @@ -1,18 +0,0 @@ -#!lua name=echolib - -local function TableConcat(t1,t2) - for i=1,#t2 do - t1[#t1+1] = t2[i] - end - return t1 -end - -local function echo(keys, args) - return TableConcat(keys, args) -end - -redis.register_function{ - function_name='echo', - callback=echo, - flags={ 'no-writes' } -} \ No newline at end of file diff --git a/tests/scripts/lua/getset.lua b/tests/scripts/lua/getset.lua deleted file mode 100644 index a0b07059..00000000 --- a/tests/scripts/lua/getset.lua +++ /dev/null @@ -1,9 +0,0 @@ -#!lua name=getsetlib - -local function getset(keys, args) - local old = redis.call('GET', keys[1]) - local new = redis.call('SET', keys[1], args[1]) - return old -end - -redis.register_function('getset', getset) \ No newline at end of file diff --git a/tests/scripts/start_cluster.exp b/tests/scripts/start_cluster.exp deleted file mode 100755 index 7a3ece93..00000000 --- a/tests/scripts/start_cluster.exp +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/expect - -eval spawn ./create-cluster create -set prompt ":|#|\\\$" -interact -o -nobuffer -re $prompt return -send "yes\r" -interact - diff --git a/tests/scripts/stop_all_redis.sh b/tests/scripts/stop_all_redis.sh deleted file mode 100755 index a5ca89d9..00000000 --- a/tests/scripts/stop_all_redis.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -if [ ! -d "./tests/tmp" ]; then - echo "Must be in application root for redis installation scripts to work." - exit 1 -fi - -declare -a arr=("REDIS_VERSION" "REDIS_USERNAME" "REDIS_PASSWORD" "REDIS_SENTINEL_PASSWORD") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -echo "Stopping redis processes..." -pgrep redis | sudo xargs kill -9 - -echo "Stopping sentinel redis in docker..." -docker-compose -f ./tests/sentinel-docker-compose.yml stop \ No newline at end of file diff --git a/tests/scripts/tls-creds.sh b/tests/scripts/tls-creds.sh deleted file mode 100755 index c8deebdd..00000000 --- a/tests/scripts/tls-creds.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -. tests/scripts/utils.sh - -check_root_dir -generate_cluster_credentials \ No newline at end of file diff --git a/tests/scripts/tls/node-1.cnf b/tests/scripts/tls/node-1.cnf deleted file mode 100644 index 22d4ed3b..00000000 --- a/tests/scripts/tls/node-1.cnf +++ /dev/null @@ -1,20 +0,0 @@ -[req] -distinguished_name = req_distinguished_name -req_extensions = req_ext -prompt = no - -[req_distinguished_name] -C = US -ST = WA -CN = redis-cluster-tls-1 - -[req_ext] -subjectAltName = @alt_names - -[alt_names] -DNS.1 = redis-cluster-tls-1 -DNS.2 = redis-cluster-tls-2 -DNS.3 = redis-cluster-tls-3 -DNS.4 = redis-cluster-tls-4 -DNS.5 = redis-cluster-tls-5 -DNS.6 = redis-cluster-tls-6 \ No newline at end of file diff --git a/tests/scripts/tls/node-2.cnf b/tests/scripts/tls/node-2.cnf deleted file mode 100644 index ea435225..00000000 --- a/tests/scripts/tls/node-2.cnf +++ /dev/null @@ -1,20 +0,0 @@ -[req] -distinguished_name = req_distinguished_name -req_extensions = req_ext -prompt = no - -[req_distinguished_name] -C = US -ST = WA -CN = redis-cluster-tls-2 - -[req_ext] -subjectAltName = @alt_names - -[alt_names] -DNS.1 = redis-cluster-tls-1 -DNS.2 = redis-cluster-tls-2 -DNS.3 = redis-cluster-tls-3 -DNS.4 = redis-cluster-tls-4 -DNS.5 = redis-cluster-tls-5 -DNS.6 = redis-cluster-tls-6 \ No newline at end of file diff --git a/tests/scripts/tls/node-3.cnf b/tests/scripts/tls/node-3.cnf deleted file mode 100644 index e1ba7d4b..00000000 --- a/tests/scripts/tls/node-3.cnf +++ /dev/null @@ -1,20 +0,0 @@ -[req] -distinguished_name = req_distinguished_name -req_extensions = req_ext -prompt = no - -[req_distinguished_name] -C = US -ST = WA -CN = redis-cluster-tls-3 - -[req_ext] -subjectAltName = @alt_names - -[alt_names] -DNS.1 = redis-cluster-tls-1 -DNS.2 = redis-cluster-tls-2 -DNS.3 = redis-cluster-tls-3 -DNS.4 = redis-cluster-tls-4 -DNS.5 = redis-cluster-tls-5 -DNS.6 = redis-cluster-tls-6 \ No newline at end of file diff --git a/tests/scripts/tls/node-4.cnf b/tests/scripts/tls/node-4.cnf deleted file mode 100644 index c21e6110..00000000 --- a/tests/scripts/tls/node-4.cnf +++ /dev/null @@ -1,20 +0,0 @@ -[req] -distinguished_name = req_distinguished_name -req_extensions = req_ext -prompt = no - -[req_distinguished_name] -C = US -ST = WA -CN = redis-cluster-tls-4 - -[req_ext] -subjectAltName = @alt_names - -[alt_names] -DNS.1 = redis-cluster-tls-1 -DNS.2 = redis-cluster-tls-2 -DNS.3 = redis-cluster-tls-3 -DNS.4 = redis-cluster-tls-4 -DNS.5 = redis-cluster-tls-5 -DNS.6 = redis-cluster-tls-6 \ No newline at end of file diff --git a/tests/scripts/tls/node-5.cnf b/tests/scripts/tls/node-5.cnf deleted file mode 100644 index a2e89e39..00000000 --- a/tests/scripts/tls/node-5.cnf +++ /dev/null @@ -1,20 +0,0 @@ -[req] -distinguished_name = req_distinguished_name -req_extensions = req_ext -prompt = no - -[req_distinguished_name] -C = US -ST = WA -CN = redis-cluster-tls-5 - -[req_ext] -subjectAltName = @alt_names - -[alt_names] -DNS.1 = redis-cluster-tls-1 -DNS.2 = redis-cluster-tls-2 -DNS.3 = redis-cluster-tls-3 -DNS.4 = redis-cluster-tls-4 -DNS.5 = redis-cluster-tls-5 -DNS.6 = redis-cluster-tls-6 \ No newline at end of file diff --git a/tests/scripts/tls/node-6.cnf b/tests/scripts/tls/node-6.cnf deleted file mode 100644 index 2cb92764..00000000 --- a/tests/scripts/tls/node-6.cnf +++ /dev/null @@ -1,20 +0,0 @@ -[req] -distinguished_name = req_distinguished_name -req_extensions = req_ext -prompt = no - -[req_distinguished_name] -C = US -ST = WA -CN = redis-cluster-tls-6 - -[req_ext] -subjectAltName = @alt_names - -[alt_names] -DNS.1 = redis-cluster-tls-1 -DNS.2 = redis-cluster-tls-2 -DNS.3 = redis-cluster-tls-3 -DNS.4 = redis-cluster-tls-4 -DNS.5 = redis-cluster-tls-5 -DNS.6 = redis-cluster-tls-6 \ No newline at end of file diff --git a/tests/scripts/utils.sh b/tests/scripts/utils.sh deleted file mode 100644 index a6247662..00000000 --- a/tests/scripts/utils.sh +++ /dev/null @@ -1,229 +0,0 @@ -#!/bin/bash - -TLS_CLUSTER_PORT=30000 - -function check_root_dir { - if [ ! -d "./tests/tmp" ]; then - echo "Must be in application root for redis installation scripts to work." - exit 1 - fi -} - -declare -a arr=("REDIS_VERSION" "REDIS_USERNAME" "REDIS_PASSWORD" "REDIS_SENTINEL_PASSWORD") - -for env in "${arr[@]}" -do - if [ -z "$env" ]; then - echo "$env must be set. Run `source tests/environ` if needed." - exit 1 - fi -done - -ROOT=$PWD -[[ -z "${JOBS}" ]] && PARALLEL_JOBS='2' || PARALLEL_JOBS="${JOBS}" - -# Returns 0 if not installed, 1 otherwise. -function check_redis { - if [ -d "$ROOT/tests/tmp/redis_$REDIS_VERSION/redis-$REDIS_VERSION" ]; then - echo "Skipping redis install." - return 1 - else - echo "Redis install not found." - return 0 - fi -} - -function install_redis { - echo "Installing..." - pushd $ROOT > /dev/null - rm -rf tests/tmp/redis_cluster_$REDIS_VERSION - cd tests/tmp - - if [ -z "$USE_VALKEY" ]; then - echo "Installing Redis from redis.io" - curl -O "http://download.redis.io/releases/redis-$REDIS_VERSION.tar.gz" - else - echo "Installing valkey from github" - curl -O -L "https://github.com/valkey-io/valkey/archive/refs/tags/redis-$REDIS_VERSION.tar.gz" --output redis-$REDIS_VERSION.tar.gz - fi - - mkdir redis_$REDIS_VERSION - tar xf redis-$REDIS_VERSION.tar.gz -C redis_$REDIS_VERSION - rm redis-$REDIS_VERSION.tar.gz - - if [ -z "$USE_VALKEY" ]; then - cd redis_$REDIS_VERSION/redis-$REDIS_VERSION - else - mv redis_$REDIS_VERSION/valkey-redis-$REDIS_VERSION redis_$REDIS_VERSION/redis-$REDIS_VERSION - cd redis_$REDIS_VERSION/redis-$REDIS_VERSION - fi - - make BUILD_TLS=yes -j"${PARALLEL_JOBS}" - mv redis.conf redis.conf.bk - popd > /dev/null -} - -function configure_centralized_acl { - if [ -z "$REDIS_USERNAME" ]; then - echo "Skipping ACL setup due to missing REDIS_USERNAME..." - return - fi - if [ -z "$REDIS_PASSWORD" ]; then - echo "Skipping ACL setup due to missing REDIS_PASSWORD..." - return - fi - - echo "Configuring ACL rules..." - pushd $ROOT > /dev/null - cd tests/tmp/redis_$REDIS_VERSION/redis-$REDIS_VERSION - echo "user $REDIS_USERNAME on allkeys allcommands allchannels >$REDIS_PASSWORD" > ./test_users.acl - echo "aclfile `pwd`/test_users.acl" > ./redis_centralized.conf - popd > /dev/null -} - -function start_centralized { - pushd $ROOT > /dev/null - cd $ROOT/tests/tmp/redis_$REDIS_VERSION/redis-$REDIS_VERSION - - if [ -f "./redis_server.pid" ]; then - echo "Found running redis server. Stopping..." - kill -9 `cat ./redis_server.pid` - fi - - if [ -f "./redis_centralized.conf" ]; then - echo "Starting server with config file..." - nohup ./src/redis-server ./redis_centralized.conf > ./centralized_server.log 2>&1 & - else - echo "Starting server without config file..." - nohup ./src/redis-server > ./centralized_server.log 2>&1 & - fi - echo $! > ./redis_server.pid - echo "Redis server PID is `cat redis_server.pid`" - popd > /dev/null -} - -function enable_cluster_debug { - if [ -z "${CIRCLECI_TESTS}" ]; then - echo "Enabling DEBUG command on cluster (requires Redis version >=7)..." - pushd $ROOT > /dev/null - cd $ROOT/tests/tmp/redis_$REDIS_VERSION/redis-$REDIS_VERSION/utils/create-cluster - echo 'export ADDITIONAL_OPTIONS="--enable-debug-command yes"' > ./config.sh - - popd > /dev/null - fi -} - -function start_cluster { - echo "Creating and starting cluster..." - enable_cluster_debug - - pushd $ROOT > /dev/null - cd $ROOT/tests/tmp/redis_$REDIS_VERSION/redis-$REDIS_VERSION/utils/create-cluster - ./create-cluster stop - ./create-cluster clean - ./create-cluster start - ./create-cluster create -f - popd > /dev/null -} - -function start_cluster_tls { - echo "Creating and starting TLS cluster..." - pushd $ROOT > /dev/null - cd $ROOT/tests/tmp/redis_$REDIS_VERSION/redis-$REDIS_VERSION/utils/create-cluster-tls - ./create-cluster stop - ./create-cluster clean - ./create-cluster start - ./create-cluster create -f - popd > /dev/null - - echo "Cluster with TLS started on ports $((TLS_CLUSTER_PORT+1))-$((TLS_CLUSTER_PORT+6))" -} - -# Modify the /etc/hosts file to map the node-.example.com domains to localhost. -function modify_etc_hosts { - if [ -z "$CIRCLECI_TESTS" ]; then - read -p "Modify /etc/hosts with docker hostnames? [y/n]: " DNS_INPUT - if [ "$DNS_INPUT" = "y" ]; then - echo "Using sudo to modify /etc/hosts..." - else - return - fi - fi - - REDIS_HOSTS="127.0.0.1 redis-main redis-sentinel-1 redis-sentinel-2 redis-sentinel-3 redis-sentinel-main redis-sentinel-replica" - for i in `seq 1 6`; do - REDIS_HOSTS="$REDIS_HOSTS redis-cluster-$i redis-cluster-tls-$i" - done - - echo $REDIS_HOSTS | sudo tee -a /etc/hosts -} - -function check_cluster_credentials { - if [ -f "$ROOT/tests/tmp/creds/ca.pem" ]; then - echo "Skip generating TLS credentials." - return 1 - else - echo "TLS credentials not found." - return 0 - fi -} - -# Generate creds for a CA, a cert/key for the client, a cert/key for each node in the cluster, and sign the certs with the CA creds. -# -# Note: it's also necessary to modify DNS mappings so the CN in each cert can be used as a hostname. See `modify_etc_hosts`. -function generate_cluster_credentials { - echo "Generating keys..." - if [ ! -d "$ROOT/tests/tmp/creds" ]; then - mkdir -p $ROOT/tests/tmp/creds - fi - pushd $ROOT > /dev/null - cd $ROOT/tests/tmp/creds - rm -rf ./* - - echo "Generating CA key pair..." - openssl req -new -newkey rsa:2048 -nodes -out ca.csr -keyout ca.key -subj '/CN=redis-cluster' - openssl x509 -signkey ca.key -days 90 -req -in ca.csr -out ca.pem - # need the CA cert in DER format for rustls - openssl x509 -outform der -in ca.pem -out ca.crt - - echo "Generating client key pair..." - # native-tls wants a PKCS#8 key and redis-cli wants a PKCS#1 key - openssl genrsa -out client.key 2048 - openssl pkey -in client.key -out client.key8 - # rustls needs it in DER format - openssl rsa -in client.key -inform pem -out client_key.der -outform der - - openssl req -new -key client.key -out client.csr -subj '/CN=client.redis-cluster' - openssl x509 -req -days 90 -sha256 -in client.csr -CA ca.pem -CAkey ca.key -set_serial 01 -out client.pem - # need the client cert in DER format for rustls - openssl x509 -outform der -in client.pem -out client.crt - - echo "Generating key pairs for each cluster node..." - for i in `seq 1 6`; do - # redis-server wants a PKCS#1 key - openssl genrsa -out "node-$i.key" 2048 - # create SAN entries for all the other nodes - openssl req -new -key "node-$i.key" -out "node-$i.csr" -config "$ROOT/tests/scripts/tls/node-$i.cnf" - # might not work on os x with native-tls (https://github.com/sfackler/rust-native-tls/issues/143) - openssl x509 -req -days 90 -sha256 -in "node-$i.csr" -CA ca.pem -CAkey ca.key -set_serial 01 -out "node-$i.pem" \ - -extensions req_ext -extfile "$ROOT/tests/scripts/tls/node-$i.cnf" - done - - chmod +r ./* - TLS_CREDS_PATH=$PWD - popd > /dev/null -} - -function create_tls_cluster_config { - echo "Creating cluster configuration file..." - pushd $ROOT > /dev/null - cd $ROOT/tests/tmp/redis_$REDIS_VERSION/redis-$REDIS_VERSION - rm -rf utils/create-cluster-tls - cp -rf utils/create-cluster utils/create-cluster-tls - cp $ROOT/tests/scripts/create-cluster-tls.sh utils/create-cluster-tls/create-cluster - cd utils/create-cluster-tls - chmod +x ./create-cluster - echo "" > config.sh - - popd > /dev/null -} \ No newline at end of file diff --git a/tests/tmp/.gitkeep b/tests/tmp/.gitkeep deleted file mode 100644 index e69de29b..00000000