From df68c195c072bfa97a6915f3f62f3149c9ec9638 Mon Sep 17 00:00:00 2001 From: Shachar Langbeheim Date: Fri, 6 Jun 2025 12:29:51 +0300 Subject: [PATCH 1/3] Prepare 1.0.x branch for usage. --- .github/workflows/rust.yml | 4 ++-- Cargo.lock | 2 +- README.md | 32 ++++++++++++++++---------------- redis-test/Cargo.toml | 4 ++-- redis/Cargo.toml | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f1edc7ec3..bbda5044d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,9 +2,9 @@ name: Rust on: push: - branches: [ main, 0.*.x ] + branches: [ main, 0.*.x, 1.*.x ] pull_request: - branches: [ main, 0.*.x ] + branches: [ main, 0.*.x, 1.*.x ] env: CARGO_TERM_COLOR: always diff --git a/Cargo.lock b/Cargo.lock index f12d4faba..ce58fbefb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1917,7 +1917,7 @@ dependencies = [ [[package]] name = "redis" -version = "0.32.0" +version = "1.0.0-alpha" dependencies = [ "ahash 0.8.11", "anyhow", diff --git a/README.md b/README.md index 25317c28b..42a4a614b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The crate is called `redis` and you can depend on it via cargo: ```ini [dependencies] -redis = "0.32.0" +redis = "1.0" ``` Documentation on the library can be found at @@ -75,13 +75,13 @@ To enable asynchronous clients, enable the relevant feature in your Cargo.toml, ``` # if you use tokio -redis = { version = "0.32.0", features = ["tokio-comp"] } +redis = { version = "1.0", features = ["tokio-comp"] } # if you use smol -redis = { version = "0.32.0", features = ["smol-comp"] } +redis = { version = "1.0", features = ["smol-comp"] } # if you use async-std -redis = { version = "0.32.0", features = ["async-std-comp"] } +redis = { version = "1.0", features = ["async-std-comp"] } ``` You can then use either the `AsyncCommands` or `AsyncTypedCommands` trait. @@ -92,7 +92,7 @@ When using a sync connection, it is recommended to use a connection pool in orde disconnects or multi-threaded usage. This can be done using the `r2d2` feature. ``` -redis = { version = "0.32.0", features = ["r2d2"] } +redis = { version = "1.0", features = ["r2d2"] } ``` For async connections, connection pooling isn't necessary, unless blocking commands are used. @@ -114,31 +114,31 @@ Currently, `native-tls` and `rustls` are supported. To use `native-tls`: ``` -redis = { version = "0.32.0", features = ["tls-native-tls"] } +redis = { version = "1.0", features = ["tls-native-tls"] } # if you use tokio -redis = { version = "0.32.0", features = ["tokio-native-tls-comp"] } +redis = { version = "1.0", features = ["tokio-native-tls-comp"] } # if you use smol -redis = { version = "0.32.0", features = ["smol-native-tls-comp"] } +redis = { version = "1.0", features = ["smol-native-tls-comp"] } # if you use async-std -redis = { version = "0.32.0", features = ["async-std-native-tls-comp"] } +redis = { version = "1.0", features = ["async-std-native-tls-comp"] } ``` To use `rustls`: ``` -redis = { version = "0.32.0", features = ["tls-rustls"] } +redis = { version = "1.0", features = ["tls-rustls"] } # if you use tokio -redis = { version = "0.32.0", features = ["tokio-rustls-comp"] } +redis = { version = "1.0", features = ["tokio-rustls-comp"] } # if you use smol -redis = { version = "0.32.0", features = ["smol-rustls-comp"] } +redis = { version = "1.0", features = ["smol-rustls-comp"] } # if you use async-std -redis = { version = "0.32.0", features = ["async-std-rustls-comp"] } +redis = { version = "1.0", features = ["async-std-rustls-comp"] } ``` Add `rustls` to dependencies @@ -179,7 +179,7 @@ let client = redis::Client::open("rediss://127.0.0.1/#insecure")?; Support for Redis Cluster can be enabled by enabling the `cluster` feature in your Cargo.toml: -`redis = { version = "0.32.0", features = [ "cluster"] }` +`redis = { version = "1.0", features = [ "cluster"] }` Then you can simply use the `ClusterClient`, which accepts a list of available nodes. Note that only one node in the cluster needs to be specified when instantiating the client, though @@ -202,7 +202,7 @@ fn fetch_an_integer() -> String { Async Redis Cluster support can be enabled by enabling the `cluster-async` feature, along with your preferred async runtime, e.g.: -`redis = { version = "0.32.0", features = [ "cluster-async", "tokio-std-comp" ] }` +`redis = { version = "1.0", features = [ "cluster-async", "tokio-std-comp" ] }` ```rust use redis::cluster::ClusterClient; @@ -222,7 +222,7 @@ async fn fetch_an_integer() -> String { Support for the RedisJSON Module can be enabled by specifying "json" as a feature in your Cargo.toml. -`redis = { version = "0.32.0", features = ["json"] }` +`redis = { version = "1.0", features = ["json"] }` Then you can simply import the `JsonCommands` trait which will add the `json` commands to all Redis Connections (not to be confused with just `Commands` which only adds the default commands) diff --git a/redis-test/Cargo.toml b/redis-test/Cargo.toml index 44fd482d1..c74acdc72 100644 --- a/redis-test/Cargo.toml +++ b/redis-test/Cargo.toml @@ -13,7 +13,7 @@ rust-version = "1.75" bench = false [dependencies] -redis = { version = "0.32", path = "../redis" } +redis = { version = "1.0.0-alpha", path = "../redis" } bytes = { version = "1", optional = true } futures = { version = "0.3", optional = true } tempfile = "=3.20.0" @@ -24,7 +24,7 @@ rand = "0.9" aio = ["futures", "redis/aio"] [dev-dependencies] -redis = { version = "0.32", path = "../redis", features = [ +redis = { version = "1.0.0-alpha", path = "../redis", features = [ "aio", "tokio-comp", "safe_iterators", diff --git a/redis/Cargo.toml b/redis/Cargo.toml index 91496eaf8..076fdfe74 100644 --- a/redis/Cargo.toml +++ b/redis/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "redis" -version = "0.32.0" +version = "1.0.0-alpha" keywords = ["redis", "valkey", "cluster", "sentinel", "pubsub"] description = "Redis driver for Rust." homepage = "https://github.com/redis-rs/redis-rs" From 2650b686b986e9fb91232e1f698ec420856f9c39 Mon Sep 17 00:00:00 2001 From: Shachar Langbeheim Date: Fri, 6 Jun 2025 12:31:04 +0300 Subject: [PATCH 2/3] t --- redis/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/redis/Cargo.toml b/redis/Cargo.toml index 076fdfe74..b3fed2ffb 100644 --- a/redis/Cargo.toml +++ b/redis/Cargo.toml @@ -154,7 +154,6 @@ disable-client-setinfo = [] cache-aio = ["aio", "dep:lru"] r2d2 = ["dep:r2d2"] bb8 = ["dep:bb8"] -safe_iterators = [] # Deprecated features tls = ["tls-native-tls"] # use "tls-native-tls" instead From e2f9741c917ac376c0d4fa4814e0e3c8c52565d3 Mon Sep 17 00:00:00 2001 From: Shachar Langbeheim Date: Fri, 6 Jun 2025 12:42:00 +0300 Subject: [PATCH 3/3] Remove unsafe iterators. --- Makefile | 13 +++------ config/global.toml | 4 +-- config/redis.toml | 5 ---- redis-test/Cargo.toml | 1 - redis/examples/async-scan.rs | 3 -- redis/examples/basic.rs | 17 +++-------- redis/src/cmd.rs | 55 +----------------------------------- redis/src/lib.rs | 5 ++++ redis/tests/test_async.rs | 7 ----- redis/tests/test_basic.rs | 5 ---- 10 files changed, 16 insertions(+), 99 deletions(-) diff --git a/Makefile b/Makefile index 571727624..23b9bd983 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ build: - @RUSTFLAGS="-D warnings" cargo build -F safe_iterators --locked -p redis + @RUSTFLAGS="-D warnings" cargo build --locked -p redis test: @echo "====================================================================" @@ -8,14 +8,9 @@ test: @RUSTFLAGS="-D warnings" cargo build --locked -p redis -p redis-test --all-features @echo "====================================================================" - @echo "Testing Connection Type TCP without features except safe_iterators" + @echo "Testing Connection Type TCP without features" @echo "====================================================================" - @RUSTFLAGS="-D warnings" REDISRS_SERVER_TYPE=tcp RUST_BACKTRACE=1 cargo nextest run --locked -p redis --no-default-features -F safe_iterators -E 'not test(test_module)' - - @echo "====================================================================" - @echo "Testing Connection Type TCP without safe_iterators, but with async" - @echo "====================================================================" - @RUSTFLAGS="-D warnings -A deprecated" REDISRS_SERVER_TYPE=tcp RUST_BACKTRACE=1 cargo nextest run --locked -p redis --no-default-features -F tokio-comp -E 'not test(test_module)' + @RUSTFLAGS="-D warnings" REDISRS_SERVER_TYPE=tcp RUST_BACKTRACE=1 cargo nextest run --locked -p redis --no-default-features -E 'not test(test_module)' @echo "====================================================================" @echo "Testing Connection Type TCP with all features and RESP2" @@ -35,7 +30,7 @@ test: @echo "====================================================================" @echo "Testing Connection Type TCP with native-TLS support" @echo "====================================================================" - @RUSTFLAGS="-D warnings" REDISRS_SERVER_TYPE=tcp+tls RUST_BACKTRACE=1 cargo nextest run --locked -p redis --features=json,tokio-native-tls-comp,async-std-native-tls-comp,smol-native-tls-comp,connection-manager,cluster-async,safe_iterators -E 'not test(test_module)' + @RUSTFLAGS="-D warnings" REDISRS_SERVER_TYPE=tcp+tls RUST_BACKTRACE=1 cargo nextest run --locked -p redis --features=json,tokio-native-tls-comp,async-std-native-tls-comp,smol-native-tls-comp,connection-manager,cluster-async -E 'not test(test_module)' @echo "====================================================================" @echo "Testing Connection Type UNIX" diff --git a/config/global.toml b/config/global.toml index ad34d2575..00fae7858 100644 --- a/config/global.toml +++ b/config/global.toml @@ -1,2 +1,2 @@ -max_combo_size=4 -skip_optional_deps=false \ No newline at end of file +max_combo_size = 3 +skip_optional_deps = false diff --git a/config/redis.toml b/config/redis.toml index 8344308e0..bdcd448fa 100644 --- a/config/redis.toml +++ b/config/redis.toml @@ -138,8 +138,3 @@ forbid = [ "OR", "num-bigint", ] - -# Since the omission of the feature `safe_iterators` will produce deprecation errors, it is always required. -[[rule]] -when = true -require = ["safe_iterators"] diff --git a/redis-test/Cargo.toml b/redis-test/Cargo.toml index c74acdc72..0e1ac4674 100644 --- a/redis-test/Cargo.toml +++ b/redis-test/Cargo.toml @@ -27,7 +27,6 @@ aio = ["futures", "redis/aio"] redis = { version = "1.0.0-alpha", path = "../redis", features = [ "aio", "tokio-comp", - "safe_iterators", ] } tokio = { version = "1", features = [ "rt", diff --git a/redis/examples/async-scan.rs b/redis/examples/async-scan.rs index 668d6853f..c7c9f770e 100644 --- a/redis/examples/async-scan.rs +++ b/redis/examples/async-scan.rs @@ -10,9 +10,6 @@ async fn main() -> redis::RedisResult<()> { let _: () = con.set("async-key2", b"foo").await?; let iter: AsyncIter = con.scan().await?; - #[cfg(not(feature = "safe_iterators"))] - let mut keys: Vec = iter.collect().await; - #[cfg(feature = "safe_iterators")] let mut keys: Vec<_> = iter.map(std::result::Result::unwrap).collect().await; keys.sort(); diff --git a/redis/examples/basic.rs b/redis/examples/basic.rs index 00d5dbd83..776c47e21 100644 --- a/redis/examples/basic.rs +++ b/redis/examples/basic.rs @@ -62,19 +62,10 @@ fn do_show_scanning(con: &mut redis::Connection) -> redis::RedisResult<()> { let iter = cmd.iter::(con)?; // as a simple exercise we just sum up the iterator. - let sum: i32 = { - #[cfg(feature = "safe_iterators")] - { - let mut sum = 0; - for result in iter { - sum += result?; - } - sum - } - - #[cfg(not(feature = "safe_iterators"))] - iter.sum() - }; + let mut sum = 0; + for result in iter { + sum += result?; + } println!("The sum of all numbers in the set 0-1000: {sum}"); diff --git a/redis/src/cmd.rs b/redis/src/cmd.rs index ac672399c..3a7a3feb6 100644 --- a/redis/src/cmd.rs +++ b/redis/src/cmd.rs @@ -86,30 +86,10 @@ pub struct Cmd { cache: Option, } -#[cfg_attr( - not(feature = "safe_iterators"), - deprecated( - note = "Deprecated due to the fact that this implementation silently stops at the first value that can't be converted to T. Enable the feature `safe_iterators` for a safe version." - ) -)] /// Represents a redis iterator. pub struct Iter<'a, T: FromRedisValue> { iter: CheckedIter<'a, T>, } - -#[cfg(not(feature = "safe_iterators"))] -impl Iterator for Iter<'_, T> { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - // use the checked iterator, but keep the behavior of the deprecated - // iterator. This will return silently `None` if an error occurs. - self.iter.next()?.ok() - } -} - -#[cfg(feature = "safe_iterators")] impl Iterator for Iter<'_, T> { type Item = RedisResult; @@ -180,12 +160,6 @@ enum IterOrFuture<'a, T: FromRedisValue + 'a> { /// Represents a redis iterator that can be used with async connections. #[cfg(feature = "aio")] -#[cfg_attr( - all(feature = "aio", not(feature = "safe_iterators")), - deprecated( - note = "Deprecated due to the fact that this implementation silently stops at the first value that can't be converted to T. Enable the feature `safe_iterators` for a safe version." - ) -)] pub struct AsyncIter<'a, T: FromRedisValue + 'a> { inner: IterOrFuture<'a, T>, } @@ -239,39 +213,15 @@ impl<'a, T: FromRedisValue + 'a + Unpin + Send> AsyncIter<'a, T> { /// # Ok(()) /// # } /// ``` - #[cfg(feature = "safe_iterators")] #[inline] pub async fn next_item(&mut self) -> Option> { StreamExt::next(self).await } - - /// ```rust,no_run - /// # use redis::AsyncCommands; - /// # async fn scan_set() -> redis::RedisResult<()> { - /// # let client = redis::Client::open("redis://127.0.0.1/")?; - /// # let mut con = client.get_multiplexed_async_connection().await?; - /// let _: () = con.sadd("my_set", 42i32).await?; - /// let _: () = con.sadd("my_set", 43i32).await?; - /// let mut iter: redis::AsyncIter = con.sscan("my_set").await?; - /// while let Some(element) = iter.next_item().await { - /// assert!(element == 42 || element == 43); - /// } - /// # Ok(()) - /// # } - /// ``` - #[cfg(not(feature = "safe_iterators"))] - #[inline] - pub async fn next_item(&mut self) -> Option { - StreamExt::next(self).await - } } #[cfg(feature = "aio")] impl<'a, T: FromRedisValue + Unpin + Send + 'a> Stream for AsyncIter<'a, T> { - #[cfg(feature = "safe_iterators")] type Item = RedisResult; - #[cfg(not(feature = "safe_iterators"))] - type Item = T; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.get_mut(); @@ -293,10 +243,7 @@ impl<'a, T: FromRedisValue + Unpin + Send + 'a> Stream for AsyncIter<'a, T> { Poll::Ready((iter, value)) => { this.inner = IterOrFuture::Iter(iter); - #[cfg(feature = "safe_iterators")] - return Poll::Ready(value); - #[cfg(not(feature = "safe_iterators"))] - Poll::Ready(value.map(|res| res.ok()).flatten()) + Poll::Ready(value) } }, IterOrFuture::Empty => unreachable!(), diff --git a/redis/src/lib.rs b/redis/src/lib.rs index 9fac5c8eb..abf6bccad 100644 --- a/redis/src/lib.rs +++ b/redis/src/lib.rs @@ -553,6 +553,11 @@ let primary = sentinel.get_async_connection().await.unwrap(); # Ok(()) } "## )] +//!``` +//! +//! # Upgrading to version 1 +//! +//! * Iterators are now safe by default, without an opt-out. This means that the iterators return `RedisResult` instead of `Value` (see [this PR](https://github.com/redis-rs/redis-rs/pull/1641) for background). If you previously used the "safe_iterators" feature to opt-in to this behavior, just remove the feature declaration. Otherwise you will need to adjust your usage of iterators to account for potential conversion failures. //! #![deny(non_camel_case_types)] diff --git a/redis/tests/test_async.rs b/redis/tests/test_async.rs index 08972916d..93b7388cd 100644 --- a/redis/tests/test_async.rs +++ b/redis/tests/test_async.rs @@ -586,7 +586,6 @@ mod basic_async { .unwrap(); while let Some(x) = iter.next_item().await { - #[cfg(feature = "safe_iterators")] let x = x?; // if this assertion fails, too many items were returned by the iterator. @@ -634,7 +633,6 @@ mod basic_async { .unwrap(); while let Some(item) = iter.next_item().await { - #[cfg(feature = "safe_iterators")] let item = item?; // if this assertion fails, too many items were returned by the iterator. @@ -682,7 +680,6 @@ mod basic_async { .unwrap(); while let Some(item) = iter.next_item().await { - #[cfg(feature = "safe_iterators")] let item = item?; // if this assertion fails, too many items were returned by the iterator. @@ -939,9 +936,6 @@ mod basic_async { } let iter: redis::AsyncIter = con.scan().await.unwrap(); - #[cfg(not(feature = "safe_iterators"))] - let mut keys_from_redis: Vec = iter.collect().await; - #[cfg(feature = "safe_iterators")] let mut keys_from_redis: Vec<_> = iter.map(std::result::Result::unwrap).collect().await; keys_from_redis.sort(); @@ -1021,7 +1015,6 @@ mod basic_async { .unwrap(); } - #[cfg(feature = "safe_iterators")] #[rstest] // Test issue of AsyncCommands::scan not returning keys because wrong assumptions about the key type were made // https://github.com/redis-rs/redis-rs/issues/1309 diff --git a/redis/tests/test_basic.rs b/redis/tests/test_basic.rs index 9b961d559..3157160cd 100644 --- a/redis/tests/test_basic.rs +++ b/redis/tests/test_basic.rs @@ -1172,7 +1172,6 @@ mod basic { .iter(&mut con) .unwrap(); - #[cfg(feature = "safe_iterators")] let iter = iter.map(std::result::Result::unwrap); for x in iter { @@ -1183,7 +1182,6 @@ mod basic { assert_eq!(unseen.len(), 0); } - #[cfg(feature = "safe_iterators")] #[test] fn test_checked_scanning_error() { const KEY_COUNT: u32 = 1000; @@ -1255,7 +1253,6 @@ mod basic { .hscan_match::<&str, &str, (String, usize)>("foo", "key_0_*") .unwrap(); - #[cfg(feature = "safe_iterators")] let iter = iter.map(std::result::Result::unwrap); for (_field, value) in iter { @@ -2077,7 +2074,6 @@ mod basic { let iter: redis::Iter<'_, (String, isize)> = con.hscan("my_hash").unwrap(); let mut found = HashSet::new(); - #[cfg(feature = "safe_iterators")] let iter = iter.map(std::result::Result::unwrap); for item in iter { @@ -2160,7 +2156,6 @@ mod basic { let mut counter = 0; for kv in iter { - #[cfg(feature = "safe_iterators")] let kv = kv.unwrap(); let (key, num) = kv;