1use crate::Hash;
2use serde::{Deserialize, Serialize};
3use std::collections::{BTreeMap, BTreeSet};
4
5#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
10pub struct Event<Arg> {
11 pub cmd: u32,
12 pub arg: Arg,
13 pub deps: BTreeSet<Hash>,
14}
15
16#[derive(Clone, Copy, Debug, PartialEq)]
17pub enum IncludeSpec {
18 IncludeAll,
19 IncludeOnlyDeps,
20}
21
22#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
23pub struct Graph<Arg> {
24 pub events: BTreeMap<Hash, Event<Arg>>,
25
26 pub nstates: BTreeMap<String, BTreeSet<Hash>>,
28}
29
30impl<Arg> Default for Graph<Arg> {
31 fn default() -> Self {
32 Self {
33 events: BTreeMap::new(),
34 nstates: BTreeMap::new(),
35 }
36 }
37}
38
39#[derive(Clone, Debug, thiserror::Error)]
40pub enum GraphError {
41 #[error("unable to find the specified dataset")]
42 DatasetNotFound,
43
44 #[error("dependency circuit @ {0}")]
45 DependencyCircuit(Hash),
46
47 #[error("unable to retrieve dependency {0}")]
48 DependencyNotFound(Hash),
49
50 #[error("hash collision @ {0} detected during insertion of {}")]
52 HashCollision(Hash, String),
53}
54
55impl<Arg: Serialize> Graph<Arg> {
56 pub fn fold_state(
59 &self,
60 mut st: BTreeMap<Hash, bool>,
61 expand: bool,
62 ) -> Option<BTreeMap<Hash, bool>> {
63 loop {
64 let orig_len = st.len();
65 let mut errs = false;
66 st.extend(
67 st.clone()
68 .into_iter()
69 .flat_map(|(i, _)| match self.events.get(&i) {
70 Some(x) => Some(x.deps.iter().map(|&j| (j, true))),
71 None => {
72 errs = true;
73 None
74 }
75 })
76 .flatten(),
77 );
78 if errs {
79 return None;
80 }
81 if orig_len == st.len() {
82 break;
83 }
84 }
85 if !expand {
86 st.retain(|_, is_dep| !*is_dep);
88 }
89 Some(st)
90 }
91
92 pub fn debug_exec_order(
94 &self,
95 evids: BTreeMap<Hash, IncludeSpec>,
96 ) -> Result<Vec<Hash>, GraphError> {
97 let mut tt = BTreeSet::new();
98 let mut ret = Vec::new();
99 let mut deps = Vec::new();
100 for (main_evid, incl) in evids {
101 deps.push(main_evid);
103
104 while let Some(evid) = deps.pop() {
105 if tt.contains(&evid) {
106 continue;
108 } else if evid == main_evid && !deps.is_empty() {
109 return Err(GraphError::DependencyCircuit(main_evid));
110 }
111
112 let evwd = self
113 .events
114 .get(&evid)
115 .ok_or(GraphError::DependencyNotFound(evid))?;
116 let mut necessary_deps = evwd.deps.difference(&tt);
117 if let Some(&x) = necessary_deps.next() {
118 deps.push(evid);
119 deps.push(x);
121 deps.extend(necessary_deps.copied());
122 } else {
123 if evid == main_evid && incl != IncludeSpec::IncludeAll {
124 deps.clear();
126 break;
127 }
128 ret.push(evid);
130 tt.insert(evid);
131 }
132 }
133 }
134 Ok(ret)
135 }
136}
137
138impl<Arg> Graph<Arg> {
139 pub fn ensure_event(&mut self, ev: Event<Arg>) -> (Option<Event<Arg>>, Hash)
143 where
144 Arg: esvc_traits::CommandArg,
145 {
146 let serval = bincode::serialize::<Event<Arg>>(&ev).unwrap();
147 let h = crate::calculate_hash(&serval[..]);
148 use std::collections::btree_map::Entry;
149 (
150 match self.events.entry(h) {
151 Entry::Occupied(o) if o.get() == &ev => None,
152 Entry::Occupied(_) => Some(ev),
153 Entry::Vacant(v) => {
154 v.insert(ev);
155 None
156 }
157 },
158 h,
159 )
160 }
161}
162
163pub fn print_deps<W, DI>(w: &mut W, pfx: &str, deps: DI) -> std::io::Result<()>
164where
165 DI: Iterator<Item = Hash>,
166 W: std::io::Write,
167{
168 for i in deps {
169 writeln!(w, "{}{}", pfx, i)?;
170 }
171 Ok(())
172}