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
#![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)]
#![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")]

#[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(feature = "enable-rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "enable-rustls")))]
pub extern crate rustls;
#[cfg(feature = "enable-rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "enable-rustls")))]
pub extern crate rustls_native_certs;
#[cfg(feature = "serde-json")]
pub extern crate serde_json;
pub extern crate socket2;
#[cfg(feature = "codec")]
#[cfg_attr(docsrs, doc(cfg(feature = "codec")))]
pub extern crate tokio_util;
#[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;

/// Codecs for use with the [tokio codec](https://docs.rs/tokio-util/latest/tokio_util/codec/index.html) interface.
#[cfg(feature = "codec")]
#[cfg_attr(docsrs, doc(cfg(feature = "codec")))]
pub mod codec {
  pub use super::protocol::public::*;
}

/// Utility functions used by the client that may also be useful to callers.
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"))]
  #[cfg_attr(docsrs, doc(cfg(any(feature = "enable-rustls", feature = "enable-native-tls"))))]
  pub use crate::types::{TlsConfig, TlsConnector};
}