use crate::Hash;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Event<Arg> {
pub cmd: u32,
pub arg: Arg,
pub deps: BTreeSet<Hash>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum IncludeSpec {
IncludeAll,
IncludeOnlyDeps,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Graph<Arg> {
pub events: BTreeMap<Hash, Event<Arg>>,
pub nstates: BTreeMap<String, BTreeSet<Hash>>,
}
impl<Arg> Default for Graph<Arg> {
fn default() -> Self {
Self {
events: BTreeMap::new(),
nstates: BTreeMap::new(),
}
}
}
#[derive(Clone, Debug, thiserror::Error)]
pub enum GraphError {
#[error("unable to find the specified dataset")]
DatasetNotFound,
#[error("dependency circuit @ {0}")]
DependencyCircuit(Hash),
#[error("unable to retrieve dependency {0}")]
DependencyNotFound(Hash),
#[error("hash collision @ {0} detected during insertion of {}")]
HashCollision(Hash, String),
}
impl<Arg: Serialize> Graph<Arg> {
pub fn fold_state(
&self,
mut st: BTreeMap<Hash, bool>,
expand: bool,
) -> Option<BTreeMap<Hash, bool>> {
loop {
let orig_len = st.len();
let mut errs = false;
st.extend(
st.clone()
.into_iter()
.flat_map(|(i, _)| match self.events.get(&i) {
Some(x) => Some(x.deps.iter().map(|&j| (j, true))),
None => {
errs = true;
None
}
})
.flatten(),
);
if errs {
return None;
}
if orig_len == st.len() {
break;
}
}
if !expand {
st.retain(|_, is_dep| !*is_dep);
}
Some(st)
}
pub fn debug_exec_order(
&self,
evids: BTreeMap<Hash, IncludeSpec>,
) -> Result<Vec<Hash>, GraphError> {
let mut tt = BTreeSet::new();
let mut ret = Vec::new();
let mut deps = Vec::new();
for (main_evid, incl) in evids {
deps.push(main_evid);
while let Some(evid) = deps.pop() {
if tt.contains(&evid) {
continue;
} else if evid == main_evid && !deps.is_empty() {
return Err(GraphError::DependencyCircuit(main_evid));
}
let evwd = self
.events
.get(&evid)
.ok_or(GraphError::DependencyNotFound(evid))?;
let mut necessary_deps = evwd.deps.difference(&tt);
if let Some(&x) = necessary_deps.next() {
deps.push(evid);
deps.push(x);
deps.extend(necessary_deps.copied());
} else {
if evid == main_evid && incl != IncludeSpec::IncludeAll {
deps.clear();
break;
}
ret.push(evid);
tt.insert(evid);
}
}
}
Ok(ret)
}
}
impl<Arg> Graph<Arg> {
pub fn ensure_event(&mut self, ev: Event<Arg>) -> (Option<Event<Arg>>, Hash)
where
Arg: esvc_traits::CommandArg,
{
let serval = bincode::serialize::<Event<Arg>>(&ev).unwrap();
let h = crate::calculate_hash(&serval[..]);
use std::collections::btree_map::Entry;
(
match self.events.entry(h) {
Entry::Occupied(o) if o.get() == &ev => None,
Entry::Occupied(_) => Some(ev),
Entry::Vacant(v) => {
v.insert(ev);
None
}
},
h,
)
}
}
pub fn print_deps<W, DI>(w: &mut W, pfx: &str, deps: DI) -> std::io::Result<()>
where
DI: Iterator<Item = Hash>,
W: std::io::Write,
{
for i in deps {
writeln!(w, "{}{}", pfx, i)?;
}
Ok(())
}