extern crate libc;
use super::{Wallet, WalletType};
use api::ErrorCode;
use errors::common::CommonError;
use errors::wallet::WalletError;
use utils::json::JsonDecodable;
use self::libc::c_char;
use std::error::Error;
use std::ffi::{CString, CStr, NulError};
use std::ptr;
use std::str::Utf8Error;
#[derive(Debug, Deserialize)]
pub struct PluggedWalletJSONValue {
pub key: String,
pub value: String
}
#[derive(Debug, Deserialize)]
pub struct PluggedWalletJSONValues {
pub values: Vec<PluggedWalletJSONValue>
}
impl<'a> JsonDecodable<'a> for PluggedWalletJSONValues {}
struct PluggedWallet {
name: String,
pool_name: String,
handle: i32,
set_handler: extern fn(handle: i32,
key: *const c_char,
value: *const c_char) -> ErrorCode,
get_handler: extern fn(handle: i32,
key: *const c_char,
value_ptr: *mut *const c_char) -> ErrorCode,
get_not_expired_handler: extern fn(handle: i32,
key: *const c_char,
value_ptr: *mut *const c_char) -> ErrorCode,
list_handler: extern fn(handle: i32,
key_prefix: *const c_char,
values_json_ptr: *mut *const c_char) -> ErrorCode,
close_handler: extern fn(handle: i32) -> ErrorCode,
free_handler: extern fn(handle: i32,
value: *const c_char) -> ErrorCode
}
impl PluggedWallet {
fn new(name: &str,
pool_name: &str,
handle: i32,
set_handler: extern fn(xhandle: i32,
key: *const c_char,
value: *const c_char) -> ErrorCode,
get_handler: extern fn(xhandle: i32,
key: *const c_char,
value_ptr: *mut *const c_char) -> ErrorCode,
get_not_expired_handler: extern fn(xhandle: i32,
key: *const c_char,
value_ptr: *mut *const c_char) -> ErrorCode,
list_handler: extern fn(xhandle: i32,
key_prefix: *const c_char,
values_json_ptr: *mut *const c_char) -> ErrorCode,
close_handler: extern fn(xhandle: i32) -> ErrorCode,
free_handler: extern fn(xhandle: i32,
value: *const c_char) -> ErrorCode) -> PluggedWallet {
PluggedWallet {
name: name.to_string(),
pool_name: pool_name.to_string(),
handle: handle,
set_handler: set_handler,
get_handler: get_handler,
list_handler: list_handler,
get_not_expired_handler: get_not_expired_handler,
close_handler: close_handler,
free_handler: free_handler
}
}
}
impl Wallet for PluggedWallet {
fn set(&self, key: &str, value: &str) -> Result<(), WalletError> {
let key = CString::new(key)?;
let value = CString::new(value)?;
let err = (self.set_handler)(self.handle,
key.as_ptr(),
value.as_ptr());
if err != ErrorCode::Success {
return Err(WalletError::PluggedWallerError(err));
}
Ok(())
}
fn get(&self, key: &str) -> Result<String, WalletError> {
let key = CString::new(key)?;
let mut value_ptr: *const c_char = ptr::null_mut();
let err = (self.get_handler)(self.handle,
key.as_ptr(),
&mut value_ptr);
if err != ErrorCode::Success {
return Err(WalletError::PluggedWallerError(err));
}
let result = unsafe {
CStr::from_ptr(value_ptr).to_str()?.to_string()
};
let err = (self.free_handler)(self.handle, value_ptr);
if err != ErrorCode::Success {
return Err(WalletError::PluggedWallerError(err));
}
Ok(result)
}
fn list(&self, key_prefix: &str) -> Result<Vec<(String, String)>, WalletError> {
let key_prefix = CString::new(key_prefix)?;
let mut values_json_ptr: *const c_char = ptr::null_mut();
let err = (self.list_handler)(self.handle,
key_prefix.as_ptr(),
&mut values_json_ptr);
if err != ErrorCode::Success {
return Err(WalletError::PluggedWallerError(err));
}
let values_json = unsafe {
CStr::from_ptr(values_json_ptr).to_str()?.to_string()
};
let err = (self.free_handler)(self.handle, values_json_ptr);
if err != ErrorCode::Success {
return Err(WalletError::PluggedWallerError(err));
}
let result = PluggedWalletJSONValues::from_json(values_json.as_str())?
.values
.iter()
.map(|value| (value.key.clone(), value.value.clone()))
.collect();
Ok(result)
}
fn get_not_expired(&self, key: &str) -> Result<String, WalletError> {
let key = CString::new(key)?;
let mut value_ptr: *const c_char = ptr::null_mut();
let err = (self.get_not_expired_handler)(self.handle,
key.as_ptr(),
&mut value_ptr);
if err != ErrorCode::Success {
return Err(WalletError::PluggedWallerError(err));
}
let result = unsafe {
CStr::from_ptr(value_ptr).to_str()?.to_string()
};
let err = (self.free_handler)(self.handle, value_ptr);
if err != ErrorCode::Success {
return Err(WalletError::PluggedWallerError(err));
}
Ok(result)
}
fn get_pool_name(&self) -> String {
self.pool_name.clone()
}
fn get_name(&self) -> String {
self.name.clone()
}
}
pub struct PluggedWalletType {
create_handler: extern fn(name: *const c_char,
config: *const c_char,
credentials: *const c_char) -> ErrorCode,
open_handler: extern fn(name: *const c_char,
config: *const c_char,
runtime_config: *const c_char,
credentials: *const c_char,
handle: *mut i32) -> ErrorCode,
set_handler: extern fn(handle: i32,
key: *const c_char,
value: *const c_char) -> ErrorCode,
get_handler: extern fn(handle: i32,
key: *const c_char,
value_ptr: *mut *const c_char) -> ErrorCode,
get_not_expired_handler: extern fn(handle: i32,
key: *const c_char,
value_ptr: *mut *const c_char) -> ErrorCode,
list_handler: extern fn(handle: i32,
key_prefix: *const c_char,
values_json_ptr: *mut *const c_char) -> ErrorCode,
close_handler: extern fn(handle: i32) -> ErrorCode,
delete_handler: extern fn(name: *const c_char,
config: *const c_char,
credentials: *const c_char) -> ErrorCode,
free_handler: extern fn(xhandle: i32,
value: *const c_char) -> ErrorCode,
}
impl PluggedWalletType {
pub fn new(create_handler: extern fn(name: *const c_char,
config: *const c_char,
credentials: *const c_char) -> ErrorCode,
open_handler: extern fn(name: *const c_char,
config: *const c_char,
runtime_config: *const c_char,
credentials: *const c_char,
handle: *mut i32) -> ErrorCode,
set_handler: extern fn(handle: i32,
key: *const c_char,
value: *const c_char) -> ErrorCode,
get_handler: extern fn(handle: i32,
key: *const c_char,
value_ptr: *mut *const c_char) -> ErrorCode,
get_not_expired_handler: extern fn(handle: i32,
key: *const c_char,
value_ptr: *mut *const c_char) -> ErrorCode,
list_handler: extern fn(handle: i32,
key_prefix: *const c_char,
values_json_ptr: *mut *const c_char) -> ErrorCode,
close_handler: extern fn(handle: i32) -> ErrorCode,
delete_handler: extern fn(name: *const c_char,
config: *const c_char,
credentials: *const c_char) -> ErrorCode,
free_handler: extern fn(xhandle: i32,
value: *const c_char) -> ErrorCode) -> PluggedWalletType {
PluggedWalletType {
create_handler: create_handler,
open_handler: open_handler,
set_handler: set_handler,
get_handler: get_handler,
get_not_expired_handler: get_not_expired_handler,
list_handler: list_handler,
close_handler: close_handler,
delete_handler: delete_handler,
free_handler: free_handler
}
}
}
impl WalletType for PluggedWalletType {
fn create(&self, name: &str, config: Option<&str>, credentials: Option<&str>) -> Result<(), WalletError> {
let name = CString::new(name)?;
let config = match config {
Some(config) => Some(CString::new(config)?),
None => None
};
let credentials = match credentials {
Some(credentials) => Some(CString::new(credentials)?),
None => None
};
let err = (self.create_handler)(name.as_ptr(),
config.as_ref().map_or(ptr::null(), |x| x.as_ptr()),
credentials.as_ref().map_or(ptr::null(), |x| x.as_ptr()));
if err != ErrorCode::Success {
return Err(WalletError::PluggedWallerError(err));
}
Ok(())
}
fn delete(&self, name: &str, config: Option<&str>, credentials: Option<&str>) -> Result<(), WalletError> {
let name = CString::new(name)?;
let config = match config {
Some(config) => Some(CString::new(config)?),
None => None
};
let credentials = match credentials {
Some(credentials) => Some(CString::new(credentials)?),
None => None
};
let err = (self.delete_handler)(name.as_ptr(),
config.as_ref().map_or(ptr::null(), |x| x.as_ptr()),
credentials.as_ref().map_or(ptr::null(), |x| x.as_ptr()));
if err != ErrorCode::Success {
return Err(WalletError::PluggedWallerError(err));
}
Ok(())
}
fn open(&self, name: &str, pool_name: &str, config: Option<&str>, runtime_config: Option<&str>, credentials: Option<&str>) -> Result<Box<Wallet>, WalletError> {
let mut handle: i32 = 0;
let cname = CString::new(name)?;
let config = match config {
Some(config) => Some(CString::new(config)?),
None => None
};
let runtime_config = match runtime_config {
Some(runtime_config) => Some(CString::new(runtime_config)?),
None => None
};
let credentials = match credentials {
Some(credentials) => Some(CString::new(credentials)?),
None => None
};
let err = (self.open_handler)(cname.as_ptr(),
config.as_ref().map_or(ptr::null(), |x| x.as_ptr()),
runtime_config.as_ref().map_or(ptr::null(), |x| x.as_ptr()),
credentials.as_ref().map_or(ptr::null(), |x| x.as_ptr()),
&mut handle);
if err != ErrorCode::Success {
return Err(WalletError::PluggedWallerError(err));
}
Ok(Box::new(
PluggedWallet::new(
name,
pool_name,
handle,
self.set_handler,
self.get_handler,
self.get_not_expired_handler,
self.list_handler,
self.close_handler,
self.free_handler)))
}
}
impl From<NulError> for WalletError {
fn from(err: NulError) -> WalletError {
WalletError::CommonError(CommonError::InvalidState(format!("Null symbols in wallet keys or values: {}", err.description())))
}
}
impl From<Utf8Error> for WalletError {
fn from(err: Utf8Error) -> WalletError {
WalletError::CommonError(CommonError::InvalidState(format!("Incorrect utf8 symbols in wallet keys or values: {}", err.description())))
}
}
#[cfg(test)]
mod tests {
use super::*;
use errors::wallet::WalletError;
use utils::inmem_wallet::InmemWallet;
use std::time::Duration;
use std::thread;
#[test]
fn plugged_wallet_type_new_works() {
InmemWallet::cleanup();
PluggedWalletType::new(
InmemWallet::create,
InmemWallet::open,
InmemWallet::set,
InmemWallet::get,
InmemWallet::get_not_expied,
InmemWallet::list,
InmemWallet::close,
InmemWallet::delete,
InmemWallet::free
);
InmemWallet::cleanup();
}
#[test]
fn plugged_wallet_type_create_works() {
InmemWallet::cleanup();
let wallet_type = PluggedWalletType::new(
InmemWallet::create,
InmemWallet::open,
InmemWallet::set,
InmemWallet::get,
InmemWallet::get_not_expied,
InmemWallet::list,
InmemWallet::close,
InmemWallet::delete,
InmemWallet::free
);
wallet_type.create("wallet1", None, None).unwrap();
InmemWallet::cleanup();
}
#[test]
fn plugged_wallet_type_create_works_for_twice() {
InmemWallet::cleanup();
let wallet_type = PluggedWalletType::new(
InmemWallet::create,
InmemWallet::open,
InmemWallet::set,
InmemWallet::get,
InmemWallet::get_not_expied,
InmemWallet::list,
InmemWallet::close,
InmemWallet::delete,
InmemWallet::free
);
wallet_type.create("wallet1", None, None).unwrap();
let res = wallet_type.create("wallet1", None, None);
assert_match!(Err(WalletError::PluggedWallerError(ErrorCode::CommonInvalidState)), res);
InmemWallet::cleanup();
}
#[test]
fn plugged_wallet_type_delete_works() {
InmemWallet::cleanup();
let wallet_type = PluggedWalletType::new(
InmemWallet::create,
InmemWallet::open,
InmemWallet::set,
InmemWallet::get,
InmemWallet::get_not_expied,
InmemWallet::list,
InmemWallet::close,
InmemWallet::delete,
InmemWallet::free
);
wallet_type.create("wallet1", None, None).unwrap();
wallet_type.delete("wallet1", None, None).unwrap();
wallet_type.create("wallet1", None, None).unwrap();
InmemWallet::cleanup();
}
#[test]
fn plugged_wallet_type_open_works() {
InmemWallet::cleanup();
let wallet_type = PluggedWalletType::new(
InmemWallet::create,
InmemWallet::open,
InmemWallet::set,
InmemWallet::get,
InmemWallet::get_not_expied,
InmemWallet::list,
InmemWallet::close,
InmemWallet::delete,
InmemWallet::free
);
wallet_type.create("wallet1", None, None).unwrap();
wallet_type.open("wallet1", "pool1", None, None, None).unwrap();
InmemWallet::cleanup();
}
#[test]
fn plugged_wallet_set_get_works() {
InmemWallet::cleanup();
let wallet_type = PluggedWalletType::new(
InmemWallet::create,
InmemWallet::open,
InmemWallet::set,
InmemWallet::get,
InmemWallet::get_not_expied,
InmemWallet::list,
InmemWallet::close,
InmemWallet::delete,
InmemWallet::free
);
wallet_type.create("wallet1", None, None).unwrap();
let wallet = wallet_type.open("wallet1", "pool1", None, None, None).unwrap();
wallet.set("key1", "value1").unwrap();
let value = wallet.get("key1").unwrap();
assert_eq!("value1", value);
InmemWallet::cleanup();
}
#[test]
fn plugged_wallet_set_get_works_for_reopen() {
InmemWallet::cleanup();
let wallet_type = PluggedWalletType::new(
InmemWallet::create,
InmemWallet::open,
InmemWallet::set,
InmemWallet::get,
InmemWallet::get_not_expied,
InmemWallet::list,
InmemWallet::close,
InmemWallet::delete,
InmemWallet::free
);
wallet_type.create("wallet1", None, None).unwrap();
{
let wallet = wallet_type.open("wallet1", "pool1", None, None, None).unwrap();
wallet.set("key1", "value1").unwrap();
}
let wallet = wallet_type.open("wallet1", "pool1", None, None, None).unwrap();
let value = wallet.get("key1").unwrap();
assert_eq!("value1", value);
InmemWallet::cleanup();
}
#[test]
fn plugged_wallet_get_works_for_unknown() {
InmemWallet::cleanup();
let wallet_type = PluggedWalletType::new(
InmemWallet::create,
InmemWallet::open,
InmemWallet::set,
InmemWallet::get,
InmemWallet::get_not_expied,
InmemWallet::list,
InmemWallet::close,
InmemWallet::delete,
InmemWallet::free
);
wallet_type.create("wallet1", None, None).unwrap();
let wallet = wallet_type.open("wallet1", "pool1", None, None, None).unwrap();
let value = wallet.get("key1");
assert_match!(Err(WalletError::PluggedWallerError(ErrorCode::WalletNotFoundError)), value);
InmemWallet::cleanup();
}
#[test]
fn plugged_wallet_set_get_works_for_update() {
InmemWallet::cleanup();
let wallet_type = PluggedWalletType::new(
InmemWallet::create,
InmemWallet::open,
InmemWallet::set,
InmemWallet::get,
InmemWallet::get_not_expied,
InmemWallet::list,
InmemWallet::close,
InmemWallet::delete,
InmemWallet::free
);
wallet_type.create("wallet1", None, None).unwrap();
let wallet = wallet_type.open("wallet1", "pool1", None, None, None).unwrap();
wallet.set("key1", "value1").unwrap();
let value = wallet.get("key1").unwrap();
assert_eq!("value1", value);
wallet.set("key1", "value2").unwrap();
let value = wallet.get("key1").unwrap();
assert_eq!("value2", value);
InmemWallet::cleanup();
}
#[test]
fn plugged_wallet_set_get_not_expired_works() {
InmemWallet::cleanup();
let wallet_type = PluggedWalletType::new(
InmemWallet::create,
InmemWallet::open,
InmemWallet::set,
InmemWallet::get,
InmemWallet::get_not_expied,
InmemWallet::list,
InmemWallet::close,
InmemWallet::delete,
InmemWallet::free
);
wallet_type.create("wallet1", None, None).unwrap();
let wallet = wallet_type.open("wallet1", "pool1", None, Some("{\"freshness_time\": 1}"), None).unwrap();
wallet.set("key1", "value1").unwrap();
thread::sleep(Duration::new(2, 0));
let value = wallet.get_not_expired("key1");
assert_match!(Err(WalletError::PluggedWallerError(ErrorCode::WalletNotFoundError)), value);
InmemWallet::cleanup();
}
#[test]
fn plugged_wallet_list_works() {
InmemWallet::cleanup();
let wallet_type = PluggedWalletType::new(
InmemWallet::create,
InmemWallet::open,
InmemWallet::set,
InmemWallet::get,
InmemWallet::get_not_expied,
InmemWallet::list,
InmemWallet::close,
InmemWallet::delete,
InmemWallet::free
);
wallet_type.create("wallet1", None, None).unwrap();
let wallet = wallet_type.open("wallet1", "pool1", None, None, None).unwrap();
wallet.set("key1::subkey1", "value1").unwrap();
wallet.set("key1::subkey2", "value2").unwrap();
let mut key_values = wallet.list("key1::").unwrap();
key_values.sort();
assert_eq!(2, key_values.len());
let (key, value) = key_values.pop().unwrap();
assert_eq!("key1::subkey2", key);
assert_eq!("value2", value);
let (key, value) = key_values.pop().unwrap();
assert_eq!("key1::subkey1", key);
assert_eq!("value1", value);
InmemWallet::cleanup();
}
#[test]
fn plugged_wallet_get_pool_name_works() {
InmemWallet::cleanup();
let wallet_type = PluggedWalletType::new(
InmemWallet::create,
InmemWallet::open,
InmemWallet::set,
InmemWallet::get,
InmemWallet::get_not_expied,
InmemWallet::list,
InmemWallet::close,
InmemWallet::delete,
InmemWallet::free
);
wallet_type.create("wallet1", None, None).unwrap();
let wallet = wallet_type.open("wallet1", "pool1", None, None, None).unwrap();
assert_eq!(wallet.get_pool_name(), "pool1");
InmemWallet::cleanup();
}
#[test]
fn plugged_wallet_get_name_works() {
InmemWallet::cleanup();
let wallet_type = PluggedWalletType::new(
InmemWallet::create,
InmemWallet::open,
InmemWallet::set,
InmemWallet::get,
InmemWallet::get_not_expied,
InmemWallet::list,
InmemWallet::close,
InmemWallet::delete,
InmemWallet::free
);
wallet_type.create("wallet1", None, None).unwrap();
let wallet = wallet_type.open("wallet1", "pool1", None, None, None).unwrap();
assert_eq!(wallet.get_name(), "wallet1");
InmemWallet::cleanup();
}
}