crabdis-core 0.1.42

Core types and RESP protocol implementation for crabdis
use tokio::time::Instant;

use crate::prelude::*;

/// Value is the type that can be returned via the RESP protocol.
/// It is also used to store values in the store.
/// It is almost free to clone, so we can use it as a reference type.
#[derive(Clone)]
pub enum Value {
    Ok,   // only for response
    Pong, // only for response
    Nil,
    Simple(Arc<str>),
    Error(Arc<str>),
    Integer(i64),
    String(Arc<str>),
    Multi(Arc<[Self]>),
    Expire((Arc<Self>, Instant)),
    Map(HashMap<Self, Self>),
    Set(HashSet<Self>),
    Push(Arc<[Self]>), // For RESP3 push messages (pub/sub)
}

impl PartialEq for Value {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::Ok, Self::Ok) | (Self::Pong, Self::Pong) | (Self::Nil, Self::Nil) => true,
            (Self::Simple(a), Self::Simple(b))
            | (Self::Error(a), Self::Error(b))
            | (Self::String(a), Self::String(b)) => a == b,
            (Self::Integer(a), Self::Integer(b)) => a == b,
            (Self::Multi(a), Self::Multi(b)) | (Self::Push(a), Self::Push(b)) => a == b,
            (Self::Map(a), Self::Map(b)) => a == b,
            (Self::Set(a), Self::Set(b)) => a == b,
            // Compare only inner values; ignore Instant to match Hash
            (Self::Expire(_), Self::Expire(_)) => {
                unreachable!("Expire values should not be compared")
            }
            _ => false,
        }
    }
}

impl Eq for Value {}

impl std::hash::Hash for Value {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        match self {
            Self::Ok => "ok".hash(state),
            Self::Nil => "nil".hash(state),
            Self::Pong => "pong".hash(state),
            Self::Simple(s) | Self::Error(s) | Self::String(s) => s.hash(state),
            Self::Integer(i) => i.hash(state),
            Self::Multi(v) | Self::Push(v) => v.hash(state),
            Self::Expire((v, _)) => v.hash(state),
            Self::Map(_) | Self::Set(_) => unreachable!("Map and Set should not be hashed"),
        }
    }
}

#[macro_export]
macro_rules! value_error {
    ($($arg:tt)*) => {
        $crate::value::Value::Error(format!($($arg)*).into())
    };
}

#[macro_export]
macro_rules! value_multi {
	() => {
		$crate::value::statics::EMPTY_MULTI.clone()
	};
    ($($arg:tt)*) => {
        $crate::value::Value::Multi(vec![$($arg)*].into())
    };
}

#[macro_export]
macro_rules! value_push {
    ($($arg:tt)*) => {
        $crate::value::Value::Push(vec![$($arg)*].into())
    };
}

#[macro_export]
macro_rules! value_map {
    () => {
		$crate::value::statics::EMPTY_MAP.clone()
	};
	($($key:expr => $value:expr),* $(,)?) => {
		$crate::value::Value::Map(
			vec![$(($key.into(), $value.into())),*].into_iter().collect()
		)
	};
}

impl Value {
    #[must_use]
    pub fn expired(&self) -> bool {
        match self {
            Self::Expire((_, expires_at)) => Instant::now() > *expires_at,
            _ => false,
        }
    }

    pub fn set_expire(&mut self, expires_at: Instant) {
        match self {
            Self::Expire((_, e)) => *e = expires_at,
            _ => *self = Self::Expire((Arc::new(self.clone()), expires_at)),
        }
    }

    #[must_use]
    pub fn is_some(&self) -> bool {
        match self {
            Self::Nil => false,
            Self::Expire((_, expires_at)) => Instant::now() <= *expires_at,
            _ => true,
        }
    }

    #[must_use]
    pub fn inner(&self) -> &Self {
        match self {
            Self::Expire((v, _)) => v.inner(),
            _ => self,
        }
    }

    #[must_use]
    pub fn inner_mut(&mut self) -> Option<&mut Self> {
        match self {
            Self::Expire((v, _)) => Arc::get_mut(v),
            _ => Some(self),
        }
    }

    #[must_use]
    pub const fn expire_at(&self) -> Option<Instant> {
        match self {
            Self::Expire((_, expires_at)) => Some(*expires_at),
            _ => None,
        }
    }

    #[must_use]
    pub fn as_i64(&self) -> Option<i64> {
        match self.inner() {
            Self::Integer(i) => Some(*i),
            Self::String(s) => s.parse::<i64>().ok(),
            _ => None,
        }
    }

    #[must_use]
    pub fn as_string(&self) -> Option<Arc<str>> {
        match self.inner() {
            Self::String(s) => Some(s.clone()),
            Self::Integer(i) => Some(i.to_string().into()),
            _ => None,
        }
    }

    #[must_use]
    pub fn is_none(&self) -> bool {
        !self.is_some()
    }

    #[must_use]
    pub fn is_primitive(&self) -> bool {
        matches!(
            self.inner(),
            Self::Simple(_) | Self::Error(_) | Self::Integer(_) | Self::String(_)
        )
    }
}

impl From<Option<Self>> for Value {
    fn from(value: Option<Self>) -> Self {
        value.unwrap_or(Self::Nil)
    }
}

impl From<String> for Value {
    fn from(value: String) -> Self {
        Self::String(value.into())
    }
}

impl From<&str> for Value {
    fn from(value: &str) -> Self {
        Self::String(value.into())
    }
}

impl From<Arc<str>> for Value {
    fn from(value: Arc<str>) -> Self {
        Self::String(value)
    }
}

impl From<Vec<Self>> for Value {
    fn from(value: Vec<Self>) -> Self {
        Self::Multi(value.into())
    }
}

impl From<&[Self]> for Value {
    fn from(value: &[Self]) -> Self {
        Self::Multi(value.into())
    }
}

impl From<i64> for Value {
    fn from(value: i64) -> Self {
        Self::Integer(value)
    }
}

impl From<Option<Arc<str>>> for Value {
    fn from(value: Option<Arc<str>>) -> Self {
        value.map_or(Self::Nil, Self::String)
    }
}

impl std::fmt::Debug for Value {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Ok => write!(f, "OK"),
            Self::Pong => write!(f, "PONG"),
            Self::Nil => write!(f, "(nil)"),
            Self::Simple(s) | Self::String(s) | Self::Error(s) => write!(f, "\"{s}\""),
            Self::Integer(i) => write!(f, "{i}"),
            Self::Multi(v) => write!(f, "{v:?}"),
            Self::Expire((v, expires_at)) => {
                write!(f, "EXPIRE(value={v:?}, expires_at={expires_at:?})")
            }
            Self::Map(m) => write!(f, "%{m:?}"),
            Self::Set(s) => write!(f, "~{s:?}"),
            Self::Push(p) => write!(f, ">{p:?}"),
        }
    }
}

// global static values
pub mod statics {
    use std::collections::HashMap;
    use std::sync::{Arc, LazyLock};

    use super::Value;

    static EMPTY_ARC: LazyLock<Arc<[Value]>> = LazyLock::new(|| Arc::from([]));
    pub static EMPTY_MULTI: LazyLock<Value> = LazyLock::new(|| Value::Multi(EMPTY_ARC.clone()));
    pub static EMPTY_MAP: LazyLock<Value> = LazyLock::new(|| Value::Map(HashMap::default()));
}