use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int, c_uchar, c_ulong, c_ulonglong, c_void};
use std::ptr;
#[derive(PartialEq)]
pub enum DumpLevel {
HEADER,
HASHTABLE,
SUBHASHTABLE,
ELEMENT,
PAGE,
}
pub enum DebugLevel {
SILENT,
ERROR,
WARNING,
MESSAGE,
}
#[repr(C)]
pub struct K2hKeyPack {
pub pkey: *mut u8,
pub length: usize,
}
#[repr(C)]
pub struct K2hAttrPack {
pub pkey: *mut u8,
pub keylength: usize,
pub pval: *mut u8,
pub vallength: usize,
}
#[link(name = "k2hash")]
extern "C" {
fn k2h_open_mem(maskbitcnt: i32, cmaskbitcnt: i32, maxelementcnt: i32, pagesize: i32) -> u64;
fn k2h_set_str_value_wa(
handle: u64,
pkey: *const c_char,
pval: *const c_char,
pass: *const c_char,
expire: *const c_ulonglong,
) -> bool;
fn k2h_get_str_direct_value_wp(
handle: u64,
pkey: *const c_char,
pass: *const c_char,
) -> *mut c_char;
fn k2h_find_first(handle: u64) -> u64;
fn k2h_find_first_str_subkey(handle: u64, pkey: *const c_char) -> u64;
fn k2h_find_next(findhandle: u64) -> u64;
fn k2h_find_get_key(findhandle: u64, ppkey: *mut *mut c_uchar, pkeylength: *mut usize) -> bool;
fn k2h_keyq_handle_str_prefix(handle: u64, is_fifo: bool, pref: *const c_char) -> u64;
fn k2h_keyq_str_push_keyval_wa(
keyqhandle: u64,
pkey: *const c_char,
pval: *const c_char,
encpass: *const c_char,
expire: *const c_ulonglong,
) -> bool;
fn k2h_keyq_dump(qhandle: u64, stream: *mut c_void) -> bool;
fn k2h_keyq_free(qhandle: u64) -> bool;
fn k2h_keyq_count(qhandle: u64) -> c_int;
fn k2h_keyq_str_read_keyval_wp(
keyqhandle: u64,
ppkey: *mut *mut c_char,
ppval: *mut *mut c_char,
pos: c_int,
encpass: *const c_char,
) -> bool;
fn k2h_keyq_empty(qhandle: u64) -> bool;
fn k2h_keyq_str_pop_keyval_wp(
keyqhandle: u64,
ppkey: *mut *mut c_char,
ppval: *mut *mut c_char,
encpass: *const c_char,
) -> bool;
fn k2h_keyq_remove(qhandle: u64, count: c_int) -> bool;
fn k2h_q_handle_str_prefix(handle: u64, is_fifo: bool, pref: *const c_char) -> u64;
fn k2h_q_str_push_wa(
qhandle: u64,
pdata: *const c_char,
pattrspck: *const c_void,
attrspckcnt: c_int,
encpass: *const c_char,
expire: *const c_ulonglong,
) -> bool;
fn k2h_q_remove(qhandle: u64, count: c_int) -> bool;
fn k2h_q_free(qhandle: u64) -> bool;
fn k2h_q_count(qhandle: u64) -> c_int;
fn k2h_q_str_read_wp(
qhandle: u64,
ppdata: *mut *mut c_char,
pos: c_int,
encpass: *const c_char,
) -> bool;
fn k2h_q_empty(qhandle: u64) -> bool;
fn k2h_q_str_pop_wp(qhandle: u64, ppdata: *mut *mut c_char, encpass: *const c_char) -> bool;
fn k2h_q_dump(qhandle: u64, stream: *mut c_void) -> bool;
fn k2h_add_attr_crypt_pass(handle: u64, pass: *const c_char, is_default_encrypt: bool) -> bool;
fn k2h_add_attr_plugin_library(handle: u64, libpath: *const c_char) -> bool;
fn k2h_add_attr(
handle: u64,
pkey: *const u8,
keylength: usize,
pattrkey: *const u8,
attrkeylength: usize,
pattrval: *const u8,
attrvallength: usize,
) -> bool;
fn k2h_add_subkey_wa(
handle: u64,
pkey: *const u8,
keylength: usize,
psubkey: *const u8,
skeylength: usize,
pval: *const u8,
vallength: usize,
pass: *const c_char,
expire: *const c_ulonglong,
) -> bool;
fn k2h_close(handle: u64) -> bool;
fn k2h_create(
filepath: *const c_char,
maskbitcnt: i32,
cmaskbitcnt: i32,
maxelementcnt: i32,
pagesize: usize,
) -> bool;
fn k2h_disable_transaction(handle: u64) -> bool;
fn k2h_dump_head(handle: u64, stream: *mut c_void) -> bool;
fn k2h_dump_keytable(handle: u64, stream: *mut c_void) -> bool;
fn k2h_dump_full_keytable(handle: u64, stream: *mut c_void) -> bool;
fn k2h_dump_elementtable(handle: u64, stream: *mut c_void) -> bool;
fn k2h_dump_full(handle: u64, stream: *mut c_void) -> bool;
fn k2h_get_direct_attrs(
handle: u64,
pkey: *const u8,
keylength: usize,
pattrspckcnt: *mut c_int,
) -> *mut c_void;
fn k2h_get_direct_subkeys(
handle: u64,
pkey: *const u8,
keylength: usize,
pskeypckcnt: *mut c_int,
) -> *mut c_void;
fn k2h_get_transaction_archive_fd(handle: u64) -> c_int;
fn k2h_get_transaction_thread_pool() -> c_int;
fn k2h_load_archive(handle: u64, filepath: *const c_char, errskip: bool) -> bool;
fn k2h_open(
pfile: *const c_char,
readonly: bool,
removefile: bool,
fullmap: bool,
maskbitcnt: i32,
cmaskbitcnt: i32,
maxelementcnt: i32,
pagesize: i32,
) -> u64;
fn k2h_print_attr_version(handle: u64, stream: *mut c_void) -> bool;
fn k2h_print_attr_information(handle: u64, stream: *mut c_void) -> bool;
fn k2h_print_state(handle: u64, stream: *mut c_void) -> bool;
fn k2h_print_version(stream: *mut c_void);
fn k2h_put_archive(handle: u64, filepath: *const c_char, errskip: bool) -> bool;
fn k2h_remove_str_all(handle: u64, pkey: *const c_char) -> bool;
fn k2h_remove_str(handle: u64, pkey: *const c_char) -> bool;
fn k2h_rename_str(handle: u64, pkey: *const c_char, pnewkey: *const c_char) -> bool;
fn k2h_remove_str_subkey(handle: u64, pkey: *const c_char, psubkey: *const c_char) -> bool;
fn k2h_set_common_attr(
handle: u64,
is_mtime: *const bool,
is_defenc: *const bool,
passfile: *const c_char,
is_history: *const bool,
expire: *const c_ulong,
) -> bool;
fn k2h_set_debug_level_silent() -> bool;
fn k2h_set_debug_level_error() -> bool;
fn k2h_set_debug_level_warning() -> bool;
fn k2h_set_debug_level_message() -> bool;
fn k2h_transaction_param_we(
handle: u64,
enable: bool,
transfile: *const c_char,
pprefix: *const u8,
prefixlen: usize,
pparam: *const u8,
paramlen: usize,
expire: *const c_ulonglong,
) -> bool;
fn k2h_set_transaction_thread_pool(count: c_int) -> bool;
}
pub struct K2hashKey {
k2h_handle: u64,
key: Option<String>,
handle: u64,
}
impl K2hashKey {
pub fn new(k2h_handle: u64, key: Option<String>) -> Result<Self, &'static str> {
if k2h_handle == 0 {
return Err("handle should not be 0");
}
let mut k2hkey = K2hashKey {
k2h_handle,
key,
handle: 0,
};
if let Some(ref k) = k2hkey.key {
let c_key = CString::new(k.as_str()).unwrap();
k2hkey.handle = unsafe { k2h_find_first_str_subkey(k2hkey.k2h_handle, c_key.as_ptr()) };
} else {
k2hkey.handle = unsafe { k2h_find_first(k2hkey.k2h_handle) };
}
if k2hkey.handle == 0 {
return Err("handle should not be K2H_INVALID_HANDLE");
}
Ok(k2hkey)
}
}
impl Iterator for K2hashKey {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
let mut ppkey: *mut c_char = ptr::null_mut();
let mut pkeylength: usize = 0;
let result = unsafe {
k2h_find_get_key(
self.handle,
&mut ppkey as *mut *mut c_char as *mut *mut c_uchar,
&mut pkeylength as *mut usize,
)
};
if result && !ppkey.is_null() && pkeylength > 0 {
let key = unsafe { CStr::from_ptr(ppkey).to_string_lossy().into_owned() };
self.handle = unsafe { k2h_find_next(self.handle) };
Some(key)
} else {
None
}
}
}
pub struct K2hash {
handle: u64,
}
impl K2hash {
pub fn handle(&self) -> u64 {
self.handle
}
pub fn set_debug_level(level: DebugLevel) -> Result<(), &'static str> {
let result = match level {
DebugLevel::SILENT => unsafe { k2h_set_debug_level_silent() },
DebugLevel::ERROR => unsafe { k2h_set_debug_level_error() },
DebugLevel::WARNING => unsafe { k2h_set_debug_level_warning() },
DebugLevel::MESSAGE => unsafe { k2h_set_debug_level_message() },
};
if result {
Ok(())
} else {
Err("Failed to set debug level")
}
}
pub fn open(file: &str) -> Result<Self, &'static str> {
let f = CString::new(file).unwrap();
let handle = unsafe { k2h_open(f.as_ptr(), false, false, false, 8, 4, 1024, 512) };
if handle == 0 {
Err("k2h_open_mem failed")
} else {
Ok(K2hash { handle })
}
}
pub fn open_mem() -> Result<Self, &'static str> {
let handle = unsafe { k2h_open_mem(8, 4, 1024, 512) };
if handle == 0 {
Err("k2h_open_mem failed")
} else {
Ok(K2hash { handle })
}
}
pub fn set(&self, key: &str, value: &str) -> Result<(), &'static str> {
return self.set_with_options(key, value, None, None);
}
pub fn set_with_options(
&self,
key: &str,
value: &str,
password: Option<&str>,
expire_duration: Option<u64>,
) -> Result<(), &'static str> {
let k = CString::new(key).unwrap();
let v = CString::new(value).unwrap();
if k.is_empty() || v.is_empty() {
return Err("Key and value cannot be empty");
}
let pass = password.map(|p| CString::new(p).unwrap());
let c_pass = pass.as_ref().map_or(ptr::null(), |p| p.as_ptr());
let expire = expire_duration.map(|e| e as c_ulonglong);
let c_expire = expire
.as_ref()
.map_or(ptr::null(), |e| e as *const c_ulonglong);
let result =
unsafe { k2h_set_str_value_wa(self.handle, k.as_ptr(), v.as_ptr(), c_pass, c_expire) };
if result {
Ok(())
} else {
Err("Failed to set value")
}
}
pub fn get(&self, key: &str) -> Result<Option<String>, &'static str> {
return self.get_with_options(key, None);
}
pub fn get_with_options(
&self,
key: &str,
password: Option<&str>,
) -> Result<Option<String>, &'static str> {
let k = CString::new(key).unwrap();
if k.is_empty() {
return Err("Key cannot be empty");
}
let pass = password.map(|p| CString::new(p).unwrap());
let c_pass = pass.as_ref().map_or(ptr::null(), |p| p.as_ptr());
let ptr = unsafe { k2h_get_str_direct_value_wp(self.handle, k.as_ptr(), c_pass) };
if ptr.is_null() {
Err("Failed to get result")
} else {
let cstr = unsafe { CStr::from_ptr(ptr) };
Ok(Some(cstr.to_string_lossy().into_owned()))
}
}
pub fn add_attribute_plugin_lib(&self, path: &str) -> Result<(), &'static str> {
let path = CString::new(path).unwrap();
if path.is_empty() {
return Err("Path should be a some string");
}
let result = unsafe { k2h_add_attr_plugin_library(self.handle, path.as_ptr()) };
if result {
Ok(())
} else {
Err("add_attribute_plugin_lib returns error")
}
}
pub fn add_decryption_password(&self, password: &str) -> Result<(), &'static str> {
let password = CString::new(password).unwrap();
if password.is_empty() {
return Err("Password should be a some string");
}
let result = unsafe { k2h_add_attr_crypt_pass(self.handle, password.as_ptr(), false) };
if result {
Ok(())
} else {
Err("add_decryption_password returns error")
}
}
pub fn add_subkey(&self, key: &str, subkey: &str, subval: &str) -> Result<(), &'static str> {
return self.add_subkey_with_options(key, subkey, subval, None, None);
}
pub fn add_subkey_with_options(
&self,
key: &str,
subkey: &str,
subval: &str,
password: Option<String>,
expire_duration: Option<u64>,
) -> Result<(), &'static str> {
let k = CString::new(key).unwrap();
let sk = CString::new(subkey).unwrap();
let sv = CString::new(subval).unwrap();
let pass = password.as_ref().map(|p| CString::new(p.as_str()).unwrap());
let c_pass = pass.as_ref().map_or(ptr::null(), |p| p.as_ptr());
let expire = expire_duration.map(|e| e as c_ulonglong);
let c_expire = expire
.as_ref()
.map_or(ptr::null(), |e| e as *const c_ulonglong);
let result = unsafe {
k2h_add_subkey_wa(
self.handle,
k.as_bytes_with_nul().as_ptr(),
k.as_bytes_with_nul().len(),
sk.as_bytes_with_nul().as_ptr(),
sk.as_bytes_with_nul().len(),
sv.as_bytes_with_nul().as_ptr(),
sv.as_bytes_with_nul().len(),
c_pass,
c_expire,
)
};
if result {
Ok(())
} else {
Err("k2h_add_subkey_wa returns error")
}
}
pub fn begin_tx(&self, txfile: &str) -> Result<(), &'static str> {
return self.begin_tx_with_options(txfile, None, None, None);
}
pub fn begin_tx_with_options(
&self,
txfile: &str,
prefix: Option<String>,
param: Option<String>,
expire_duration: Option<u64>,
) -> Result<(), &'static str> {
let txf = CString::new(txfile).unwrap();
let pre = prefix.as_ref().map(|p| CString::new(p.as_str()).unwrap());
let c_pre = pre
.as_ref()
.map_or(ptr::null(), |p| p.as_bytes_with_nul().as_ptr());
let pre_length = pre.as_ref().map(|c| c.as_bytes_with_nul().len());
let par = param.as_ref().map(|p| CString::new(p.as_str()).unwrap());
let c_par = par
.as_ref()
.map_or(ptr::null(), |p| p.as_bytes_with_nul().as_ptr());
let par_length = par.as_ref().map(|c| c.as_bytes_with_nul().len());
let expire = expire_duration.map(|e| e as c_ulonglong);
let c_expire = expire
.as_ref()
.map_or(ptr::null(), |e| e as *const c_ulonglong);
let result = unsafe {
k2h_transaction_param_we(
self.handle,
true,
txf.as_ptr(),
c_pre,
pre_length.unwrap_or(0), c_par,
par_length.unwrap_or(0), c_expire,
)
};
if result {
Ok(())
} else {
Err("begin_tx returns error")
}
}
pub fn create(path: &str) -> Result<(), &'static str> {
return K2hash::create_with_options(path, 8, 4, 1024, 512);
}
pub fn create_with_options(
path: &str,
maskbit: i32,
cmaskbit: i32,
maxelementcnt: i32,
pagesize: usize,
) -> Result<(), &'static str> {
let p = CString::new(path).unwrap();
if p.is_empty() {
return Err("path cannot be empty");
}
let maskbit_len: i32 = maskbit;
let cmaskbit_len: i32 = cmaskbit;
let maxelementcnt_len: i32 = maxelementcnt;
let pagesize_len: usize = pagesize;
let result = unsafe {
k2h_create(
p.as_ptr(),
maskbit_len,
cmaskbit_len,
maxelementcnt_len,
pagesize_len,
)
};
if result {
Ok(())
} else {
Err("k2h_create returns error")
}
}
pub fn dump_to_file(&self, path: &str) -> Result<(), &'static str> {
return self.dump_to_file_with_options(path, true);
}
pub fn dump_to_file_with_options(
&self,
path: &str,
is_skip_error: bool,
) -> Result<(), &'static str> {
let p = CString::new(path).unwrap();
if p.is_empty() {
return Err("path cannot be empty");
}
let i: bool = is_skip_error;
let result = unsafe { k2h_put_archive(self.handle, p.as_ptr(), i) };
if result {
Ok(())
} else {
Err("k2h_create returns error")
}
}
pub fn enable_encryption(&self, enable: bool) -> Result<(), &'static str> {
let is_defenc = enable;
let result = unsafe {
k2h_set_common_attr(
self.handle,
ptr::null(), &is_defenc as *const bool, ptr::null(), ptr::null(), ptr::null(), )
};
if result {
Ok(())
} else {
Err("k2h_create returns error")
}
}
pub fn enable_history(&self, enable: bool) -> Result<(), &'static str> {
let is_history = enable;
let result = unsafe {
k2h_set_common_attr(
self.handle,
ptr::null(), ptr::null(), ptr::null(), &is_history as *const bool, ptr::null(), )
};
if result {
Ok(())
} else {
Err("k2h_create returns error")
}
}
pub fn enable_mtime(&self, enable: bool) -> Result<(), &'static str> {
let is_mtime = enable;
let result = unsafe {
k2h_set_common_attr(
self.handle,
&is_mtime as *const bool, ptr::null(), ptr::null(), ptr::null(), ptr::null(), )
};
if result {
Ok(())
} else {
Err("k2h_create returns error")
}
}
pub fn get_attributes(
&self,
key: &str,
) -> Result<Option<HashMap<String, String>>, &'static str> {
let k = CString::new(key).unwrap();
if k.is_empty() {
return Err("key should be passed");
}
let k_length = k.as_bytes_with_nul().len();
let mut pattrspckcnt: c_int = 0; let result = unsafe {
k2h_get_direct_attrs(
self.handle,
k.as_bytes_with_nul().as_ptr(), k_length,
&mut pattrspckcnt, )
};
println!("pattrspckcnt: {}", pattrspckcnt);
if !result.is_null() && pattrspckcnt > 0 {
let mut attrs: HashMap<String, String> = HashMap::new();
for i in 0..pattrspckcnt {
let attr_pack: &K2hAttrPack =
unsafe { &*(result as *const K2hAttrPack).add(i as usize) };
let key = unsafe {
CStr::from_ptr(attr_pack.pkey as *const c_char)
.to_string_lossy()
.into_owned()
};
let val = unsafe {
CStr::from_ptr(attr_pack.pval as *const c_char)
.to_string_lossy()
.into_owned()
};
let keylength = unsafe { attr_pack.keylength as usize };
let vallength = unsafe { attr_pack.vallength as usize };
println!("keylength: {}", keylength);
println!("vallength: {}", vallength);
attrs.insert(key, val);
}
Ok(Some(attrs))
} else {
Err("k2h_get_direct_attrs returns error")
}
}
pub fn get_subkeys(&self, key: &str) -> Result<Option<Vec<String>>, &'static str> {
let k = CString::new(key).unwrap();
if k.is_empty() {
return Err("key should be passed");
}
let k_length = k.as_bytes_with_nul().len();
let mut pskeypckcnt: c_int = 0;
let result = unsafe {
k2h_get_direct_subkeys(
self.handle,
k.as_bytes_with_nul().as_ptr(),
k_length,
&mut pskeypckcnt,
)
};
if !result.is_null() && pskeypckcnt > 0 {
let mut keys: Vec<String> = Vec::new();
for i in 0..pskeypckcnt {
let key_pack: &K2hKeyPack =
unsafe { &*(result as *const K2hKeyPack).add(i as usize) };
let key = unsafe {
CStr::from_ptr(key_pack.pkey as *const c_char)
.to_string_lossy()
.into_owned()
};
keys.push(key);
}
Ok(Some(keys))
} else {
Err("k2h_get_direct_subkeys returns error")
}
}
pub fn get_tx_file_fd(&self) -> Result<i32, &'static str> {
let fd = unsafe { k2h_get_transaction_archive_fd(self.handle) };
if fd < 0 {
Err("Failed to get result")
} else {
Ok(fd)
}
}
pub fn get_tx_pool_size() -> Result<i32, &'static str> {
let pool_size = unsafe { k2h_get_transaction_thread_pool() };
if pool_size < 0 {
Err("Failed to get result")
} else {
Ok(pool_size)
}
}
pub fn load_from_file(
&self,
path: &str,
is_skip_error: Option<bool>,
) -> Result<(), &'static str> {
let p = CString::new(path).unwrap();
let skip_error = is_skip_error.unwrap_or(true);
if p.is_empty() {
return Err("Failed to get path");
}
let result = unsafe { k2h_load_archive(self.handle, p.as_ptr(), skip_error) };
if result {
Ok(())
} else {
Err("k2h_load_archive returns error")
}
}
pub fn print_attribute_plugins(&self) -> Result<(), &'static str> {
let result = unsafe { k2h_print_attr_version(self.handle, core::ptr::null_mut()) };
if result {
Ok(())
} else {
Err("k2h_print_attr_version returns error")
}
}
pub fn print_attributes(&self) -> Result<(), &'static str> {
let result = unsafe { k2h_print_attr_information(self.handle, core::ptr::null_mut()) };
if result {
Ok(())
} else {
Err("k2h_print_attr_information returns error")
}
}
pub fn print_data_stats(&self) -> Result<(), &'static str> {
let result = unsafe { k2h_print_state(self.handle, core::ptr::null_mut()) };
if result {
Ok(())
} else {
Err("k2h_print_state returns error")
}
}
pub fn print_table_stats(&self, dump_level: DumpLevel) -> Result<(), &'static str> {
let dl = dump_level;
if dl == DumpLevel::HEADER {
let result = unsafe { k2h_dump_head(self.handle, core::ptr::null_mut()) };
if result {
Ok(())
} else {
Err("k2h_dump_head returns error")
}
} else if dl == DumpLevel::HASHTABLE {
let result = unsafe { k2h_dump_keytable(self.handle, core::ptr::null_mut()) };
if result {
Ok(())
} else {
Err("k2h_dump_head returns error")
}
} else if dl == DumpLevel::SUBHASHTABLE {
let result = unsafe { k2h_dump_full_keytable(self.handle, core::ptr::null_mut()) };
if result {
Ok(())
} else {
Err("k2h_dump_head returns error")
}
} else if dl == DumpLevel::ELEMENT {
let result = unsafe { k2h_dump_elementtable(self.handle, core::ptr::null_mut()) };
if result {
Ok(())
} else {
Err("k2h_dump_head returns error")
}
} else if dl == DumpLevel::PAGE {
let result = unsafe { k2h_dump_full(self.handle, core::ptr::null_mut()) };
if result {
Ok(())
} else {
Err("k2h_dump_head returns error")
}
} else {
return Err("k2h_dump_head returns error");
}
}
pub fn remove(&self, key: &str) -> Result<(), &'static str> {
return self.remove_with_options(key, false);
}
pub fn remove_with_options(
&self,
key: &str,
remove_all_subkeys: bool,
) -> Result<(), &'static str> {
let k = CString::new(key).unwrap();
let b_remove_all_subkeys = remove_all_subkeys;
if k.is_empty() {
return Err("key should be passed");
}
if b_remove_all_subkeys {
let result = unsafe { k2h_remove_str_all(self.handle, k.as_ptr()) };
if result {
Ok(())
} else {
Err("Failed to h.k2h_remove_str_all")
}
} else {
let result = unsafe { k2h_remove_str(self.handle, k.as_ptr()) };
if result {
Ok(())
} else {
Err("Failed to set value")
}
}
}
pub fn remove_subkeys(&self, key: &str, subkeys: Vec<&str>) -> Result<(), &'static str> {
let k = CString::new(key).unwrap();
if k.is_empty() {
return Err("key should be passed");
}
let v_subkeys_iter = subkeys.iter();
for skey in v_subkeys_iter {
let sk = CString::new(*skey).unwrap();
let result = unsafe { k2h_remove_str_subkey(self.handle, k.as_ptr(), sk.as_ptr()) };
if result == false {
return Err("Failed to h.k2h_remove_str_all");
}
}
Ok(())
}
pub fn rename(&self, oldkey: &str, newkey: &str) -> Result<(), &'static str> {
let okey = CString::new(oldkey).unwrap();
if okey.is_empty() {
return Err("oldkey should be passed");
}
let nkey = CString::new(newkey).unwrap();
if nkey.is_empty() {
return Err("newkey should be passed");
}
let result = unsafe { k2h_rename_str(self.handle, okey.as_ptr(), nkey.as_ptr()) };
if result {
Ok(())
} else {
Err("Failed to h.k2h_rename")
}
}
pub fn set_attribute(
&self,
key: &str,
attr_name: &str,
attr_val: &str,
) -> Result<(), &'static str> {
let key = CString::new(key).unwrap();
if key.as_bytes().is_empty() {
return Err("key should be passed");
}
let k_length = key.as_bytes_with_nul().len();
let attr_name = CString::new(attr_name).unwrap();
if attr_name.as_bytes().is_empty() {
return Err("attr_name should be passed");
}
let name_length = attr_name.as_bytes_with_nul().len();
let attr_val = CString::new(attr_val).unwrap();
if attr_val.as_bytes().is_empty() {
return Err("attr_val should be passed");
}
let val_length = attr_val.as_bytes_with_nul().len();
let result = unsafe {
k2h_add_attr(
self.handle,
key.as_bytes_with_nul().as_ptr(),
k_length,
attr_name.as_bytes_with_nul().as_ptr(),
name_length,
attr_val.as_bytes_with_nul().as_ptr(),
val_length,
)
};
if result {
Ok(())
} else {
Err("Failed to h.k2h_set_attribute")
}
}
pub fn set_default_encryption_password(&self, password: &str) -> Result<(), &'static str> {
let p = CString::new(password).unwrap();
if p.is_empty() {
return Err("password should be passed");
}
let result = unsafe { k2h_add_attr_crypt_pass(self.handle, p.as_ptr(), true) };
if result {
Ok(())
} else {
Err("Failed to set value")
}
}
pub fn set_subkeys(&self, key: &str, subkeys: Vec<(&str, &str)>) -> Result<(), &'static str> {
return self.set_subkeys_with_options(key, subkeys, None, None);
}
pub fn set_subkeys_with_options(
&self,
key: &str,
subkeys: Vec<(&str, &str)>,
password: Option<String>,
expire_duration: Option<u64>,
) -> Result<(), &'static str> {
let k = CString::new(key).unwrap();
if k.is_empty() {
return Err("key should be passed");
}
let k_length = k.as_bytes_with_nul().len();
let pass = password.as_ref().map(|p| CString::new(p.as_str()).unwrap());
let c_pass = pass.as_ref().map_or(ptr::null(), |p| p.as_ptr());
let expire = expire_duration.map(|e| e as c_ulonglong);
let c_expire = expire
.as_ref()
.map_or(ptr::null(), |e| e as *const c_ulonglong);
let v_subkeys_iter = subkeys.iter();
for (subk, subv) in v_subkeys_iter {
let sk = CString::new(*subk).unwrap();
let sv = CString::new(*subv).unwrap();
if sk.as_bytes().is_empty() {
return Err("subkey's key should be passed");
}
let sk_length = sk.as_bytes_with_nul().len();
if sv.as_bytes().is_empty() {
return Err("subkey's val should be passed");
}
let sv_length = sv.as_bytes_with_nul().len();
let result = unsafe {
k2h_add_subkey_wa(
self.handle,
k.as_bytes_with_nul().as_ptr(),
k_length,
sk.as_bytes_with_nul().as_ptr(),
sk_length,
sv.as_bytes_with_nul().as_ptr(),
sv_length,
c_pass,
c_expire,
)
};
if result == false {
return Err("Failed to h.k2h_remove_str_all");
}
}
Ok(())
}
pub fn stop_tx(&self) -> Result<(), &'static str> {
let result = unsafe { k2h_disable_transaction(self.handle) };
if result {
Ok(())
} else {
Err("k2h_disable_transaction returns error")
}
}
pub fn version() -> Result<(), &'static str> {
unsafe { k2h_print_version(core::ptr::null_mut()) };
Ok(())
}
pub fn set_tx_pool_size(size: i32) -> Result<(), &'static str> {
if size < 0 {
return Err("size should be 0 or positive");
}
let result = unsafe { k2h_set_transaction_thread_pool(size) };
if result {
Ok(())
} else {
Err("Failed to set value")
}
}
}
impl Drop for K2hash {
fn drop(&mut self) {
unsafe {
k2h_close(self.handle);
}
}
}
pub struct BaseQueue {
k2h: u64,
fifo: bool,
prefix: Option<String>,
password: Option<String>,
expire_duration: Option<u64>,
handle: u64,
}
impl BaseQueue {
pub fn new(
k2h: u64,
fifo: bool,
prefix: Option<String>,
password: Option<String>,
expire_duration: Option<u64>,
) -> Self {
BaseQueue {
k2h,
fifo,
prefix,
password,
expire_duration,
handle: 0,
}
}
pub fn handle(&self) -> u64 {
self.handle
}
}
pub struct Queue {
base: BaseQueue,
}
pub struct KeyQueue {
base: BaseQueue,
}
impl Queue {
pub fn new(
k2h: u64,
fifo: bool,
prefix: Option<String>,
password: Option<String>,
expire_duration: Option<u64>,
) -> Result<Self, &'static str> {
let mut base = BaseQueue::new(k2h, fifo, prefix, password, expire_duration);
let c_prefix = base
.prefix
.as_ref()
.map(|p| CString::new(p.as_str()).unwrap());
let ptr = c_prefix.as_ref().map_or(ptr::null(), |c| c.as_ptr());
base.handle = unsafe { k2h_q_handle_str_prefix(base.k2h, base.fifo, ptr) };
if base.handle == 0 {
Err("Queue instance failed")
} else {
Ok(Queue { base })
}
}
pub fn handle(&self) -> u64 {
self.base.handle
}
pub fn put(&self, value: &str) -> Result<(), &'static str> {
let c_val = CString::new(value).unwrap();
let c_pass = self
.base
.password
.as_ref()
.map(|p| CString::new(p.as_str()).unwrap());
let c_pattrspck = ptr::null();
let c_attrspckcnt = 0;
let expire = self.base.expire_duration.map(|e| e as c_ulonglong);
let result = unsafe {
k2h_q_str_push_wa(
self.base.handle,
c_val.as_ptr(),
c_pattrspck,
c_attrspckcnt,
c_pass.as_ref().map_or(ptr::null(), |p| p.as_ptr()),
expire
.as_ref()
.map_or(ptr::null(), |e| e as *const c_ulonglong),
)
};
if result {
Ok(())
} else {
Err("Failed to set value")
}
}
pub fn get(&self) -> Option<String> {
let mut val_ptr: *mut c_char = ptr::null_mut();
let c_pass = self
.base
.password
.as_ref()
.map(|p| CString::new(p.as_str()).unwrap());
let result = unsafe {
k2h_q_str_pop_wp(
self.base.handle,
&mut val_ptr,
c_pass.as_ref().map_or(ptr::null(), |p| p.as_ptr()),
)
};
if result && !val_ptr.is_null() {
let cstr = unsafe { CStr::from_ptr(val_ptr) };
Some(cstr.to_string_lossy().into_owned())
} else {
None
}
}
pub fn qsize(&self) -> usize {
unsafe { k2h_q_count(self.base.handle) as usize }
}
pub fn element(&self, position: usize) -> Option<String> {
let mut ppdata: *mut c_char = ptr::null_mut();
let c_pass = self
.base
.password
.as_ref()
.map(|p| CString::new(p.as_str()).unwrap());
let result = unsafe {
k2h_q_str_read_wp(
self.base.handle,
&mut ppdata,
position as c_int,
c_pass.as_ref().map_or(ptr::null(), |p| p.as_ptr()),
)
};
if result && !ppdata.is_null() {
let cstr = unsafe { CStr::from_ptr(ppdata) };
Some(cstr.to_string_lossy().into_owned())
} else {
None
}
}
pub fn empty(&self) -> bool {
unsafe { k2h_q_empty(self.base.handle) }
}
pub fn print(&self) -> Result<(), &'static str> {
let result = unsafe { k2h_q_dump(self.base.handle, ptr::null_mut()) };
if result {
Ok(())
} else {
Err("k2h_q_dump returns error")
}
}
pub fn remove(&self, count: usize) -> Result<Vec<String>, &'static str> {
if count == 0 {
return Ok(Vec::new());
}
let mut vals = Vec::new();
for _ in 0..count {
let mut val_ptr: *mut c_char = ptr::null_mut();
let c_pass = self
.base
.password
.as_ref()
.map(|p| CString::new(p.as_str()).unwrap());
let result = unsafe {
k2h_q_str_pop_wp(
self.base.handle,
&mut val_ptr,
c_pass.as_ref().map_or(ptr::null(), |p| p.as_ptr()),
)
};
if result && !val_ptr.is_null() {
let cstr = unsafe { CStr::from_ptr(val_ptr) };
vals.push(cstr.to_string_lossy().into_owned());
} else {
break; }
}
Ok(vals)
}
pub fn clear(&self) -> bool {
let count = self.qsize() as c_int;
if count > 0 {
unsafe { k2h_q_remove(self.base.handle, count) }
} else {
true
}
}
pub fn close(&self) -> bool {
unsafe { k2h_q_free(self.base.handle) }
}
}
pub struct QueueBuilder {
k2h: u64,
fifo: bool,
prefix: Option<String>,
password: Option<String>,
expire_duration: Option<u64>,
}
impl QueueBuilder {
pub fn new(k2h: u64) -> Self {
QueueBuilder {
k2h,
fifo: true,
prefix: None,
password: None,
expire_duration: None,
}
}
pub fn fifo(mut self, fifo: bool) -> Self {
self.fifo = fifo;
self
}
pub fn prefix(mut self, prefix: String) -> Self {
self.prefix = Some(prefix);
self
}
pub fn password(mut self, password: String) -> Self {
self.password = Some(password);
self
}
pub fn expire_duration(mut self, expire_duration: u64) -> Self {
self.expire_duration = Some(expire_duration);
self
}
pub fn build(self) -> Result<Queue, &'static str> {
Queue::new(
self.k2h,
self.fifo,
self.prefix,
self.password,
self.expire_duration,
)
}
}
impl KeyQueue {
pub fn new(
k2h: u64,
fifo: bool,
prefix: Option<String>,
password: Option<String>,
expire_duration: Option<u64>,
) -> Result<Self, &'static str> {
let mut base = BaseQueue::new(k2h, fifo, prefix, password, expire_duration);
let c_prefix = base
.prefix
.as_ref()
.map(|p| CString::new(p.as_str()).unwrap());
let ptr = c_prefix.as_ref().map_or(ptr::null(), |c| c.as_ptr());
base.handle = unsafe { k2h_keyq_handle_str_prefix(base.k2h, base.fifo, ptr) };
if base.handle == 0 {
Err("Failed to create KeyQueue handle")
} else {
Ok(KeyQueue { base })
}
}
pub fn handle(&self) -> u64 {
self.base.handle
}
pub fn put(&self, key: &str, value: &str) -> Result<(), &'static str> {
let c_key = CString::new(key).unwrap();
let c_val = CString::new(value).unwrap();
let c_pass = self
.base
.password
.as_ref()
.map(|p| CString::new(p.as_str()).unwrap());
let expire = self.base.expire_duration.map(|e| e as c_ulonglong);
let result = unsafe {
k2h_keyq_str_push_keyval_wa(
self.base.handle,
c_key.as_ptr(),
c_val.as_ptr(),
c_pass.as_ref().map_or(ptr::null(), |p| p.as_ptr()),
expire
.as_ref()
.map_or(ptr::null(), |e| e as *const c_ulonglong),
)
};
if result {
Ok(())
} else {
Err("Failed to set value")
}
}
pub fn get(&self) -> Option<(String, String)> {
let mut key_ptr: *mut c_char = ptr::null_mut();
let mut val_ptr: *mut c_char = ptr::null_mut();
let c_pass = self
.base
.password
.as_ref()
.map(|p| CString::new(p.as_str()).unwrap());
let result = unsafe {
k2h_keyq_str_pop_keyval_wp(
self.base.handle,
&mut key_ptr,
&mut val_ptr,
c_pass.as_ref().map_or(ptr::null(), |p| p.as_ptr()),
)
};
if result && !key_ptr.is_null() && !val_ptr.is_null() {
let key = unsafe { CStr::from_ptr(key_ptr).to_string_lossy().into_owned() };
let val = unsafe { CStr::from_ptr(val_ptr).to_string_lossy().into_owned() };
Some((key, val))
} else {
None
}
}
pub fn qsize(&self) -> usize {
unsafe { k2h_keyq_count(self.base.handle) as usize }
}
pub fn element(&self, position: usize) -> Option<(String, String)> {
if position < 0 {
return None; }
let mut ppkey: *mut c_char = ptr::null_mut();
let mut ppval: *mut c_char = ptr::null_mut();
let c_pass = self
.base
.password
.as_ref()
.map(|p| CString::new(p.as_str()).unwrap());
let result = unsafe {
k2h_keyq_str_read_keyval_wp(
self.base.handle,
&mut ppkey,
&mut ppval,
position as c_int,
c_pass.as_ref().map_or(ptr::null(), |p| p.as_ptr()),
)
};
if result && !ppkey.is_null() && !ppval.is_null() {
let key = unsafe { CStr::from_ptr(ppkey).to_string_lossy().into_owned() };
let val = unsafe { CStr::from_ptr(ppval).to_string_lossy().into_owned() };
Some((key, val))
} else {
None
}
}
pub fn empty(&self) -> bool {
unsafe { k2h_keyq_empty(self.base.handle) }
}
pub fn print(&self) -> Result<(), &'static str> {
let result = unsafe { k2h_keyq_dump(self.base.handle, ptr::null_mut()) };
if result {
Ok(())
} else {
Err("k2h_keyq_dump returns error")
}
}
pub fn remove(&self, count: usize) -> Result<Vec<(String, String)>, &'static str> {
if count == 0 {
return Ok(Vec::new());
}
let mut vals = Vec::new();
for _ in 0..count {
let mut key_ptr: *mut c_char = ptr::null_mut();
let mut val_ptr: *mut c_char = ptr::null_mut();
let c_pass = self
.base
.password
.as_ref()
.map(|p| CString::new(p.as_str()).unwrap());
let result = unsafe {
k2h_keyq_str_pop_keyval_wp(
self.base.handle,
&mut key_ptr,
&mut val_ptr,
c_pass.as_ref().map_or(ptr::null(), |p| p.as_ptr()),
)
};
if result && !key_ptr.is_null() && !val_ptr.is_null() {
let key = unsafe { CStr::from_ptr(key_ptr).to_string_lossy().into_owned() };
let val = unsafe { CStr::from_ptr(val_ptr).to_string_lossy().into_owned() };
vals.push((key, val));
} else {
break; }
}
Ok(vals)
}
pub fn clear(&self) -> bool {
let count = self.qsize() as c_int;
if count > 0 {
unsafe { k2h_keyq_remove(self.base.handle, count) }
} else {
true
}
}
pub fn close(&self) -> bool {
unsafe { k2h_keyq_free(self.base.handle) }
}
}
pub struct KeyQueueBuilder {
k2h: u64,
fifo: bool,
prefix: Option<String>,
password: Option<String>,
expire_duration: Option<u64>,
}
impl KeyQueueBuilder {
pub fn new(k2h: u64) -> Self {
KeyQueueBuilder {
k2h,
fifo: true,
prefix: None,
password: None,
expire_duration: None,
}
}
pub fn fifo(mut self, fifo: bool) -> Self {
self.fifo = fifo;
self
}
pub fn prefix(mut self, prefix: String) -> Self {
self.prefix = Some(prefix);
self
}
pub fn password(mut self, password: String) -> Self {
self.password = Some(password);
self
}
pub fn expire_duration(mut self, expire_duration: u64) -> Self {
self.expire_duration = Some(expire_duration);
self
}
pub fn build(self) -> Result<KeyQueue, &'static str> {
KeyQueue::new(
self.k2h,
self.fifo,
self.prefix,
self.password,
self.expire_duration,
)
}
}