bakare/src/index/io.rs

169 lines
5.2 KiB
Rust
Raw Normal View History

2021-05-16 19:15:07 +01:00
use std::{
collections::HashMap,
fs::{self, File},
io::Read,
path::{Path, PathBuf},
};
use uuid::Uuid;
use crate::index::item::IndexItem;
use crate::index::{lock, Index};
2021-02-07 12:23:06 +00:00
use crate::io::error_correcting_encoder;
use crate::repository::ItemId;
use anyhow::Result;
use anyhow::*;
use lock::Lock;
use nix::unistd::getpid;
use std::{cmp::max, io::Write};
impl Index {
2021-05-16 19:15:07 +01:00
pub fn load(repository_path: &Path) -> Result<Self> {
if !repository_path.exists() {
2020-12-25 21:52:40 +00:00
let mut index = Index::new()?;
index.save(repository_path)?;
}
2020-11-13 22:37:41 +00:00
let lock = Lock::lock(repository_path)?;
2020-12-25 21:52:40 +00:00
let index_file_path = &Index::index_file_path_for_repository_path(repository_path)?;
let index = Index::load_from_file(index_file_path)?;
lock.release()?;
log::debug!(
2020-12-25 21:52:40 +00:00
"[{}] loaded index from {}, version: {}; {} items",
getpid(),
2021-05-16 19:15:07 +01:00
index_file_path.to_string_lossy(),
2020-12-25 21:52:40 +00:00
index.version,
index.newest_items_by_source_path.len()
);
Ok(index)
}
2021-05-16 19:15:07 +01:00
pub fn save(&mut self, repository_path: &Path) -> Result<()> {
let lock_id = Uuid::new_v4();
2020-12-25 21:52:40 +00:00
let lock = Lock::lock(repository_path)?;
let index_file_path = &Index::index_file_path_for_repository_path(repository_path)?;
2021-05-16 19:15:07 +01:00
if index_file_path.exists() {
2020-12-25 21:52:40 +00:00
let index = Index::load_from_file(&Index::index_file_path_for_repository_path(repository_path)?)?;
self.merge_items_by_file_id(index.items_by_file_id);
self.merge_newest_items(index.newest_items_by_source_path);
2020-11-08 19:20:55 +00:00
self.version = max(self.version, index.version);
}
self.version = self.version.next();
2020-12-25 21:52:40 +00:00
self.write_index_to_file(index_file_path)?;
lock.release()?;
2020-12-25 21:52:40 +00:00
log::debug!(
"[{}] saved index version {} with lock id {} to {}; {} items",
getpid(),
self.version,
lock_id,
2021-05-16 19:15:07 +01:00
index_file_path.to_string_lossy(),
2020-12-25 21:52:40 +00:00
self.newest_items_by_source_path.len()
);
Ok(())
}
2021-05-16 19:15:07 +01:00
fn write_index_to_file(&mut self, index_file_path: &Path) -> Result<()> {
2020-12-25 21:52:40 +00:00
let parent = index_file_path.parent();
match parent {
2021-05-16 19:15:07 +01:00
None => Err(anyhow!(format!(
"cannot get parent for {}",
index_file_path.to_string_lossy()
))),
Some(parent) => Ok(fs::create_dir_all(parent)),
}??;
2020-12-25 21:52:40 +00:00
2021-05-16 20:24:11 +01:00
let serialised = serde_json::to_string_pretty(&self)?;
2021-02-07 00:13:41 +00:00
let bytes = serialised.as_bytes();
2021-02-07 12:23:06 +00:00
let encoded = error_correcting_encoder::encode(bytes)?;
2021-02-07 00:13:41 +00:00
2020-12-25 21:52:40 +00:00
{
2021-05-16 19:15:07 +01:00
let mut file = File::create(index_file_path)?;
2021-05-15 20:10:26 +01:00
file.write_all(&encoded).context("writing index to disk")?;
2020-12-25 21:52:40 +00:00
file.flush()?;
}
2021-02-07 00:13:41 +00:00
2021-02-07 12:23:06 +00:00
let readback = {
2021-05-16 19:15:07 +01:00
let mut file = File::open(index_file_path)?;
2021-02-07 12:23:06 +00:00
let mut readback = vec![];
file.read_to_end(&mut readback)?;
readback
};
2021-02-07 00:13:41 +00:00
if readback != encoded {
2020-12-25 21:52:40 +00:00
Err(anyhow!("index readback incorrect"))
} else {
Ok(())
}
}
2021-05-16 19:15:07 +01:00
fn load_from_file(index_file_path: &Path) -> Result<Self> {
let mut file = File::open(index_file_path)?;
2021-05-15 21:07:32 +01:00
let mut encoded = vec![];
file.read_to_end(&mut encoded)?;
let decoded = error_correcting_encoder::decode(&encoded)?;
let index_text = String::from_utf8(decoded)?;
2021-05-16 19:15:07 +01:00
let index: Index = serde_json::from_str(&index_text)
.context(format!("cannot read index from: {}", index_file_path.to_string_lossy()))?;
Ok(index)
}
fn merge_newest_items(&mut self, old_newest_items: HashMap<String, IndexItem>) {
for (source_path, old_newest_item) in old_newest_items {
if let Some(new_newest_item) = self.newest_items_by_source_path.get(&source_path) {
if old_newest_item.version() > new_newest_item.version() {
self.newest_items_by_source_path.insert(source_path, old_newest_item);
}
} else {
self.newest_items_by_source_path.insert(source_path, old_newest_item);
}
}
}
fn merge_items_by_file_id(&mut self, old_items_by_file_id: HashMap<ItemId, IndexItem>) {
self.items_by_file_id.extend(old_items_by_file_id);
}
2021-05-16 19:15:07 +01:00
fn index_file_path_for_repository_path(path: &Path) -> Result<PathBuf> {
Ok(path.join("index"))
}
}
2020-11-08 19:20:55 +00:00
#[cfg(test)]
mod must {
use crate::index::Index;
use anyhow::Result;
2021-05-16 19:15:07 +01:00
use pretty_assertions::assert_eq;
use tempfile::tempdir;
2020-11-08 19:20:55 +00:00
#[test]
fn have_version_increased_when_saved() -> Result<()> {
2021-05-16 19:15:07 +01:00
let temp_dir = tempdir()?;
2020-12-25 21:52:40 +00:00
let mut index = Index::new()?;
2020-11-08 19:20:55 +00:00
let old_version = index.version;
2021-10-22 21:20:53 +01:00
index.save(temp_dir.path())?;
2020-11-08 19:20:55 +00:00
let new_version = index.version;
assert!(new_version > old_version);
Ok(())
}
2021-02-06 23:13:08 +00:00
#[test]
fn be_same_when_loaded_from_disk() -> Result<()> {
2021-05-16 19:15:07 +01:00
let repository_path = tempdir()?;
2021-02-06 23:13:08 +00:00
let mut original = Index::new()?;
2021-10-22 21:20:53 +01:00
original.save(repository_path.path())?;
let loaded = Index::load(repository_path.path())?;
2021-02-06 23:13:08 +00:00
assert_eq!(original, loaded);
Ok(())
}
2020-11-08 19:20:55 +00:00
}