hoc/src/cache.rs

146 lines
4.1 KiB
Rust
Raw Normal View History

2019-06-16 22:20:09 +02:00
use crate::error::{Error, Result};
2019-04-19 22:51:58 +02:00
use std::{
2019-04-29 20:37:20 +02:00
borrow::Cow,
collections::HashMap,
2019-04-19 22:51:58 +02:00
fs::{create_dir_all, File, OpenOptions},
io::BufReader,
path::Path,
};
/// Enum to indicate the state of the cache
#[derive(Debug)]
2019-04-29 20:37:20 +02:00
pub(crate) enum CacheState<'a> {
2019-04-19 22:51:58 +02:00
/// Current head and cached head are the same
Current {
count: u64,
commits: u64,
cache: Cache<'a>,
},
2019-04-19 22:51:58 +02:00
/// Cached head is older than current head
Old {
head: String,
cache: Cache<'a>,
},
NoneForBranch(Cache<'a>),
2019-04-19 22:51:58 +02:00
/// No cache was found
No,
}
2019-04-29 20:37:20 +02:00
impl<'a> CacheState<'a> {
#[instrument]
pub(crate) fn read_from_file(
path: impl AsRef<Path> + std::fmt::Debug,
branch: &str,
head: &str,
) -> Result<CacheState<'a>> {
trace!("Reading cache");
2019-04-19 22:51:58 +02:00
if path.as_ref().exists() {
let cache: Cache = serde_json::from_reader(BufReader::new(File::open(path)?))?;
Ok(cache
.entries
.get(branch)
.map(|c| {
if c.head == head {
trace!("Cache is up to date");
CacheState::Current {
count: c.count,
commits: c.commits,
// TODO: get rid of clone
cache: cache.clone(),
}
} else {
trace!("Cache is out of date");
CacheState::Old {
head: c.head.to_string(),
// TODO: get rid of clone
cache: cache.clone(),
}
}
2019-07-07 13:28:43 +02:00
})
// TODO: get rid of clone
.unwrap_or_else(|| CacheState::NoneForBranch(cache.clone())))
2019-04-19 22:51:58 +02:00
} else {
Ok(CacheState::No)
}
}
#[instrument]
pub(crate) fn calculate_new_cache(
self,
count: u64,
commits: u64,
head: Cow<'a, str>,
branch: &'a str,
) -> Cache<'a> {
trace!("Calculating new cache");
2019-04-19 22:51:58 +02:00
match self {
CacheState::Old { mut cache, .. } => {
if let Some(mut cache) = cache.entries.get_mut(branch) {
cache.head = head;
cache.count += count;
cache.commits += commits;
}
cache
}
CacheState::Current { cache, .. } => cache,
CacheState::NoneForBranch(mut cache) => {
trace!("Creating new cache for branch");
cache.entries.insert(
branch.into(),
CacheEntry {
head,
count,
commits,
},
);
2019-04-19 22:51:58 +02:00
cache
}
CacheState::No => {
trace!("Creating new cache file");
let mut entries = HashMap::with_capacity(1);
entries.insert(
branch.into(),
CacheEntry {
commits,
head,
count,
},
);
Cache { entries }
}
2019-04-19 22:51:58 +02:00
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
2019-04-29 20:37:20 +02:00
pub(crate) struct Cache<'a> {
pub entries: HashMap<Cow<'a, str>, CacheEntry<'a>>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub(crate) struct CacheEntry<'a> {
2019-07-07 13:28:43 +02:00
/// HEAD commit ref
2019-04-29 20:37:20 +02:00
pub head: Cow<'a, str>,
2019-07-07 13:28:43 +02:00
/// HoC value
2019-04-19 22:51:58 +02:00
pub count: u64,
2019-07-07 13:28:43 +02:00
/// Number of commits
pub commits: u64,
2019-04-19 22:51:58 +02:00
}
2019-04-29 20:37:20 +02:00
impl<'a> Cache<'a> {
#[instrument]
pub(crate) fn write_to_file(&self, path: impl AsRef<Path> + std::fmt::Debug) -> Result<()> {
trace!("Persisting cache to disk");
2019-04-19 22:51:58 +02:00
create_dir_all(path.as_ref().parent().ok_or(Error::Internal)?)?;
serde_json::to_writer(
OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)?,
self,
)?;
Ok(())
}
}