mco_redis_rs/
lib.rs

1//! redis-rs is a rust implementation of a Redis client library.  It exposes
2//! a general purpose interface to Redis and also provides specific helpers for
3//! commonly used functionality.
4//!
5//! The crate is called `redis` and you can depend on it via cargo:
6//!
7//! ```ini
8//! [dependencies.redis]
9//! version = "*"
10//! ```
11//!
12//! If you want to use the git version:
13//!
14//! ```ini
15//! [dependencies.redis]
16//! git = "https://github.com/mitsuhiko/redis-rs.git"
17//! ```
18//!
19//! # Basic Operation
20//!
21//! redis-rs exposes two API levels: a low- and a high-level part.
22//! The high-level part does not expose all the functionality of redis and
23//! might take some liberties in how it speaks the protocol.  The low-level
24//! part of the API allows you to express any request on the redis level.
25//! You can fluently switch between both API levels at any point.
26//!
27//! ## Connection Handling
28//!
29//! For connecting to redis you can use a client object which then can produce
30//! actual connections.  Connections and clients as well as results of
31//! connections and clients are considered `ConnectionLike` objects and
32//! can be used anywhere a request is made.
33//!
34//! The full canonical way to get a connection is to create a client and
35//! to ask for a connection from it:
36//!
37//! ```rust,no_run
38//! extern crate redis;
39//!
40//! fn do_something() -> redis::RedisResult<()> {
41//!     let client = redis::Client::open("redis://127.0.0.1/")?;
42//!     let mut con = client.get_connection()?;
43//!
44//!     /* do something here */
45//!
46//!     Ok(())
47//! }
48//! ```
49//!
50//! ## Optional Features
51//!
52//! There are a few features defined that can enable additional functionality
53//! if so desired.  Some of them are turned on by default.
54//!
55//! * `acl`: enables acl support (enabled by default)
56//! * `aio`: enables async IO support (enabled by default)
57//! * `geospatial`: enables geospatial support (enabled by default)
58//! * `script`: enables script support (enabled by default)
59//! * `r2d2`: enables r2d2 connection pool support (optional)
60//! * `cluster`: enables redis cluster support (optional)
61//! * `tokio-comp`: enables support for tokio (optional)
62//! * `connection-manager`: enables support for automatic reconnection (optional)
63//!
64//! ## Connection Parameters
65//!
66//! redis-rs knows different ways to define where a connection should
67//! go.  The parameter to `Client::open` needs to implement the
68//! `IntoConnectionInfo` trait of which there are three implementations:
69//!
70//! * string slices in `redis://` URL format.
71//! * URL objects from the redis-url crate.
72//! * `ConnectionInfo` objects.
73//!
74//! The URL format is `redis://[<username>][:<password>@]<hostname>[:port][/<db>]`
75//!
76//! If Unix socket support is available you can use a unix URL in this format:
77//!
78//! `redis+unix:///<path>[?db=<db>[&pass=<password>][&user=<username>]]`
79//!
80//! For compatibility with some other redis libraries, the "unix" scheme
81//! is also supported:
82//!
83//! `unix:///<path>[?db=<db>][&pass=<password>][&user=<username>]]`
84//!
85//! ## Executing Low-Level Commands
86//!
87//! To execute low-level commands you can use the `cmd` function which allows
88//! you to build redis requests.  Once you have configured a command object
89//! to your liking you can send a query into any `ConnectionLike` object:
90//!
91//! ```rust,no_run
92//! fn do_something(con: &mut redis::Connection) -> redis::RedisResult<()> {
93//!     let _ : () = redis::cmd("SET").arg("my_key").arg(42).query(con)?;
94//!     Ok(())
95//! }
96//! ```
97//!
98//! Upon querying the return value is a result object.  If you do not care
99//! about the actual return value (other than that it is not a failure)
100//! you can always type annotate it to the unit type `()`.
101//!
102//! Note that commands with a sub-command (like "MEMORY USAGE", "ACL WHOAMI",
103//! "LATENCY HISTORY", etc) must specify the sub-command as a separate `arg`:
104//!
105//! ```rust,no_run
106//! fn do_something(con: &mut redis::Connection) -> redis::RedisResult<usize> {
107//!     // This will result in a server error: "unknown command `MEMORY USAGE`"
108//!     // because "USAGE" is technically a sub-command of "MEMORY".
109//!     redis::cmd("MEMORY USAGE").arg("my_key").query(con)?;
110//!
111//!     // However, this will work as you'd expect
112//!     redis::cmd("MEMORY").arg("USAGE").arg("my_key").query(con)
113//! }
114//! ```
115//!
116//! ## Executing High-Level Commands
117//!
118//! The high-level interface is similar.  For it to become available you
119//! need to use the `Commands` trait in which case all `ConnectionLike`
120//! objects the library provides will also have high-level methods which
121//! make working with the protocol easier:
122//!
123//! ```rust,no_run
124//! extern crate redis;
125//! use redis::Commands;
126//!
127//! fn do_something(con: &mut redis::Connection) -> redis::RedisResult<()> {
128//!     let _ : () = con.set("my_key", 42)?;
129//!     Ok(())
130//! }
131//! ```
132//!
133//! Note that high-level commands are work in progress and many are still
134//! missing!
135//!
136//! ## Type Conversions
137//!
138//! Because redis inherently is mostly type-less and the protocol is not
139//! exactly friendly to developers, this library provides flexible support
140//! for casting values to the intended results.  This is driven through the `FromRedisValue` and `ToRedisArgs` traits.
141//!
142//! The `arg` method of the command will accept a wide range of types through
143//! the `ToRedisArgs` trait and the `query` method of a command can convert the
144//! value to what you expect the function to return through the `FromRedisValue`
145//! trait.  This is quite flexible and allows vectors, tuples, hashsets, hashmaps
146//! as well as optional values:
147//!
148//! ```rust,no_run
149//! # use redis::Commands;
150//! # use std::collections::{HashMap, HashSet};
151//! # fn do_something() -> redis::RedisResult<()> {
152//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
153//! # let mut con = client.get_connection().unwrap();
154//! let count : i32 = con.get("my_counter")?;
155//! let count = con.get("my_counter").unwrap_or(0i32);
156//! let k : Option<String> = con.get("missing_key")?;
157//! let name : String = con.get("my_name")?;
158//! let bin : Vec<u8> = con.get("my_binary")?;
159//! let map : HashMap<String, i32> = con.hgetall("my_hash")?;
160//! let keys : Vec<String> = con.hkeys("my_hash")?;
161//! let mems : HashSet<i32> = con.smembers("my_set")?;
162//! let (k1, k2) : (String, String) = con.get(&["k1", "k2"])?;
163//! # Ok(())
164//! # }
165//! ```
166//!
167//! # Iteration Protocol
168//!
169//! In addition to sending a single query, iterators are also supported.  When
170//! used with regular bulk responses they don't give you much over querying and
171//! converting into a vector (both use a vector internally) but they can also
172//! be used with `SCAN` like commands in which case iteration will send more
173//! queries until the cursor is exhausted:
174//!
175//! ```rust,no_run
176//! # fn do_something() -> redis::RedisResult<()> {
177//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
178//! # let mut con = client.get_connection().unwrap();
179//! let mut iter : redis::Iter<isize> = redis::cmd("SSCAN").arg("my_set")
180//!     .cursor_arg(0).clone().iter(&mut con)?;
181//! for x in iter {
182//!     // do something with the item
183//! }
184//! # Ok(()) }
185//! ```
186//!
187//! As you can see the cursor argument needs to be defined with `cursor_arg`
188//! instead of `arg` so that the library knows which argument needs updating
189//! as the query is run for more items.
190//!
191//! # Pipelining
192//!
193//! In addition to simple queries you can also send command pipelines.  This
194//! is provided through the `pipe` function.  It works very similar to sending
195//! individual commands but you can send more than one in one go.  This also
196//! allows you to ignore individual results so that matching on the end result
197//! is easier:
198//!
199//! ```rust,no_run
200//! # fn do_something() -> redis::RedisResult<()> {
201//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
202//! # let mut con = client.get_connection().unwrap();
203//! let (k1, k2) : (i32, i32) = redis::pipe()
204//!     .cmd("SET").arg("key_1").arg(42).ignore()
205//!     .cmd("SET").arg("key_2").arg(43).ignore()
206//!     .cmd("GET").arg("key_1")
207//!     .cmd("GET").arg("key_2").query(&mut con)?;
208//! # Ok(()) }
209//! ```
210//!
211//! If you want the pipeline to be wrapped in a `MULTI`/`EXEC` block you can
212//! easily do that by switching the pipeline into `atomic` mode.  From the
213//! caller's point of view nothing changes, the pipeline itself will take
214//! care of the rest for you:
215//!
216//! ```rust,no_run
217//! # fn do_something() -> redis::RedisResult<()> {
218//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
219//! # let mut con = client.get_connection().unwrap();
220//! let (k1, k2) : (i32, i32) = redis::pipe()
221//!     .atomic()
222//!     .cmd("SET").arg("key_1").arg(42).ignore()
223//!     .cmd("SET").arg("key_2").arg(43).ignore()
224//!     .cmd("GET").arg("key_1")
225//!     .cmd("GET").arg("key_2").query(&mut con)?;
226//! # Ok(()) }
227//! ```
228//!
229//! You can also use high-level commands on pipelines:
230//!
231//! ```rust,no_run
232//! # fn do_something() -> redis::RedisResult<()> {
233//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
234//! # let mut con = client.get_connection().unwrap();
235//! let (k1, k2) : (i32, i32) = redis::pipe()
236//!     .atomic()
237//!     .set("key_1", 42).ignore()
238//!     .set("key_2", 43).ignore()
239//!     .get("key_1")
240//!     .get("key_2").query(&mut con)?;
241//! # Ok(()) }
242//! ```
243//!
244//! # Transactions
245//!
246//! Transactions are available through atomic pipelines.  In order to use
247//! them in a more simple way you can use the `transaction` function of a
248//! connection:
249//!
250//! ```rust,no_run
251//! # fn do_something() -> redis::RedisResult<()> {
252//! use redis::Commands;
253//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
254//! # let mut con = client.get_connection().unwrap();
255//! let key = "the_key";
256//! let (new_val,) : (isize,) = redis::transaction(&mut con, &[key], |con, pipe| {
257//!     let old_val : isize = con.get(key)?;
258//!     pipe
259//!         .set(key, old_val + 1).ignore()
260//!         .get(key).query(con)
261//! })?;
262//! println!("The incremented number is: {}", new_val);
263//! # Ok(()) }
264//! ```
265//!
266//! For more information see the `transaction` function.
267//!
268//! # PubSub
269//!
270//! Pubsub is currently work in progress but provided through the `PubSub`
271//! connection object.  Due to the fact that Rust does not have support
272//! for async IO in libnative yet, the API does not provide a way to
273//! read messages with any form of timeout yet.
274//!
275//! Example usage:
276//!
277//! ```rust,no_run
278//! # fn do_something() -> redis::RedisResult<()> {
279//! let client = redis::Client::open("redis://127.0.0.1/")?;
280//! let mut con = client.get_connection()?;
281//! let mut pubsub = con.as_pubsub();
282//! pubsub.subscribe("channel_1")?;
283//! pubsub.subscribe("channel_2")?;
284//!
285//! loop {
286//!     let msg = pubsub.get_message()?;
287//!     let payload : String = msg.get_payload()?;
288//!     println!("channel '{}': {}", msg.get_channel_name(), payload);
289//! }
290//! # }
291//! ```
292//!
293#![cfg_attr(
294    feature = "script",
295    doc = r##"
296# Scripts
297
298Lua scripts are supported through the `Script` type in a convenient
299way (it does not support pipelining currently).  It will automatically
300load the script if it does not exist and invoke it.
301
302Example:
303
304```rust,no_run
305# fn do_something() -> redis::RedisResult<()> {
306# let client = redis::Client::open("redis://127.0.0.1/").unwrap();
307# let mut con = client.get_connection().unwrap();
308let script = redis::Script::new(r"
309    return tonumber(ARGV[1]) + tonumber(ARGV[2]);
310");
311let result : isize = script.arg(1).arg(2).invoke(&mut con)?;
312assert_eq!(result, 3);
313# Ok(()) }
314```
315"##
316)]
317//!
318#![cfg_attr(
319    feature = "aio",
320    doc = r##"
321# Async
322
323In addition to the synchronous interface that's been explained above there also exists an
324asynchronous interface based on [`futures`][] and [`tokio`][].
325
326This interface exists under the `aio` (async io) module and largely mirrors the synchronous
327with a few concessions to make it fit the constraints of `futures`.
328
329```rust,no_run
330use futures::prelude::*;
331use redis::AsyncCommands;
332
333# #[tokio::main]
334# async fn main() -> redis::RedisResult<()> {
335let client = redis::Client::open("redis://127.0.0.1/").unwrap();
336let mut con = client.get_async_connection().await?;
337
338con.set("key1", b"foo").await?;
339
340redis::cmd("SET").arg(&["key2", "bar"]).query_async(&mut con).await?;
341
342let result = redis::cmd("MGET")
343 .arg(&["key1", "key2"])
344 .query_async(&mut con)
345 .await;
346assert_eq!(result, Ok(("foo".to_string(), b"bar".to_vec())));
347# Ok(()) }
348```
349"##
350)]
351//!
352//! [`futures`]:https://crates.io/crates/futures
353//! [`tokio`]:https://tokio.rs
354
355#![deny(non_camel_case_types)]
356#![warn(missing_docs)]
357#![cfg_attr(docsrs, warn(broken_intra_doc_links))]
358#![cfg_attr(docsrs, feature(doc_cfg))]
359
360// public api
361pub use crate::client::Client;
362pub use crate::cmd::{cmd, pack_command, pipe, Arg, Cmd, Iter};
363pub use crate::commands::{Commands, ControlFlow, LposOptions, PubSubCommands};
364pub use crate::connection::{
365    parse_redis_url, transaction, Connection, ConnectionAddr, ConnectionInfo, ConnectionLike,
366    IntoConnectionInfo, Msg, PubSub, RedisConnectionInfo,
367};
368pub use crate::parser::{parse_redis_value, Parser};
369pub use crate::pipeline::Pipeline;
370
371#[cfg(feature = "script")]
372#[cfg_attr(docsrs, doc(cfg(feature = "script")))]
373pub use crate::script::{Script, ScriptInvocation};
374
375pub use crate::types::{
376    // utility functions
377    from_redis_value,
378
379    // error kinds
380    ErrorKind,
381
382    // conversion traits
383    FromRedisValue,
384
385    // utility types
386    InfoDict,
387    NumericBehavior,
388
389    // error and result types
390    RedisError,
391    RedisResult,
392    RedisWrite,
393    ToRedisArgs,
394
395    // low level values
396    Value,
397};
398
399#[cfg(feature = "aio")]
400#[cfg_attr(docsrs, doc(cfg(feature = "aio")))]
401pub use crate::{
402    cmd::AsyncIter, commands::AsyncCommands, parser::parse_redis_value_async, types::RedisFuture,
403};
404
405mod macros;
406mod pipeline;
407
408#[cfg(feature = "acl")]
409#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
410pub mod acl;
411
412#[cfg(feature = "aio")]
413#[cfg_attr(docsrs, doc(cfg(feature = "aio")))]
414pub mod aio;
415
416#[cfg(feature = "geospatial")]
417#[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
418pub mod geo;
419
420#[cfg(feature = "cluster")]
421#[cfg_attr(docsrs, doc(cfg(feature = "cluster")))]
422pub mod cluster;
423
424#[cfg(feature = "cluster")]
425mod cluster_client;
426
427#[cfg(feature = "cluster")]
428mod cluster_pipeline;
429
430#[cfg(feature = "cluster")]
431mod cluster_routing;
432
433#[cfg(feature = "r2d2")]
434#[cfg_attr(docsrs, doc(cfg(feature = "r2d2")))]
435mod r2d2;
436
437#[cfg(feature = "streams")]
438#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
439pub mod streams;
440
441mod client;
442mod cmd;
443mod commands;
444mod connection;
445mod parser;
446mod script;
447mod types;