zyst 1.0.3

Redis-compatible server
Documentation
use crate::errors::ZystError;
use crate::response::ZystResponse;
use crate::types::Command;
use crate::types::CommandArgs;
use crate::types::Db;
use crate::types::DbValue;
use crate::types::KeyHash;

pub async fn hset(db: &Db, command: Command) -> Result<ZystResponse, ZystError> {
    let (key_name, key_values) = match &command.args {
        CommandArgs::HashFields { key, fields } => (key.to_string(), fields),
        _ => return Err(ZystError::InvalidCommand),
    };

    let key = {
        let db_read = db.read().await;
        db_read.get(&key_name).cloned()
    };

    let mut db_write = db.write().await;

    let nb = match key {
        Some(DbValue::HashKey(mut k)) => {
            let before_len = k.data.len();
            k.data.extend(key_values.clone());
            let after_len = k.data.len();
            db_write.insert(key_name, DbValue::HashKey(k));
            after_len - before_len
        }
        None => {
            db_write.insert(
                key_name.clone(),
                DbValue::HashKey(KeyHash {
                    name: key_name,
                    data: key_values.clone(),
                    ..Default::default()
                }),
            );
            key_values.len()
        }
        Some(_) => return Err(ZystError::WrongType),
    };

    Ok(ZystResponse::Int(nb as i64))
}

pub async fn hget(db: &Db, command: Command) -> Result<ZystResponse, ZystError> {
    let (hash_name, field_name) = match &command.args {
        CommandArgs::KeyWithValue { key, value } => (key, value),
        _ => return Err(ZystError::InvalidCommand),
    };

    let db_read = db.read().await;

    match db_read.get(hash_name) {
        Some(DbValue::HashKey(hash)) => match hash.data.get(field_name) {
            Some(value) => Ok(ZystResponse::SimpleString(value.to_string())),
            None => Ok(ZystResponse::Nil),
        },
        None => Ok(ZystResponse::Nil),
        Some(_) => Err(ZystError::WrongType),
    }
}

pub async fn hgetall(db: &Db, command: Command) -> Result<ZystResponse, ZystError> {
    let key_name = match &command.args {
        CommandArgs::SingleKey(key_name) => key_name,
        _ => return Err(ZystError::InvalidCommand),
    };

    let db_read = db.read().await;

    let results = match db_read.get(key_name) {
        Some(DbValue::HashKey(key)) => key
            .data
            .iter()
            .flat_map(|(k, v)| vec![k.clone(), v.clone()])
            .collect::<Vec<String>>(),
        Some(_) => return Err(ZystError::WrongType),
        None => return Ok(ZystResponse::EmptyArray),
    };

    Ok(ZystResponse::List(results))
}

pub async fn hdel(db: &Db, command: Command) -> Result<ZystResponse, ZystError> {
    let (key_name, fields) = match &command.args {
        CommandArgs::KeyWithValues { key, values } => (key.clone(), values.clone()),
        _ => return Err(ZystError::InvalidCommand),
    };

    let mut db_write = db.write().await;

    match db_write.get_mut(&key_name) {
        Some(DbValue::HashKey(hash)) => {
            let mut deleted_count = 0;
            for field in fields {
                if hash.data.swap_remove(&field).is_some() {
                    deleted_count += 1;
                }
            }

            if hash.data.is_empty() {
                db_write.swap_remove(&key_name);
            }
            Ok(ZystResponse::Int(deleted_count))
        }
        Some(_) => Err(ZystError::WrongType),
        None => Ok(ZystResponse::Int(0)),
    }
}