I am making a Tonic-based gRPC microservice that uses the Redis client. I can't figure out an example of implicitly converting a RedisError
into a tonic::Status
when an asynchronous error occurs.
async fn make_transaction(
&self,
request: Request<TransactionRequest>,
) -> Result<Response<TransactionResponse>, Status> {
let mut con = self.client.get_async_connection().await?;
con.set("my_key", 42).await?;
...
}
The connection from Redis client can fail as well as the set. I would rather not use .map_err()
since that seems to break the async.
I was thinking I need to implement the trait From<Status>
and From<RedisError>
but not sure how to do it. This is my attempt, but it doesn't work since Tonic wants a tonic::Status
, not a ApiError
struct that I made:
pub struct ApiError {}
impl From<tonic::Status> for ApiError {
fn from(err: Status) -> ApiError {
ApiError { }
}
}
impl From<RedisError> for Status {
fn from(err: redis::RedisError) -> ApiError {
ApiError { }
}
}
I managed to get around this type of problem by using a custom type as a transition vessel for the original error (which is RedisError
in your case), so something like the following might work for you as well:
pub struct ApiError {}
impl From<RedisError> for ApiError {
fn from(err: RedisError) -> Self {
Self { }
}
}
impl From<ApiError> for Status {
fn from(err: ApiError) -> Self {
Self::internal("Failed talking to Redis")
}
}
async fn set_key(client: ..., key: &str, value: i64) -> Result<(), ApiError> {
let mut con = client.get_async_connection().await?;
con.set(key, value).await?
...
}
async fn make_transaction(
&self,
request: Request<TransactionRequest>,
) -> Result<Response<TransactionResponse>, Status> {
set_key(self.client, "my_key".into(), 42).await?
}
I am not sure if this is the best way, but seems to do the job.