use std::collections::HashMap;
use std::sync::Arc;
use async_trait::async_trait;
use thiserror::Error;
use tokio::sync::Mutex;
use super::types::{LeafRecord, TreeInfo};
#[derive(Debug, Error)]
pub enum StoreError {
#[error("unknown tree: {tree}")]
UnknownTree { tree: String },
}
pub type StoreResult<T> = Result<T, StoreError>;
#[async_trait]
pub trait CnftStore: Send + Sync {
async fn put_tree(&self, info: TreeInfo) -> StoreResult<()>;
async fn get_tree(&self, tree: &[u8; 32]) -> StoreResult<Option<TreeInfo>>;
async fn alloc_leaf_index(&self, tree: &[u8; 32]) -> StoreResult<u64>;
async fn ensure_num_minted_at_least(&self, tree: &[u8; 32], floor: u64) -> StoreResult<()>;
async fn put_leaf(&self, record: LeafRecord) -> StoreResult<()>;
async fn get_leaf(&self, asset_id: &[u8; 32]) -> StoreResult<Option<LeafRecord>>;
async fn get_leaf_by_index(
&self,
tree: &[u8; 32],
leaf_index: u64,
) -> StoreResult<Option<LeafRecord>>;
async fn list_leaves(&self, tree: &[u8; 32]) -> StoreResult<Vec<LeafRecord>>;
async fn get_last_signature(&self, tree: &[u8; 32]) -> StoreResult<Option<String>>;
async fn set_last_signature(&self, tree: &[u8; 32], signature: String) -> StoreResult<()>;
}
#[derive(Default)]
pub struct MemoryCnftStore {
inner: Arc<Mutex<MemoryStoreInner>>,
}
#[derive(Default)]
struct MemoryStoreInner {
trees: HashMap<[u8; 32], TreeInfo>,
leaves_by_asset: HashMap<[u8; 32], LeafRecord>,
leaf_index: HashMap<([u8; 32], u64), [u8; 32]>,
tree_leaf_order: HashMap<[u8; 32], Vec<[u8; 32]>>,
last_sig: HashMap<[u8; 32], String>,
}
impl MemoryCnftStore {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[async_trait]
impl CnftStore for MemoryCnftStore {
async fn put_tree(&self, info: TreeInfo) -> StoreResult<()> {
let mut g = self.inner.lock().await;
g.trees.insert(info.tree, info);
Ok(())
}
async fn get_tree(&self, tree: &[u8; 32]) -> StoreResult<Option<TreeInfo>> {
let g = self.inner.lock().await;
Ok(g.trees.get(tree).cloned())
}
async fn alloc_leaf_index(&self, tree: &[u8; 32]) -> StoreResult<u64> {
let mut g = self.inner.lock().await;
let info = g
.trees
.get_mut(tree)
.ok_or_else(|| StoreError::UnknownTree {
tree: bs58_like(tree),
})?;
let idx = info.num_minted;
info.num_minted = idx + 1;
Ok(idx)
}
async fn ensure_num_minted_at_least(&self, tree: &[u8; 32], floor: u64) -> StoreResult<()> {
let mut g = self.inner.lock().await;
let info = g
.trees
.get_mut(tree)
.ok_or_else(|| StoreError::UnknownTree {
tree: bs58_like(tree),
})?;
if info.num_minted < floor {
info.num_minted = floor;
}
Ok(())
}
async fn put_leaf(&self, record: LeafRecord) -> StoreResult<()> {
let mut g = self.inner.lock().await;
let asset_id = record.asset_id;
let tree = record.tree;
let leaf_index = record.leaf_index;
let is_new = !g.leaves_by_asset.contains_key(&asset_id);
g.leaves_by_asset.insert(asset_id, record);
g.leaf_index.insert((tree, leaf_index), asset_id);
if is_new {
g.tree_leaf_order.entry(tree).or_default().push(asset_id);
}
Ok(())
}
async fn get_leaf(&self, asset_id: &[u8; 32]) -> StoreResult<Option<LeafRecord>> {
let g = self.inner.lock().await;
Ok(g.leaves_by_asset.get(asset_id).cloned())
}
async fn get_leaf_by_index(
&self,
tree: &[u8; 32],
leaf_index: u64,
) -> StoreResult<Option<LeafRecord>> {
let g = self.inner.lock().await;
if let Some(asset_id) = g.leaf_index.get(&(*tree, leaf_index)) {
Ok(g.leaves_by_asset.get(asset_id).cloned())
} else {
Ok(None)
}
}
async fn list_leaves(&self, tree: &[u8; 32]) -> StoreResult<Vec<LeafRecord>> {
let g = self.inner.lock().await;
let order = g.tree_leaf_order.get(tree).cloned().unwrap_or_default();
Ok(order
.into_iter()
.filter_map(|asset_id| g.leaves_by_asset.get(&asset_id).cloned())
.collect())
}
async fn get_last_signature(&self, tree: &[u8; 32]) -> StoreResult<Option<String>> {
let g = self.inner.lock().await;
Ok(g.last_sig.get(tree).cloned())
}
async fn set_last_signature(&self, tree: &[u8; 32], signature: String) -> StoreResult<()> {
let mut g = self.inner.lock().await;
g.last_sig.insert(*tree, signature);
Ok(())
}
}
fn bs58_like(bytes: &[u8; 32]) -> String {
let mut s = String::with_capacity(64);
for b in bytes {
use std::fmt::Write;
let _ = write!(&mut s, "{b:02x}");
}
s
}