shardmap 0.3.1

Sharded embedded in-memory map with optional cache, protocol, and server internals
Documentation
use bytes::BytesMut;

use crate::commands::expire::{ExpireCondition, expire_at_changed, parse_expire_condition_frame};
use crate::commands::redis::{
    define_redis_command, error, int, parse_i64, write_frame, wrong_arity,
};
use crate::protocol::Frame;
#[cfg(feature = "server")]
use crate::server::wire::ServerWire;
use crate::storage::EmbeddedStore;

define_redis_command!(ExpireAt, "EXPIREAT", true);

impl crate::commands::redis::RedisCommand for ExpireAt {
    fn execute(store: &EmbeddedStore, args: &[&[u8]]) -> Frame {
        execute_absolute_expire(store, args, false)
    }

    #[cfg(feature = "server")]
    fn write_resp(store: &EmbeddedStore, args: &[&[u8]], out: &mut BytesMut) {
        write_absolute_expire_resp(store, args, false, out);
    }
}

pub(crate) fn execute_absolute_expire(
    store: &EmbeddedStore,
    args: &[&[u8]],
    millis: bool,
) -> Frame {
    let command = if millis { "PEXPIREAT" } else { "EXPIREAT" };
    match args {
        [key, timestamp, options @ ..] if options.len() <= 1 => match parse_i64(timestamp) {
            Ok(timestamp) => {
                let expire_at_ms = match millis {
                    true => timestamp,
                    false => timestamp.saturating_mul(1_000),
                };
                let Ok(condition) = parse_expire_condition_frame(options) else {
                    return error("ERR syntax error");
                };
                int(expire_at_changed_i64(store, key, expire_at_ms, condition))
            }
            Err(_) => error("ERR value is not an integer or out of range"),
        },
        _ => wrong_arity(command),
    }
}

pub(crate) fn expire_at_changed_i64(
    store: &EmbeddedStore,
    key: &[u8],
    expire_at_ms: i64,
    condition: ExpireCondition,
) -> i64 {
    match expire_at_ms <= 0 {
        true => expire_at_changed(store, key, 0, condition),
        false => expire_at_changed(store, key, expire_at_ms as u64, condition),
    }
}

#[cfg(feature = "server")]
pub(crate) fn write_absolute_expire_resp(
    store: &EmbeddedStore,
    args: &[&[u8]],
    millis: bool,
    out: &mut BytesMut,
) {
    let command = if millis { "PEXPIREAT" } else { "EXPIREAT" };
    match args {
        [key, timestamp, options @ ..] if options.len() <= 1 => match parse_i64(timestamp) {
            Ok(timestamp) => {
                let expire_at_ms = match millis {
                    true => timestamp,
                    false => timestamp.saturating_mul(1_000),
                };
                let Ok(condition) = parse_expire_condition_frame(options) else {
                    ServerWire::write_resp_error(out, "ERR syntax error");
                    return;
                };
                ServerWire::write_resp_integer(
                    out,
                    expire_at_changed_i64(store, key, expire_at_ms, condition),
                );
            }
            Err(_) => {
                ServerWire::write_resp_error(out, "ERR value is not an integer or out of range")
            }
        },
        _ => write_frame(out, &wrong_arity(command)),
    }
}