use std::path::Path;
use std::hash::Hash;
use std::collections::HashMap;
use tokio::io::Result as TokioResult;
use tokio::io::ErrorKind;
use crate::col::Col;
pub trait ListKeyTrait<K> {
fn key(&self) -> K;
}
pub struct List<T, K> {
col: Col<T>,
ixmap: HashMap<K, usize>,
}
impl<K: Clone + Eq + Hash, T: Clone + ListKeyTrait<K>> List<T, K> {
pub async fn new(path: impl AsRef<Path>) -> TokioResult<Self> {
let mut col = Col::<T>::new(path).await?;
let ixmap = Self::_build_ixmap(&mut col).await?;
Ok(Self { col, ixmap })
}
pub fn exists(&self, key: &K) -> bool {
self.ixmap.contains_key(key)
}
pub async fn size(&self) -> TokioResult<usize> {
self.col.size().await
}
pub async fn list(&mut self) -> TokioResult<Vec<T>> {
self.col.get_all().await
}
pub async fn map(&mut self) -> TokioResult<HashMap<K, T>> {
Ok(
self.col.get_all().await?
.into_iter()
.map(|rec| (rec.key(), rec))
.collect()
)
}
pub async fn detail(&mut self, key: &K) -> TokioResult<T> {
if let Some(&ix) = self.ixmap.get(key) {
self.col.get(ix).await
} else {
Err(ErrorKind::NotFound.into())
}
}
pub async fn add(&mut self, rec: &T) -> TokioResult<()> {
let key = rec.key();
if !self.ixmap.contains_key(&key) {
let ix = self.col.push(rec).await?;
self.ixmap.insert(key, ix);
Ok(())
} else {
Err(ErrorKind::AlreadyExists.into())
}
}
pub async fn remove(&mut self, key: &K) -> TokioResult<()> {
if let Some(&ix) = self.ixmap.get(key) {
let size = self.col.size().await?;
let rec = self.col.get(size - 1).await?;
self.col.update(ix, &rec).await?;
self.col.resize(size - 1).await?;
self.ixmap.remove(key);
Ok(())
} else {
Err(ErrorKind::NotFound.into())
}
}
pub async fn modify(&mut self, key: &K, rec: &T) -> TokioResult<()> {
if let Some(&ix) = self.ixmap.get(key) {
let new_key = rec.key();
if &new_key == key {
self.col.update(ix, rec).await?;
Ok(())
} else if self.ixmap.contains_key(&new_key) {
Err(ErrorKind::AlreadyExists.into())
} else {
self.col.update(ix, rec).await?;
self.ixmap.remove(key);
self.ixmap.insert(new_key, ix);
Ok(())
}
} else {
Err(ErrorKind::NotFound.into())
}
}
async fn _build_ixmap(col: &mut Col<T>) -> TokioResult<HashMap<K, usize>> {
Ok(col.get_all().await?
.iter().enumerate()
.map(|(ix, rec)| (rec.key(), ix))
.collect()
)
}
}