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,
|
2020-07-20 20:14:22 +02:00
|
|
|
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
|
2020-10-30 16:40:35 +01:00
|
|
|
#[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
|
2020-07-20 20:14:22 +02:00
|
|
|
Current {
|
|
|
|
count: u64,
|
|
|
|
commits: u64,
|
|
|
|
cache: Cache<'a>,
|
|
|
|
},
|
2019-04-19 22:51:58 +02:00
|
|
|
/// Cached head is older than current head
|
2020-07-20 20:14:22 +02:00
|
|
|
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> {
|
2020-10-30 16:40:35 +01:00
|
|
|
#[instrument]
|
2020-07-20 20:14:22 +02:00
|
|
|
pub(crate) fn read_from_file(
|
2020-10-30 16:40:35 +01:00
|
|
|
path: impl AsRef<Path> + std::fmt::Debug,
|
2020-07-20 20:14:22 +02:00
|
|
|
branch: &str,
|
|
|
|
head: &str,
|
|
|
|
) -> Result<CacheState<'a>> {
|
2020-10-30 16:40:35 +01:00
|
|
|
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)?))?;
|
2020-07-20 20:14:22 +02:00
|
|
|
Ok(cache
|
|
|
|
.entries
|
|
|
|
.get(branch)
|
|
|
|
.map(|c| {
|
|
|
|
if c.head == head {
|
2020-10-30 16:40:35 +01:00
|
|
|
trace!("Cache is up to date");
|
2020-07-20 20:14:22 +02:00
|
|
|
CacheState::Current {
|
|
|
|
count: c.count,
|
|
|
|
commits: c.commits,
|
|
|
|
// TODO: get rid of clone
|
|
|
|
cache: cache.clone(),
|
|
|
|
}
|
|
|
|
} else {
|
2020-10-30 16:40:35 +01:00
|
|
|
trace!("Cache is out of date");
|
2020-07-20 20:14:22 +02:00
|
|
|
CacheState::Old {
|
|
|
|
head: c.head.to_string(),
|
|
|
|
// TODO: get rid of clone
|
|
|
|
cache: cache.clone(),
|
|
|
|
}
|
|
|
|
}
|
2019-07-07 13:28:43 +02:00
|
|
|
})
|
2020-07-20 20:14:22 +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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-30 16:40:35 +01:00
|
|
|
#[instrument]
|
2020-07-20 20:14:22 +02:00
|
|
|
pub(crate) fn calculate_new_cache(
|
|
|
|
self,
|
|
|
|
count: u64,
|
|
|
|
commits: u64,
|
|
|
|
head: Cow<'a, str>,
|
|
|
|
branch: &'a str,
|
|
|
|
) -> Cache<'a> {
|
2020-10-30 16:40:35 +01:00
|
|
|
trace!("Calculating new cache");
|
2019-04-19 22:51:58 +02:00
|
|
|
match self {
|
2020-07-20 20:14:22 +02:00
|
|
|
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) => {
|
2020-10-30 16:40:35 +01:00
|
|
|
trace!("Creating new cache for branch");
|
2020-07-20 20:14:22 +02:00
|
|
|
cache.entries.insert(
|
|
|
|
branch.into(),
|
|
|
|
CacheEntry {
|
|
|
|
head,
|
|
|
|
count,
|
|
|
|
commits,
|
|
|
|
},
|
|
|
|
);
|
2019-04-19 22:51:58 +02:00
|
|
|
cache
|
|
|
|
}
|
2020-07-20 20:14:22 +02:00
|
|
|
CacheState::No => {
|
2020-10-30 16:40:35 +01:00
|
|
|
trace!("Creating new cache file");
|
2020-07-20 20:14:22 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-30 16:40:35 +01:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
2019-04-29 20:37:20 +02:00
|
|
|
pub(crate) struct Cache<'a> {
|
2020-07-20 20:14:22 +02:00
|
|
|
pub entries: HashMap<Cow<'a, str>, CacheEntry<'a>>,
|
|
|
|
}
|
|
|
|
|
2020-10-30 16:40:35 +01:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
2020-07-20 20:14:22 +02:00
|
|
|
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> {
|
2020-10-30 16:40:35 +01:00
|
|
|
#[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(())
|
|
|
|
}
|
|
|
|
}
|