use std::collections::{BTreeMap, HashMap};
use std::os::raw::{c_int, c_void};
use valkey_module::alloc::ValkeyAlloc;
use valkey_module::logging::log_notice;
use valkey_module::native_types::ValkeyType;
use valkey_module::redisvalue::ValkeyValueKey;
use valkey_module::{
raw, valkey_module, Context, NextArg, RedisModuleIO, RedisModuleTypeMethods, ValkeyError,
ValkeyResult, ValkeyString, ValkeyValue, REDISMODULE_TYPE_METHOD_VERSION,
};
#[derive(Debug, Default)]
struct MyType {
my_string: String,
my_number: i64,
my_vec: Vec<String>,
my_map: HashMap<String, String>,
}
impl MyType {
fn to_btreemap(&self) -> BTreeMap<ValkeyValueKey, ValkeyValue> {
let mut outut = BTreeMap::new();
outut.insert("my_string".into(), self.my_string.clone().into());
outut.insert("my_number".into(), self.my_number.into());
let my_vec = self
.my_vec
.iter()
.map(|s| s.into())
.collect::<Vec<ValkeyValue>>();
outut.insert("my_vec".into(), my_vec.into());
let my_map = self
.my_map
.iter()
.map(|(k, v)| (k.clone().into(), v.clone().into()))
.collect::<BTreeMap<ValkeyValueKey, ValkeyValue>>();
outut.insert("my_map".into(), my_map.into());
outut
}
}
static MY_TYPE: ValkeyType = ValkeyType::new(
"mytype789",
0,
RedisModuleTypeMethods {
version: REDISMODULE_TYPE_METHOD_VERSION as u64,
rdb_load: Some(rdb_load),
rdb_save: Some(rdb_save),
aof_rewrite: None,
free: Some(free),
mem_usage: None,
digest: None,
aux_load: None,
aux_save: None,
aux_save_triggers: 0,
free_effort: None,
unlink: None,
copy: None,
defrag: None,
free_effort2: None,
unlink2: None,
copy2: None,
mem_usage2: None,
aux_save2: None,
},
);
extern "C" fn free(value: *mut c_void) {
if value.is_null() {
return;
}
unsafe { drop(Box::from_raw(value.cast::<MyType>())) }
}
extern "C" fn rdb_save(rdb: *mut RedisModuleIO, value: *mut c_void) {
if value.is_null() || rdb.is_null() {
return;
}
let item = unsafe { &*value.cast::<MyType>() };
raw::save_string(rdb, item.my_string.as_str());
raw::save_signed(rdb, item.my_number);
raw::save_unsigned(rdb, item.my_vec.len() as u64);
for str in &item.my_vec {
raw::save_string(rdb, str.as_str());
}
raw::save_unsigned(rdb, item.my_map.len() as u64);
for (key, value) in &item.my_map {
raw::save_string(rdb, key.as_str());
raw::save_string(rdb, value.as_str());
}
}
extern "C" fn rdb_load(rdb: *mut RedisModuleIO, _encver: c_int) -> *mut c_void {
if rdb.is_null() {
return std::ptr::null_mut();
}
let my_string = match raw::load_string(rdb) {
Ok(tmp) => tmp.to_string(),
Err(err) => {
log_notice(&format!("rdb_load my_string error: {}", err));
return std::ptr::null_mut();
}
};
let my_number = match raw::load_signed(rdb) {
Ok(tmp) => tmp,
Err(err) => {
log_notice(&format!("rdb_load my_number error: {}", err));
return std::ptr::null_mut();
}
};
let vec_size = match raw::load_unsigned(rdb) {
Ok(tmp) => tmp as usize,
Err(err) => {
log_notice(&format!("rdb_load vec_size error: {}", err));
return std::ptr::null_mut();
}
};
let mut my_vec = Vec::with_capacity(vec_size);
for count in 0..vec_size {
match raw::load_string(rdb) {
Ok(tmp) => my_vec.push(tmp.to_string()),
Err(err) => {
log_notice(&format!("rdb_load my_vec error: {}, count: {}", err, count));
return std::ptr::null_mut();
}
}
}
let map_size = match raw::load_unsigned(rdb) {
Ok(tmp) => tmp as usize,
Err(err) => {
log_notice(&format!("rdb_load map_size error: {}", err));
return std::ptr::null_mut();
}
};
let mut my_map = HashMap::with_capacity(map_size);
for count in 0..map_size {
let key = match raw::load_string(rdb) {
Ok(tmp) => tmp.to_string(),
Err(err) => {
log_notice(&format!(
"rdb_load my_map key error: {}, count: {}",
err, count
));
return std::ptr::null_mut();
}
};
let value = match raw::load_string(rdb) {
Ok(tmp) => tmp.to_string(),
Err(err) => {
log_notice(&format!(
"rdb_load my_map value error: {}, count: {}",
err, count
));
return std::ptr::null_mut();
}
};
my_map.insert(key, value);
}
let my_type = MyType {
my_string,
my_number,
my_vec,
my_map,
};
Box::into_raw(Box::new(my_type)) as *mut c_void
}
fn my_get(ctx: &Context, args: Vec<ValkeyString>) -> ValkeyResult {
let key_arg = args.into_iter().nth(1).ok_or(ValkeyError::WrongArity)?;
let key = ctx.open_key(&key_arg);
let current_value = key.get_value::<MyType>(&MY_TYPE)?;
match current_value {
Some(tmp) => Ok(tmp.to_btreemap().into()),
None => Ok(ValkeyValue::Null.into()),
}
}
fn my_set_string(ctx: &Context, args: Vec<ValkeyString>) -> ValkeyResult {
let mut args = args.into_iter().skip(1);
if args.len() != 2 {
return Err(ValkeyError::WrongArity);
}
let key_arg = args.next_arg()?;
let value_arg = args.next_string()?;
let key = ctx.open_key_writable(&key_arg);
let current_value = key.get_value::<MyType>(&MY_TYPE)?;
match current_value {
Some(tmp) => {
tmp.my_string = value_arg;
}
None => {
let my_type = MyType {
my_string: value_arg.to_string(),
..Default::default()
};
key.set_value(&MY_TYPE, my_type)?;
}
}
Ok("OK".into())
}
fn my_set_number(ctx: &Context, args: Vec<ValkeyString>) -> ValkeyResult {
let mut args = args.into_iter().skip(1);
if args.len() != 2 {
return Err(ValkeyError::WrongArity);
}
let key_arg = args.next_arg()?;
let value_arg = args.next_i64()?;
let key = ctx.open_key_writable(&key_arg);
let current_value = key.get_value::<MyType>(&MY_TYPE)?;
match current_value {
Some(tmp) => {
tmp.my_number = value_arg;
}
None => {
let my_type = MyType {
my_number: value_arg,
..Default::default()
};
key.set_value(&MY_TYPE, my_type)?;
}
}
Ok("OK".into())
}
fn my_vec_push(ctx: &Context, args: Vec<ValkeyString>) -> ValkeyResult {
let mut args = args.into_iter().skip(1);
if args.len() != 2 {
return Err(ValkeyError::WrongArity);
}
let key_arg = args.next_arg()?;
let value_arg = args.next_string()?;
let key = ctx.open_key_writable(&key_arg);
let current_value = key.get_value::<MyType>(&MY_TYPE)?;
match current_value {
Some(tmp) => {
tmp.my_vec.push(value_arg.to_string());
}
None => {
let my_type = MyType {
my_vec: vec![value_arg.to_string()],
..Default::default()
};
key.set_value(&MY_TYPE, my_type)?;
}
}
Ok("OK".into())
}
fn my_map_insert(ctx: &Context, args: Vec<ValkeyString>) -> ValkeyResult {
let mut args = args.into_iter().skip(1);
if args.len() != 3 {
return Err(ValkeyError::WrongArity);
}
let key_arg = args.next_arg()?;
let value_k_arg = args.next_string()?;
let value_v_arg = args.next_string()?;
let key = ctx.open_key_writable(&key_arg);
let current_value = key.get_value::<MyType>(&MY_TYPE)?;
match current_value {
Some(tmp) => {
tmp.my_map
.insert(value_k_arg.to_string(), value_v_arg.to_string());
}
None => {
let my_type = MyType {
my_map: HashMap::from([(value_k_arg.to_string(), value_v_arg.to_string())]),
..Default::default()
};
key.set_value(&MY_TYPE, my_type)?;
}
}
Ok("OK".into())
}
valkey_module! {
name: "data_type3",
version: 1,
allocator: (ValkeyAlloc, ValkeyAlloc),
data_types: [
MY_TYPE,
],
commands: [
["my-get", my_get, "readonly", 0, 0, 0],
["my-set-string", my_set_string, "write", 0, 0, 0],
["my-set-number", my_set_number, "write", 0, 0, 0],
["my-vec-push", my_vec_push, "write", 0, 0, 0],
["my-map-insert", my_map_insert, "write", 0, 0, 0],
],
}