use tiny_keccak::{Hasher, Keccak};
use crate::{
easycodec::EasyCodec,
sim_context::{self, SimContext},
};
const KEY_NAME: &str = "name";
const KEY_DEPTH: &str = "depth";
pub struct StoreMap<'a, C>
where
C: SimContext,
{
name: String,
depth: i64,
ctx: &'a C,
}
impl<C: SimContext> StoreMap<'_, C> {
pub fn new<'a>(ctx: &'a C, name: &str, depth: i64) -> Result<StoreMap<'a, C>, String> {
if depth <= 0 {
return Err("depth must be greater than zero".to_string());
}
if name.is_empty() {
return Err("name cannot be empty".to_string());
}
let map_key = get_hash(name.as_bytes());
let mut state_key = name.to_string();
state_key.push_str(map_key.as_str());
let data = match ctx.get_state(state_key.as_str(), "") {
Ok(data) => data,
Err(_) => return Err("get state faile.".to_string()),
};
let store_map;
if data.len() != 0 {
let ec = EasyCodec::new_with_bytes(&data);
store_map = StoreMap {
name: ec.get_string(KEY_NAME).unwrap(),
depth: ec.get_i64(KEY_DEPTH).unwrap(),
ctx,
}
} else {
store_map = StoreMap {
name: name.to_string(),
depth,
ctx,
};
match store_map.save(state_key.as_str()) {
Ok(_) => {}
Err(e) => return Err(e),
}
}
Ok(store_map)
}
fn save(&self, state_key: &str) -> Result<(), String> {
let mut ec = EasyCodec::new();
ec.add_string(KEY_NAME, &self.name);
ec.add_i64(KEY_DEPTH, self.depth);
let data = ec.marshal();
if self.ctx.put_state(state_key, "", &data) != sim_context::SUCCESS_CODE {
return Err("failed to put state".to_string());
}
Ok(())
}
fn check_depth(&self, key: &Vec<String>) -> Option<String> {
for k in key {
if k.is_empty() {
return Some("key cannot be empty".to_string());
}
}
if self.depth != (key.len() as i64) {
return Some("please check keys".to_string());
}
None
}
fn generate_key(&self, key: &Vec<String>) -> (String, String) {
let mut field = get_hash(self.name.as_bytes());
for k in key {
field.push_str(k);
field = get_hash(field.as_bytes());
}
let mut gened_key = self.name.clone();
let end_key = key.join("");
gened_key.push_str(end_key.as_str());
(gened_key, field)
}
pub fn get(&self, key: &Vec<String>) -> Result<Vec<u8>, String> {
if let Some(e) = self.check_depth(key) {
return Err(e);
}
let (gened_key, field) = self.generate_key(key);
match self.ctx.get_state(&gened_key, &field) {
Ok(res) => Ok(res),
Err(_) => Err("get state fail".to_string()),
}
}
pub fn set(&self, key: &Vec<String>, value: &[u8]) -> Result<(), String> {
if let Some(e) = self.check_depth(key) {
return Err(e);
};
let (gened_key, field) = self.generate_key(key);
let re = self.ctx.put_state(&gened_key, &field, value);
if re != sim_context::SUCCESS_CODE {
return Err("failed to put state".to_string());
}
Ok(())
}
pub fn del(&self, key: &Vec<String>) -> Result<(), String> {
if let Some(e) = self.check_depth(key) {
return Err(e);
}
let (gened_key, field) = self.generate_key(key);
if self.ctx.delete_state(&gened_key, &field) != sim_context::SUCCESS_CODE {
return Err("failed to del state".to_string());
}
Ok(())
}
pub fn exist(&self, key: &Vec<String>) -> Result<bool, String> {
if let Some(e) = self.check_depth(key) {
return Err(e);
}
let (gened_key, field) = self.generate_key(key);
match self.ctx.get_state(&gened_key, &field) {
Ok(data) => {
if data.len() > 0 {
return Ok(true);
}
}
Err(_) => return Err("failed to get state".to_string()),
}
Ok(false)
}
}
fn get_hash(data: &[u8]) -> String {
let mut keccak = Keccak::v256();
keccak.update(data);
let mut output = [0u8; 32];
keccak.finalize(&mut output);
hex::encode(output)
}