Messaging
vld-redis wraps a sync or async Redis connection. Values are validated and stored as JSON on write, and decoded and validated on read.
Install
Cargo.toml
[dependencies]
vld = "0.5"
vld-redis = "0.5" # redis 0.x (>=0.32) by default
The selected redis crate is re-exported as vld_redis::redis, so you do not need a separate dependency.
Features
| Feature | Default | Effect |
|---|---|---|
redis-0 | yes | redis >=0.32, <1 |
redis-1 | redis 1.x | |
aio | AsyncRedisConn over redis::aio::ConnectionLike | |
aio-tokio | aio plus redis' tokio support (tokio-comp) |
Exactly one of redis-0 / redis-1 must be enabled. redis refuses to compile aio without a runtime: use aio-tokio, or enable aio together with a runtime feature (smol-comp, …) on your own redis dependency of the same version.
vld-redis = { version = "0.5", default-features = false, features = ["redis-1", "aio-tokio"] }
MSRV: 1.85 (see MSRV).
Example
use vld_redis::prelude::*;
use vld_redis::redis;
vld::schema! {
#[derive(Debug, serde::Serialize)]
pub struct UserSchema {
pub name: String => vld::string().min(1).max(100),
pub email: String => vld::string().email(),
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = redis::Client::open("redis://127.0.0.1/")?;
let mut conn = RedisConn::new(client.get_connection()?);
let alice = UserSchema { name: "Alice".into(), email: "alice@example.com".into() };
conn.set("user:1", &alice)?;
let loaded: Option<UserSchema> = conn.get("user:1")?;
assert_eq!(loaded.unwrap().email, "alice@example.com");
let bad = UserSchema { name: "".into(), email: "nope".into() };
assert!(matches!(conn.set("user:2", &bad), Err(VldRedisError::Validation(_))));
Ok(())
}
API
RedisConn<C>— sync wrapper overredis::ConnectionLike;new,into_inner,Deref/DerefMutto the connection.AsyncRedisConn<C>(aio) — the same methods,async, overredis::aio::ConnectionLike.- Validating commands:
set/get,mset/mget,hset/hget,lpush/rpush/lpop/rpop,sadd/smembers,zadd/zrange,publish. VldRedisError—Validation,Serialization,Deserialization,Redis(RedisError).
Gotchas
- The value type is the schema. Written values must be
Serialize + VldParse(aschema!struct with#[derive(serde::Serialize)]); read values are parsed withVldParse. - The wrapper's methods shadow
redis::Commands/redis::AsyncCommands.conn.get(k)is always the validating JSONget. Commands without a validating version (del,expire,incr, …) go throughDeref. To call the native one, use the trait on the inner connection:let raw: Option<String> = redis::Commands::get(&mut *conn, "key")?; - A stored value that is not valid JSON fails with
Deserialization; valid JSON that fails the schema fails withValidation.