use crate::core::error::{RedisError, RedisResult};
use bytes::Bytes;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RespValue {
SimpleString(String),
Error(String),
Integer(i64),
BulkString(Bytes),
Null,
Array(Vec<RespValue>),
}
impl RespValue {
pub fn as_string(&self) -> RedisResult<String> {
match self {
Self::SimpleString(s) => Ok(s.clone()),
Self::BulkString(b) => String::from_utf8(b.to_vec())
.map_err(|e| RedisError::Type(format!("Invalid UTF-8: {e}"))),
Self::Null => Err(RedisError::Type("Value is null".to_string())),
_ => Err(RedisError::Type(format!(
"Cannot convert {self:?} to string"
))),
}
}
pub fn as_int(&self) -> RedisResult<i64> {
match self {
Self::Integer(i) => Ok(*i),
Self::BulkString(b) => {
let s = String::from_utf8(b.to_vec())
.map_err(|e| RedisError::Type(format!("Invalid UTF-8: {e}")))?;
s.parse::<i64>()
.map_err(|e| RedisError::Type(format!("Cannot parse integer: {e}")))
}
_ => Err(RedisError::Type(format!(
"Cannot convert {self:?} to integer"
))),
}
}
pub fn as_bytes(&self) -> RedisResult<Bytes> {
match self {
Self::BulkString(b) => Ok(b.clone()),
Self::SimpleString(s) => Ok(Bytes::from(s.as_bytes().to_vec())),
Self::Null => Err(RedisError::Type("Value is null".to_string())),
_ => Err(RedisError::Type(format!(
"Cannot convert {self:?} to bytes"
))),
}
}
pub fn as_array(&self) -> RedisResult<Vec<Self>> {
match self {
Self::Array(arr) => Ok(arr.clone()),
_ => Err(RedisError::Type(format!(
"Cannot convert {self:?} to array"
))),
}
}
#[must_use]
pub const fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
#[must_use]
pub const fn is_error(&self) -> bool {
matches!(self, Self::Error(_))
}
pub fn as_bool(&self) -> RedisResult<bool> {
match self {
Self::Integer(1) => Ok(true),
Self::Integer(0) => Ok(false),
Self::SimpleString(s) if s == "OK" => Ok(true),
Self::BulkString(b) => {
let s = String::from_utf8(b.to_vec())
.map_err(|e| RedisError::Type(format!("Invalid UTF-8: {e}")))?;
Ok(s == "1" || s.to_lowercase() == "true")
}
_ => Err(RedisError::Type(format!(
"Cannot convert {self:?} to boolean"
))),
}
}
#[must_use]
pub fn into_error(self) -> Option<String> {
match self {
Self::Error(msg) => Some(msg),
_ => None,
}
}
}
impl From<String> for RespValue {
fn from(s: String) -> Self {
Self::BulkString(Bytes::from(s.into_bytes()))
}
}
impl From<&str> for RespValue {
fn from(s: &str) -> Self {
Self::BulkString(Bytes::from(s.as_bytes().to_vec()))
}
}
impl From<i64> for RespValue {
fn from(i: i64) -> Self {
Self::Integer(i)
}
}
impl From<Vec<u8>> for RespValue {
fn from(b: Vec<u8>) -> Self {
Self::BulkString(Bytes::from(b))
}
}
impl From<Bytes> for RespValue {
fn from(b: Bytes) -> Self {
Self::BulkString(b)
}
}
impl TryFrom<RespValue> for String {
type Error = RedisError;
fn try_from(value: RespValue) -> Result<Self, Self::Error> {
value.as_string()
}
}
impl TryFrom<RespValue> for i64 {
type Error = RedisError;
fn try_from(value: RespValue) -> Result<Self, Self::Error> {
value.as_int()
}
}
impl TryFrom<RespValue> for bool {
type Error = RedisError;
fn try_from(value: RespValue) -> Result<Self, Self::Error> {
match value {
RespValue::Integer(1) => Ok(true),
RespValue::Integer(0) => Ok(false),
RespValue::SimpleString(s) if s == "OK" => Ok(true),
_ => Err(RedisError::Type(format!(
"Cannot convert {:?} to bool",
value
))),
}
}
}
impl TryFrom<RespValue> for Option<String> {
type Error = RedisError;
fn try_from(value: RespValue) -> Result<Self, Self::Error> {
match value {
RespValue::BulkString(b) => String::from_utf8(b.to_vec())
.map(Some)
.map_err(|e| RedisError::Type(format!("Invalid UTF-8: {e}"))),
RespValue::SimpleString(s) => Ok(Some(s)),
RespValue::Null => Ok(None),
_ => Err(RedisError::Type(format!(
"Cannot convert {:?} to Option<String>",
value
))),
}
}
}
impl TryFrom<RespValue> for Vec<String> {
type Error = RedisError;
fn try_from(value: RespValue) -> Result<Self, Self::Error> {
match value {
RespValue::Array(items) => {
let mut result = Self::new();
for item in items {
match item {
RespValue::BulkString(b) => {
let s = String::from_utf8(b.to_vec())
.map_err(|e| RedisError::Type(format!("Invalid UTF-8: {e}")))?;
result.push(s);
}
RespValue::SimpleString(s) => result.push(s),
RespValue::Null => {} _ => {
return Err(RedisError::Type(format!(
"Cannot convert array item {:?} to string",
item
)))
}
}
}
Ok(result)
}
_ => Err(RedisError::Type(format!(
"Cannot convert {:?} to Vec<String>",
value
))),
}
}
}
impl TryFrom<RespValue> for Vec<i64> {
type Error = RedisError;
fn try_from(value: RespValue) -> Result<Self, Self::Error> {
match value {
RespValue::Array(items) => {
let mut result = Self::new();
for item in items {
let i = item.as_int()?;
result.push(i);
}
Ok(result)
}
_ => Err(RedisError::Type(format!(
"Cannot convert {:?} to Vec<i64>",
value
))),
}
}
}