use std::marker::PhantomData;
use std::borrow::Borrow;
use super::{BaseIndex, BaseIndexIter, Snapshot, Fork, StorageKey, StorageValue};
use super::indexes_metadata::IndexType;
#[derive(Debug)]
pub struct MapIndex<T, K, V> {
base: BaseIndex<T>,
_k: PhantomData<K>,
_v: PhantomData<V>,
}
#[derive(Debug)]
pub struct MapIndexIter<'a, K, V> {
base_iter: BaseIndexIter<'a, K, V>,
}
#[derive(Debug)]
pub struct MapIndexKeys<'a, K> {
base_iter: BaseIndexIter<'a, K, ()>,
}
#[derive(Debug)]
pub struct MapIndexValues<'a, V> {
base_iter: BaseIndexIter<'a, (), V>,
}
impl<T, K, V> MapIndex<T, K, V>
where
T: AsRef<Snapshot>,
K: StorageKey,
V: StorageValue,
{
pub fn new<S: AsRef<str>>(name: S, view: T) -> Self {
MapIndex {
base: BaseIndex::new(name, IndexType::Map, view),
_k: PhantomData,
_v: PhantomData,
}
}
pub fn with_prefix<S: AsRef<str>>(name: S, prefix: Vec<u8>, view: T) -> Self {
MapIndex {
base: BaseIndex::with_prefix(name, prefix, IndexType::Map, view),
_k: PhantomData,
_v: PhantomData,
}
}
pub fn get<Q>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: StorageKey + ?Sized,
{
self.base.get(key)
}
pub fn contains<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: StorageKey + ?Sized,
{
self.base.contains(key)
}
pub fn iter(&self) -> MapIndexIter<K, V> {
MapIndexIter { base_iter: self.base.iter(&()) }
}
pub fn keys(&self) -> MapIndexKeys<K> {
MapIndexKeys { base_iter: self.base.iter(&()) }
}
pub fn values(&self) -> MapIndexValues<V> {
MapIndexValues { base_iter: self.base.iter(&()) }
}
pub fn iter_from<Q>(&self, from: &Q) -> MapIndexIter<K, V>
where
K: Borrow<Q>,
Q: StorageKey + ?Sized,
{
MapIndexIter { base_iter: self.base.iter_from(&(), from) }
}
pub fn keys_from<Q>(&self, from: &Q) -> MapIndexKeys<K>
where
K: Borrow<Q>,
Q: StorageKey + ?Sized,
{
MapIndexKeys { base_iter: self.base.iter_from(&(), from) }
}
pub fn values_from<Q>(&self, from: &Q) -> MapIndexValues<V>
where
K: Borrow<Q>,
Q: StorageKey + ?Sized,
{
MapIndexValues { base_iter: self.base.iter_from(&(), from) }
}
}
impl<'a, K, V> MapIndex<&'a mut Fork, K, V>
where
K: StorageKey,
V: StorageValue,
{
pub fn put(&mut self, key: &K, value: V) {
self.base.put(key, value)
}
pub fn remove<Q>(&mut self, key: &Q)
where
K: Borrow<Q>,
Q: StorageKey + ?Sized,
{
self.base.remove(key)
}
pub fn clear(&mut self) {
self.base.clear()
}
}
impl<'a, T, K, V> ::std::iter::IntoIterator for &'a MapIndex<T, K, V>
where
T: AsRef<Snapshot>,
K: StorageKey,
V: StorageValue,
{
type Item = (K::Owned, V);
type IntoIter = MapIndexIter<'a, K, V>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, K, V> Iterator for MapIndexIter<'a, K, V>
where
K: StorageKey,
V: StorageValue,
{
type Item = (K::Owned, V);
fn next(&mut self) -> Option<Self::Item> {
self.base_iter.next()
}
}
impl<'a, K> Iterator for MapIndexKeys<'a, K>
where
K: StorageKey,
{
type Item = K::Owned;
fn next(&mut self) -> Option<Self::Item> {
self.base_iter.next().map(|(k, ..)| k)
}
}
impl<'a, V> Iterator for MapIndexValues<'a, V>
where
V: StorageValue,
{
type Item = V;
fn next(&mut self) -> Option<Self::Item> {
self.base_iter.next().map(|(.., v)| v)
}
}
#[cfg(test)]
mod tests {
use rand::{thread_rng, Rng};
use super::*;
use super::super::{MemoryDB, Database};
const IDX_NAME: &'static str = "idx_name";
#[test]
fn str_key() {
let db = MemoryDB::new();
let mut fork = db.fork();
const KEY: &str = "key_1";
let mut index: MapIndex<_, String, _> = MapIndex::new(IDX_NAME, &mut fork);
assert_eq!(false, index.contains(KEY));
index.put(&KEY.to_owned(), 0);
assert_eq!(true, index.contains(KEY));
index.remove(KEY);
assert_eq!(false, index.contains(KEY));
}
#[test]
fn u8_slice_key() {
let db = MemoryDB::new();
let mut fork = db.fork();
const KEY: &[u8] = &[1, 2, 3];
let mut index: MapIndex<_, Vec<u8>, _> = MapIndex::new(IDX_NAME, &mut fork);
assert_eq!(false, index.contains(KEY));
index.put(&KEY.to_owned(), 0);
assert_eq!(true, index.contains(KEY));
index.remove(KEY);
assert_eq!(false, index.contains(KEY));
}
fn iter(db: Box<Database>) {
let mut fork = db.fork();
let mut map_index = MapIndex::new(IDX_NAME, &mut fork);
map_index.put(&1u8, 1u8);
map_index.put(&2u8, 2u8);
map_index.put(&3u8, 3u8);
assert_eq!(
map_index.iter().collect::<Vec<(u8, u8)>>(),
vec![(1, 1), (2, 2), (3, 3)]
);
assert_eq!(
map_index.iter_from(&0).collect::<Vec<(u8, u8)>>(),
vec![(1, 1), (2, 2), (3, 3)]
);
assert_eq!(
map_index.iter_from(&1).collect::<Vec<(u8, u8)>>(),
vec![(1, 1), (2, 2), (3, 3)]
);
assert_eq!(
map_index.iter_from(&2).collect::<Vec<(u8, u8)>>(),
vec![(2, 2), (3, 3)]
);
assert_eq!(
map_index.iter_from(&4).collect::<Vec<(u8, u8)>>(),
Vec::<(u8, u8)>::new()
);
assert_eq!(map_index.keys().collect::<Vec<u8>>(), vec![1, 2, 3]);
assert_eq!(map_index.keys_from(&0).collect::<Vec<u8>>(), vec![1, 2, 3]);
assert_eq!(map_index.keys_from(&1).collect::<Vec<u8>>(), vec![1, 2, 3]);
assert_eq!(map_index.keys_from(&2).collect::<Vec<u8>>(), vec![2, 3]);
assert_eq!(
map_index.keys_from(&4).collect::<Vec<u8>>(),
Vec::<u8>::new()
);
assert_eq!(map_index.values().collect::<Vec<u8>>(), vec![1, 2, 3]);
assert_eq!(
map_index.values_from(&0).collect::<Vec<u8>>(),
vec![1, 2, 3]
);
assert_eq!(
map_index.values_from(&1).collect::<Vec<u8>>(),
vec![1, 2, 3]
);
assert_eq!(map_index.values_from(&2).collect::<Vec<u8>>(), vec![2, 3]);
assert_eq!(
map_index.values_from(&4).collect::<Vec<u8>>(),
Vec::<u8>::new()
);
map_index.remove(&1u8);
assert_eq!(
map_index.iter_from(&0u8).collect::<Vec<(u8, u8)>>(),
vec![(2, 2), (3, 3)]
);
assert_eq!(
map_index.iter_from(&1u8).collect::<Vec<(u8, u8)>>(),
vec![(2, 2), (3, 3)]
);
}
fn gen_tempdir_name() -> String {
thread_rng().gen_ascii_chars().take(10).collect()
}
mod memorydb_tests {
use std::path::Path;
use storage::{Database, MemoryDB};
use tempdir::TempDir;
fn create_database(_: &Path) -> Box<Database> {
Box::new(MemoryDB::new())
}
#[test]
fn test_iter() {
let dir = TempDir::new(super::gen_tempdir_name().as_str()).unwrap();
let path = dir.path();
let db = create_database(path);
super::iter(db);
}
}
mod rocksdb_tests {
use std::path::Path;
use storage::Database;
use tempdir::TempDir;
fn create_database(path: &Path) -> Box<Database> {
use storage::{RocksDB, RocksDBOptions};
let mut opts = RocksDBOptions::default();
opts.create_if_missing(true);
Box::new(RocksDB::open(path, &opts).unwrap())
}
#[test]
fn test_iter() {
let dir = TempDir::new(super::gen_tempdir_name().as_str()).unwrap();
let path = dir.path();
let db = create_database(path);
super::iter(db);
}
}
}