use super::{Store, StoreError};
use std::collections::HashMap;
use std::fmt::{self, Display, Formatter, Result as FmtResult};
use std::hash::Hash;
use std::sync::Arc;
use thiserror::Error;
type StoreArc = Arc<dyn Store>;
type KeyList = Vec<String>;
type StoreKeysPair = (StoreArc, KeyList);
type StoreKeysList = Vec<StoreKeysPair>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SubKey {
String(String),
Usize(usize),
None,
}
impl Display for SubKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SubKey::String(sub_key) => write!(f, "{sub_key}"),
SubKey::Usize(sub_key) => write!(f, "{sub_key}"),
SubKey::None => write!(f, ""),
}
}
}
impl Hash for SubKey {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.to_string().hash(state);
}
}
pub type GetFullKeyAndStoreFn =
fn(&InStoreTable, SubKey) -> Result<(Arc<dyn Store>, String), InStoreTableError>;
pub type GetFullKeysAndStoresFn =
fn(&InStoreTable, Vec<SubKey>) -> Result<Vec<(Arc<dyn Store>, Vec<String>)>, InStoreTableError>;
#[cfg(feature = "stacked_mmr")]
#[derive(Debug, Clone)]
pub struct SubMMR {
pub size: usize,
pub key: String,
pub store: Arc<dyn Store>,
}
#[derive(Error, Debug)]
pub struct MissingValues {
pub stores_with_missing_keys: HashMap<String, Vec<String>>,
}
impl Display for MissingValues {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let mut formatted_output = String::new();
for (store, keys) in &self.stores_with_missing_keys {
let keys_str = keys.join(", ");
formatted_output.push_str(&format!("{store}: {keys_str}"));
formatted_output.push_str("; ");
}
if !formatted_output.is_empty() {
formatted_output.truncate(formatted_output.len() - 2);
}
write!(f, "{{{formatted_output}}}")
}
}
#[derive(Error, Debug)]
pub enum InStoreTableError {
#[error("Some keys were not found for stores: {0}")]
NotFound(#[from] MissingValues),
#[error("Store error: {0}")]
Store(#[from] StoreError),
#[error("Could not decode store key")]
CouldNotDecodeStoreKey,
#[error("Sub MMRs are not set")]
SubMMRsNotSet,
}
#[derive(Debug, Clone)]
pub struct InStoreTable {
pub store: Arc<dyn Store>,
pub key: String,
pub get_store_and_full_key: GetFullKeyAndStoreFn,
pub get_stores_and_full_keys: GetFullKeysAndStoresFn,
#[cfg(feature = "stacked_mmr")]
pub sub_mmrs: Option<Vec<SubMMR>>,
}
impl InStoreTable {
pub fn new(store: Arc<dyn Store>, key: String) -> Self {
Self {
store,
key,
get_store_and_full_key: Self::default_get_store_and_full_key,
get_stores_and_full_keys: Self::default_get_stores_and_full_keys,
#[cfg(feature = "stacked_mmr")]
sub_mmrs: None,
}
}
pub fn get_full_key(key: &str, sub_key: &str) -> String {
format!("{key}{sub_key}")
}
pub fn default_get_store_and_full_key(
&self,
sub_key: SubKey,
) -> Result<(Arc<dyn Store>, String), InStoreTableError> {
let new_sub_key = sub_key.to_string();
Ok((
self.store.clone(),
InStoreTable::get_full_key(&self.key, &new_sub_key),
))
}
pub fn default_get_stores_and_full_keys(
&self,
sub_keys: Vec<SubKey>,
) -> Result<StoreKeysList, InStoreTableError> {
let sub_keys: Vec<String> = sub_keys
.into_iter()
.map(|sub_key| InStoreTable::get_full_key(&self.key, &sub_key.to_string()))
.collect();
Ok(vec![(self.store.clone(), sub_keys)])
}
pub async fn get(&self, sub_key: SubKey) -> Result<Option<String>, InStoreTableError> {
let (store, full_key) = (self.get_store_and_full_key)(self, sub_key)?;
Ok(store.get(&full_key).await.unwrap_or_default())
}
pub async fn get_many(
&self,
sub_keys: Vec<SubKey>,
) -> Result<HashMap<String, String>, InStoreTableError> {
let stores_and_keys = (self.get_stores_and_full_keys)(self, sub_keys.clone())?;
let mut stores_with_missing_keys = HashMap::new();
let mut retrieved_sub_keys_as_string = HashMap::new();
for (store, keys) in stores_and_keys {
let keys_ref: Vec<&str> = keys.iter().map(AsRef::as_ref).collect();
let fetched = store.get_many(keys_ref).await?;
let missing_keys = keys
.into_iter()
.filter(|key| !fetched.contains_key(&key.to_string()))
.collect::<Vec<_>>();
if !missing_keys.is_empty() {
stores_with_missing_keys.insert(store.id(), missing_keys);
}
for (key, value) in fetched.iter() {
let new_key: String = if key.contains(':') {
key.split(':').skip(2).collect::<Vec<&str>>().join(":")
} else {
key.clone()
};
retrieved_sub_keys_as_string.insert(new_key, value.clone());
}
}
if !stores_with_missing_keys.is_empty() {
Err(InStoreTableError::NotFound(MissingValues {
stores_with_missing_keys,
}))
} else {
Ok(retrieved_sub_keys_as_string)
}
}
pub async fn set(&self, value: &str, sub_key: SubKey) -> Result<(), InStoreTableError> {
let (store, key) = (self.get_store_and_full_key)(self, sub_key)?;
store.set(&key, value).await?;
Ok(())
}
pub async fn set_many(
&self,
entries: HashMap<SubKey, String>,
) -> Result<(), InStoreTableError> {
let mut store_entries = HashMap::new();
for (key, value) in entries.into_iter() {
let full_key = InStoreTable::get_full_key(&self.key, &key.to_string());
store_entries.insert(full_key, value.clone());
}
self.store.set_many(store_entries).await?;
Ok(())
}
}