use std::io::{Error as IoError, ErrorKind};
use crate::prelude::*;
#[must_use]
pub const fn type_byte(value: &Value) -> Option<u8> {
match value {
Value::String(_) | Value::Integer(_) => Some(super::symbols::TYPE_STRING),
Value::Map(_) => Some(super::symbols::TYPE_HASH),
_ => None,
}
}
pub async fn save<W>(writer: &mut W, value: &Value) -> Result<()>
where
W: AsyncWrite + Unpin,
{
match value {
Value::String(s) => {
super::string::save(writer, s).await?;
}
Value::Integer(i) => {
super::string::save(writer, &i.to_string()).await?;
}
Value::Map(map) => {
super::length::save(writer, map.len() as u64).await?;
for (k, v) in map {
let key_str = value_to_string(k);
let val_str = value_to_string(v);
super::string::save(writer, &key_str).await?;
super::string::save(writer, &val_str).await?;
}
}
_ => {
return Err(IoError::new(
ErrorKind::Unsupported,
format!("Cannot save value type to RDB: {value:?}"),
)
.into());
}
}
Ok(())
}
fn value_to_string(value: &Value) -> String {
match value {
Value::String(s) => s.to_string(),
Value::Integer(i) => i.to_string(),
_ => String::new(),
}
}
pub async fn load<R>(reader: &mut R, obj_type: u8) -> Result<Value>
where
R: AsyncRead + Unpin,
{
match obj_type {
super::symbols::TYPE_STRING => {
let s = super::string::load(reader).await?;
Ok(Value::String(s))
}
super::symbols::TYPE_HASH => {
let len = super::length::load_plain(reader).await?;
#[allow(clippy::cast_possible_truncation)]
let mut map = HashMap::with_capacity(len as usize);
for _ in 0..len {
let key = super::string::load(reader).await?;
let value = super::string::load(reader).await?;
map.insert(Value::String(key), Value::String(value));
}
Ok(Value::Map(map))
}
super::symbols::TYPE_HASH_ZIPMAP => {
let data = super::string::load_raw(reader).await?;
let map = super::zipmap::parse(&data)?;
Ok(Value::Map(map))
}
super::symbols::TYPE_HASH_ZIPLIST => {
let data = super::string::load_raw(reader).await?;
let map = super::ziplist::parse_hash(&data)?;
Ok(Value::Map(map))
}
super::symbols::TYPE_HASH_LISTPACK => {
let data = super::string::load_raw(reader).await?;
let map = super::listpack::parse_hash(&data)?;
Ok(Value::Map(map))
}
super::symbols::TYPE_LIST
| super::symbols::TYPE_LIST_ZIPLIST
| super::symbols::TYPE_LIST_QUICKLIST
| super::symbols::TYPE_LIST_QUICKLIST_2 => {
skip_list(reader, obj_type).await?;
Err(IoError::new(
ErrorKind::Unsupported,
"List type not supported (no list commands in crabdis)",
)
.into())
}
super::symbols::TYPE_SET
| super::symbols::TYPE_SET_INTSET
| super::symbols::TYPE_SET_LISTPACK => {
skip_set(reader, obj_type).await?;
Err(IoError::new(
ErrorKind::Unsupported,
"Set type not supported (no set commands in crabdis)",
)
.into())
}
super::symbols::TYPE_ZSET
| super::symbols::TYPE_ZSET_2
| super::symbols::TYPE_ZSET_ZIPLIST
| super::symbols::TYPE_ZSET_LISTPACK => {
skip_zset(reader, obj_type).await?;
Err(IoError::new(
ErrorKind::Unsupported,
"Sorted set type not supported (no sorted set commands in crabdis)",
)
.into())
}
super::symbols::TYPE_MODULE | super::symbols::TYPE_MODULE_2 => {
Err(IoError::new(ErrorKind::Unsupported, "Module types not supported").into())
}
super::symbols::TYPE_STREAM_LISTPACKS
| super::symbols::TYPE_STREAM_LISTPACKS_2
| super::symbols::TYPE_STREAM_LISTPACKS_3 => {
Err(IoError::new(ErrorKind::Unsupported, "Stream types not supported").into())
}
_ => Err(IoError::new(
ErrorKind::InvalidData,
format!("Unknown RDB object type: {obj_type}"),
)
.into()),
}
}
async fn skip_list<R>(reader: &mut R, obj_type: u8) -> Result<()>
where
R: AsyncRead + Unpin,
{
match obj_type {
super::symbols::TYPE_LIST => {
let len = super::length::load_plain(reader).await?;
for _ in 0..len {
let _ = super::string::load(reader).await?;
}
}
super::symbols::TYPE_LIST_ZIPLIST => {
let _ = super::string::load_raw(reader).await?;
}
super::symbols::TYPE_LIST_QUICKLIST | super::symbols::TYPE_LIST_QUICKLIST_2 => {
let len = super::length::load_plain(reader).await?;
for _ in 0..len {
let _ = super::string::load_raw(reader).await?;
}
}
_ => {}
}
Ok(())
}
async fn skip_set<R>(reader: &mut R, obj_type: u8) -> Result<()>
where
R: AsyncRead + Unpin,
{
match obj_type {
super::symbols::TYPE_SET => {
let len = super::length::load_plain(reader).await?;
for _ in 0..len {
let _ = super::string::load(reader).await?;
}
}
super::symbols::TYPE_SET_INTSET | super::symbols::TYPE_SET_LISTPACK => {
let _ = super::string::load_raw(reader).await?;
}
_ => {}
}
Ok(())
}
async fn skip_zset<R>(reader: &mut R, obj_type: u8) -> Result<()>
where
R: AsyncRead + Unpin,
{
match obj_type {
super::symbols::TYPE_ZSET | super::symbols::TYPE_ZSET_2 => {
let len = super::length::load_plain(reader).await?;
for _ in 0..len {
let _ = super::string::load(reader).await?;
if obj_type == super::symbols::TYPE_ZSET_2 {
let mut bytes = [0u8; 8];
reader.read_exact(&mut bytes).await?;
} else {
let _ = super::string::load(reader).await?;
}
}
}
super::symbols::TYPE_ZSET_ZIPLIST | super::symbols::TYPE_ZSET_LISTPACK => {
let _ = super::string::load_raw(reader).await?;
}
_ => {}
}
Ok(())
}