bullrs 0.0.3

A BullMQ compatible Job Queue based on Redis
Documentation
use chrono::{DateTime, Utc};
use serde::de::DeserializeOwned;
use std::{collections::HashMap, time::Duration};
use thiserror::Error;

#[derive(Debug, Error)]
pub enum RedisHashMapError {
    #[error("Value missing in key value map from redis: {key}")]
    Missing { key: String },
    #[error("Could not deserialize json value: {0}")]
    Deserialize(#[from] serde_json::Error),
    #[error("Timestamp {key} was out of range: {ts}")]
    TimeStampOutOfRange { key: String, ts: i64 },
}

pub trait RedisHashMapExt {
    fn get_v(&self, key: &str) -> Result<String, RedisHashMapError>;
    fn extract<T: DeserializeOwned>(&self, key: &str) -> Result<T, RedisHashMapError>;
    fn extract_opt<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, RedisHashMapError>;
    fn extract_timestamp_ms(&self, key: &str) -> Result<DateTime<Utc>, RedisHashMapError>;
    fn extract_duration_ms_opt(&self, key: &str) -> Result<Option<Duration>, RedisHashMapError>;
}

impl RedisHashMapExt for HashMap<String, String> {
    fn get_v(&self, key: &str) -> Result<String, RedisHashMapError> {
        match self.get(key) {
            None => Err(RedisHashMapError::Missing { key: key.into() }),
            Some(v) => Ok(v.clone()),
        }
    }
    fn extract<T: DeserializeOwned>(&self, key: &str) -> Result<T, RedisHashMapError> {
        match self.get(key) {
            None => Err(RedisHashMapError::Missing { key: key.into() }),
            Some(v) => Ok(serde_json::from_str(v)?),
        }
    }
    fn extract_opt<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, RedisHashMapError> {
        match self.get(key) {
            None => Ok(None),
            Some(v) => Ok(serde_json::from_str(v)?),
        }
    }
    fn extract_timestamp_ms(&self, key: &str) -> Result<DateTime<Utc>, RedisHashMapError> {
        let ts: i64 = self.extract(key)?;
        match DateTime::from_timestamp_millis(ts) {
            Some(dt) => Ok(dt),
            None => Err(RedisHashMapError::TimeStampOutOfRange {
                key: key.into(),
                ts,
            }),
        }
    }
    fn extract_duration_ms_opt(&self, key: &str) -> Result<Option<Duration>, RedisHashMapError> {
        let ms: Option<u64> = self.extract_opt(key)?;
        Ok(ms.map(Duration::from_millis))
    }
}