use std::{
collections::HashMap,
path::{Path, PathBuf},
sync::{Arc, RwLock},
};
use crate::{load_file_contents, Hashed, LedgerEntry, LedgerHash, LedgerItem, TheLedgerEvent};
#[derive(Clone)]
pub struct BlockChain<T: LedgerItem> {
cached: Arc<RwLock<Vec<LedgerEntry<T>>>>,
index_to_hash: Arc<RwLock<HashMap<LedgerHash, usize>>>,
entries_path: Arc<PathBuf>,
}
impl<T: LedgerItem> BlockChain<T> {
pub fn new(path: PathBuf) -> Self {
std::fs::create_dir_all(&path).unwrap();
let cached = Self::load_ledger(&path);
let mut index_to_hash: HashMap<LedgerHash, usize> = Default::default();
for entry in &cached {
index_to_hash.insert(entry.data_hash(), entry.index);
}
Self {
cached: Arc::new(RwLock::new(cached)),
index_to_hash: Arc::new(RwLock::new(index_to_hash)),
entries_path: Arc::new(path),
}
}
pub fn chain(&self) -> Vec<LedgerEntry<T>> {
self.cached.read().unwrap().clone()
}
pub fn current_hash(&self) -> Option<Hashed> {
self.current_head().map(|entry| entry.data_hash())
}
fn current_index(&self) -> usize {
self.cached.read().unwrap().len()
}
fn current_head(&self) -> Option<LedgerEntry<T>> {
self.cached.read().unwrap().last().cloned()
}
pub fn save(&self, event: TheLedgerEvent<T>) -> Hashed {
use std::io::Write;
let previous = self.current_head();
let entry = LedgerEntry::new(previous.as_ref(), event);
let index = self.current_index();
let ledger_hash = entry.data_hash();
let name = format!("{:06}", self.current_index());
let path = &self.entries_path.join(name);
assert!(!path.exists());
let mut file = std::fs::File::create_new(path).unwrap();
let serialized = serde_json::to_string_pretty(&entry).unwrap();
file.write_all(serialized.as_bytes()).unwrap();
self.cached.write().unwrap().push(entry);
self.index_to_hash
.write()
.unwrap()
.insert(ledger_hash.clone(), index);
self.current_hash().unwrap()
}
fn load_ledger(space: &Path) -> Vec<LedgerEntry<T>> {
dbg!(space);
let mut foo: Vec<(usize, LedgerEntry<T>)> = {
let map: HashMap<String, Vec<u8>> = load_file_contents(space);
let mut foo: Vec<(usize, LedgerEntry<T>)> = Default::default();
if map.is_empty() {
return vec![];
}
for (_hash, value) in map.into_iter() {
let action: LedgerEntry<T> = serde_json::from_slice(&value).unwrap();
let idx = action.index;
foo.push((idx, action));
}
foo
};
foo.sort_by_key(|k| k.0);
let mut output: Vec<LedgerEntry<T>> = vec![];
let mut _prev_hash: Option<String> = None;
for (_, entry) in foo {
output.push(entry);
}
output
}
}