I'm implementing a simple task queue using redis in Rust, but am struggling to deserialize the returned values from redis into my custom types.
In total I thought of 3 approches:
FromRedisValue
traitThe 3rd approach worked but feels artificial. I'd like to figure out either 1 or 2, both of which I'm failing at.
I have a simple Task definition:
#[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Task {
pub id: u32,
pub desc: String,
}
which I'm trying to receive into a worker:
use serde_redis::RedisDeserialize;
pub fn execute_task(conn: &mut Connection) {
let task: Task = redis::cmd("LPOP")
.arg("task_q")
.query::<Task>(conn)
.unwrap()
.deserialize()
.unwrap();
println!("... executing task {} ...", task.id);
}
but I'm getting the following error:
error[E0277]: the trait bound `Task: FromRedisValue` is not satisfied
--> src/bin/worker.rs:38:10
|
38 | .query::<Task>(conn)
| ^^^^^ the trait `FromRedisValue` is not implemented for `Task`
error[E0599]: no method named `deserialize` found for struct `Task` in the current scope
--> src/bin/worker.rs:40:10
|
40 | .deserialize()
| ^^^^^^^^^^^ method not found in `Task`
So clearly I integrated the crate wrong, as it's not working. The documentation is super brief and the source code way over my head as a beginner - what could I be missing?
My naive approach:
impl FromRedisValue for Task {
fn from_redis_value(v: &Value) -> RedisResult<Self> {
let t = Task {
id: v.id,
desc: v.desc,
};
RedisResult::Ok(t)
}
fn from_redis_values(items: &[Value]) -> RedisResult<Vec<Self>> {
let tasks = items
.into_iter()
.map(|v| Task {
id: v.id,
desc: v.desc,
})
.collect();
RedisResult::Ok(tasks)
}
}
The errors I'm getting:
error[E0609]: no field `id` on type `&Value`
--> src/redis_tut.rs:203:19
|
203 | id: v.id,
| ^^
error[E0609]: no field `desc` on type `&Value`
--> src/redis_tut.rs:204:21
|
204 | desc: v.desc,
| ^^^^
// ...the same for the vector implementation
So clearly redis's Value doesn't have / know of the fields I want for Task. What's the right way to do this?
Redis doesn't define structured serialization formats. It mostly store strings and integers. So you have to choose or define your format for your struct.
A popular one is JSON, as you noticed, but if you just want to (de)serialize simple pairs of (id, description), it's not very readable nor convenient.
In such a case, you can define your own format, for example the id and the description with a dash in between:
#[derive(Debug, PartialEq)]
pub struct Task {
pub id: u32,
pub desc: String,
}
// assume a task is defined as "<id>-<desc>"
impl FromRedisValue for Task {
fn from_redis_value(v: &Value) -> RedisResult<Self> {
let v: String = from_redis_value(v)?;
if let Some((id, desc)) = v.split_once('-') {
if let Ok(id) = id.parse() {
Ok(Task {
id,
desc: desc.to_string(),
})
} else {
Err((ErrorKind::TypeError, "bad first token").into())
}
} else {
Err((ErrorKind::TypeError, "missing dash").into())
}
}
}