use crate::config::Config;
use crate::Timestamp;
use anyhow::Error;
use fxhash::FxHasher64;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::hash::{Hash, Hasher};
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct Hashed {
pub hash: u64,
pub updated: Timestamp,
}
#[derive(Deserialize, Serialize, Default, Debug, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct DiskState {
#[serde(default)]
pub last_update: BTreeMap<String, Timestamp>,
#[serde(default)]
pub once: BTreeMap<String, Timestamp>,
#[serde(default)]
pub hashes: BTreeMap<String, Hashed>,
}
impl DiskState {
pub fn into_state(self, config: &Config, now: Timestamp) -> State<'_> {
State {
dirty: false,
last_update: self.last_update,
once: self.once,
hashes: self.hashes,
config,
now,
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct State<'a> {
pub dirty: bool,
pub last_update: BTreeMap<String, Timestamp>,
pub once: BTreeMap<String, Timestamp>,
pub hashes: BTreeMap<String, Hashed>,
pub config: &'a Config,
pub now: Timestamp,
}
impl<'a> State<'a> {
pub fn new(config: &'a Config, now: Timestamp) -> Self {
State {
dirty: Default::default(),
last_update: Default::default(),
once: Default::default(),
hashes: Default::default(),
config,
now,
}
}
pub fn last_update<'time>(&'time self, name: &str) -> Option<&'time Timestamp> {
self.last_update.get(name)
}
pub fn touch(&mut self, name: &str) {
self.dirty = true;
self.last_update.insert(name.to_string(), Timestamp::now());
}
pub fn has_run_once(&self, id: &str) -> bool {
self.once.contains_key(id)
}
pub fn touch_once(&mut self, id: &str) {
self.dirty = true;
self.once.insert(id.to_string(), Timestamp::now());
}
pub fn is_hash_fresh<H: Hash>(&self, id: &str, hash: H) -> Result<bool, Error> {
let hashed = match self.hashes.get(id) {
Some(hashed) => hashed,
None => return Ok(false),
};
let mut state = FxHasher64::default();
hash.hash(&mut state);
if hashed.hash != state.finish() {
return Ok(false);
}
let age = self.now.duration_since(hashed.updated)?;
Ok(age < self.config.package_refresh)
}
pub fn touch_hash<H: Hash>(&mut self, id: &str, hash: H) -> Result<(), Error> {
let mut state = FxHasher64::default();
hash.hash(&mut state);
self.dirty = true;
self.hashes.insert(
id.to_string(),
Hashed {
hash: state.finish(),
updated: Timestamp::now(),
},
);
Ok(())
}
pub fn extend(&mut self, other: State) {
if !other.dirty {
return;
}
self.dirty = true;
self.last_update.extend(other.last_update);
self.once.extend(other.once);
self.hashes.extend(other.hashes);
}
pub fn serialize(self) -> Option<DiskState> {
if !self.dirty {
return None;
}
Some(DiskState {
last_update: self.last_update,
once: self.once,
hashes: self.hashes,
})
}
}