use serde::Serialize;
use uuid::Uuid;
use std::collections::HashMap;
use std::path::PathBuf;
use crate::ephemeral::{create_page, Page};
use crate::persistence::Persistable;
use crate::{NodeId, PageId};
use mycelium_command::node::Node;
const FILENAME: &str = "hist.idx";
const HISTORY_TAG: &str = ".hist";
pub(crate) type Head = HashMap<NodeId, Vec<PageId>>;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct History {
pub(crate) head: Head,
last_inserted_page: Option<PageId>,
head_path: std::path::PathBuf,
max_page_size: usize,
#[serde(skip)]
pub(crate) pages: HashMap<PageId, Page>,
working_dir: std::path::PathBuf,
}
pub(crate) fn lazy_head(path: &PathBuf, max: usize) -> History {
let mut working_dir = path.to_path_buf();
working_dir.push(".hist");
let mut head_path = working_dir.to_path_buf();
head_path.push(FILENAME);
let hist = crate::persistence::history::read_head(&head_path);
if hist.is_some() {
hist.unwrap()
} else {
new(head_path, working_dir, max)
}
}
pub(crate) fn new(head_path: PathBuf, working_dir: PathBuf, max: usize) -> History {
History {
head: HashMap::new(),
last_inserted_page: None,
head_path,
max_page_size: max,
pages: HashMap::new(),
working_dir,
}
}
impl History {
#[allow(clippy::map_entry)]
pub(crate) fn append_history(&mut self, node: Node) -> Result<(), Box<dyn std::error::Error>> {
let page = self.get_append_page(node.get_size());
let hist_page_id = page.add(node.clone());
if self.head.contains_key(&node.get_id()) {
if !self.head[&node.get_id()].contains(&hist_page_id) {
let h = self
.head
.get_mut(&hist_page_id)
.expect("Unable to edit history page index.");
h.push(hist_page_id);
}
} else {
self.head.insert(node.get_id(), vec![hist_page_id]);
}
Ok(())
}
pub(crate) fn clear(&mut self) -> std::io::Result<()> {
self.pages.clear();
Ok(())
}
pub(crate) fn get_append_page(&mut self, node_size: usize) -> &mut Page {
if self.last_inserted_page.is_some() {
let id = self.last_inserted_page.unwrap();
match self.load_page(id) {
Ok(_) => (),
Err(e) => error!("Error: {:?}", e),
}
let page_ref = &self.pages[&id]; if page_ref.get_size() + node_size < page_ref.get_max_size() {
let id = page_ref.get_id();
return self.pages.get_mut(&id).unwrap();
}
}
let new_page = create_page(HISTORY_TAG, self.max_page_size);
let new_page_id = new_page.get_id();
self.pages.insert(new_page_id, new_page);
self.last_inserted_page = Some(new_page_id);
self.pages.get_mut(&new_page_id).unwrap()
}
pub(crate) fn get_working_directory(&self) -> PathBuf {
self.working_dir.to_path_buf()
}
pub(crate) fn get_node_pages(&self, node_id: NodeId) -> Vec<PageId> {
let mut list = vec![];
if self.head.contains_key(&node_id) {
for x in &self.head[&node_id] {
list.push(*x); }
}
list
}
pub(crate) fn get_nodes(&self, id: NodeId, limit: usize) -> Vec<Node> {
let mut nodes: Vec<Node> = vec![];
for p in self.pages.values() {
for i in p.get_nodes() {
if i.get_id() == id {
nodes.push(i.clone());
}
if limit > 0 && nodes.len() == limit {
break;
}
}
}
nodes
}
pub(crate) fn load_page(
&mut self,
page_id: PageId,
) -> std::result::Result<(), Box<dyn std::error::Error>> {
let mut page_path = self.working_dir.to_path_buf();
let id = Uuid::from_bytes(page_id);
page_path.push(id.to_string());
let p = [page_path];
self.pages = crate::persistence::load_from_vec(p.to_vec())?;
Ok(())
}
}
impl Persistable for History {
fn get_id_str(&self) -> &str {
FILENAME
}
fn save(&self, path: &std::path::PathBuf) -> std::io::Result<()> {
crate::persistence::persist(self, path)
}
}