use std::collections::BTreeSet;
use std::collections::BTreeMap;
use std::io;
use convert_case::Case;
use convert_case::Casing;
use serde::Deserialize;
use serde::Serialize;
use uuid::Uuid;
use crate::persistence::save_object;
use crate::persistence::FileDbKey;
use crate::persistence::Persist;
use crate::persistence::TargetFolder;
use crate::serde_multi_key;
fn type_format(name: &str) -> String {
format!("__#{}", name.to_case(Case::Snake))
}
pub fn index(mut index_keys: BTreeSet<String>, name: &str, id: &Uuid, target_folder: &TargetFolder) -> io::Result<Option<Uuid>> {
if !index_keys.is_empty() && index_keys.iter().any(|k| k.is_empty()) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("unauthorized empty index: {:?}", index_keys),
));
}
index_keys.insert(type_format(name));
if let Ok(mut multi_key_index) = Persist::<MultiKeyIndex>::get(target_folder) {
if let Some(existing_id) = multi_key_index.get(&index_keys) {
return Ok(Some(existing_id.clone()));
}
multi_key_index.insert(index_keys, id);
save_object::<MultiKeyIndex>(&multi_key_index, target_folder)?;
} else {
let multi_key_index = MultiKeyIndex::new().insert(index_keys, id);
save_object::<MultiKeyIndex>(&multi_key_index, target_folder)?;
}
Ok(None)
}
pub fn update(mut index_keys: BTreeSet<String>, name: &str, id: &Uuid, target_folder: &TargetFolder) -> io::Result<()> {
index_keys.insert(type_format(name));
let mut multi_key_index = Persist::<MultiKeyIndex>::get(target_folder)?;
multi_key_index.remove(id);
multi_key_index.insert(index_keys, id);
save_object::<MultiKeyIndex>(&multi_key_index, target_folder)?;
Ok(())
}
pub fn get_all_by_type(name: &str, target_folder: &TargetFolder) -> io::Result<Vec<Uuid>> {
Ok(if let Ok(multi_key_index) = Persist::<MultiKeyIndex>::get(target_folder) {
let r#type = type_format(name);
multi_key_index.find(&[&r#type])
} else {
Vec::new()
})
}
pub fn find_by_type(name: &str, keys: &[&str], target_folder: &TargetFolder) -> io::Result<Vec<Uuid>> {
Ok(if let Ok(multi_key_index) = Persist::<MultiKeyIndex>::get(target_folder) {
let mut all_keys = keys.to_vec();
let r#type = type_format(name);
all_keys.push(&r#type);
multi_key_index.find(&all_keys)
} else {
Vec::new()
})
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
struct MultiKeyIndex {
#[serde(with = "serde_multi_key")]
index: BTreeMap<BTreeSet<String>, Uuid>,
}
impl MultiKeyIndex {
fn new() -> Self {
Self {
index: BTreeMap::new(),
}
}
fn insert(&mut self, keys: BTreeSet<String>, id: &Uuid) -> Self {
self.index.insert(keys, *id);
self.clone()
}
fn get<K: IntoIterator<Item = impl Into<String>>>(&self, keys: K) -> Option<Uuid> {
let key_set: BTreeSet<String> = keys.into_iter().map(Into::into).collect();
self.index.get(&key_set).copied()
}
pub fn find(&self, keys: &[&str]) -> Vec<Uuid> {
self.index
.iter()
.filter(|(key_set, _)| {
keys.iter().all(|k| key_set.contains(*k))
})
.map(|(_, uuid)| *uuid)
.collect()
}
pub fn remove(&mut self, target_id: &Uuid) {
let keys_to_remove: Vec<_> = self.index.iter()
.filter(|(_, id)| id.eq(&target_id))
.map(|(key, _)| key.clone())
.collect();
for key in keys_to_remove {
self.index.remove(&key);
}
}
}
impl FileDbKey for MultiKeyIndex {
fn file_db_key() -> String {
String::from("index")
}
}
#[cfg(test)]
#[path = "../tests/unit_tests/index.rs"]
pub mod test;