1use crate::{
17 Cargo, Catalog, Ctx, Device, Fact, Host, Keeper, Kept, Key, Keys, Memory, NodeError, NodeId,
18 Outcome, Placement, Plan, Transport, TransportError, Value, Watcher,
19};
20use std::collections::{HashMap, HashSet};
21use std::fmt;
22use std::time::Instant;
23
24pub const NODE: &str = "node";
27
28pub const FINGERPRINT: &str = "fingerprint";
31
32pub const INPUT: &str = "input";
38
39pub const OURS: [&str; 3] = [NODE, FINGERPRINT, INPUT];
42
43pub struct Executor<'a> {
46 catalog: &'a Catalog,
47 placement: Option<&'a Placement>,
48 memory: Option<&'a Memory>,
51 keeper: Option<&'a dyn Keeper>,
54 transports: Vec<(Host, &'a dyn Transport)>,
57 watcher: Option<&'a dyn Watcher>,
59 since: Option<Instant>,
62 stamp: Vec<(String, String)>,
65 input: Option<Key>,
68}
69
70impl<'a> Executor<'a> {
71 pub fn new(catalog: &'a Catalog) -> Self {
73 Self {
74 catalog,
75 placement: None,
76 memory: None,
77 keeper: None,
78 transports: Vec::new(),
79 watcher: None,
80 since: None,
81 stamp: Vec::new(),
82 input: None,
83 }
84 }
85
86 pub fn placed(mut self, placement: &'a Placement) -> Self {
89 self.placement = Some(placement);
90 self
91 }
92
93 pub fn remembering(mut self, memory: &'a Memory) -> Self {
97 self.memory = Some(memory);
98 self
99 }
100
101 pub fn keeping(mut self, keeper: &'a dyn Keeper) -> Self {
105 self.keeper = Some(keeper);
106 self
107 }
108
109 pub fn reaching(mut self, host: impl Into<Host>, transport: &'a dyn Transport) -> Self {
113 self.transports.push((host.into(), transport));
114 self
115 }
116
117 pub fn watching(mut self, watcher: &'a dyn Watcher) -> Self {
120 self.watcher = Some(watcher);
121 self
122 }
123
124 pub fn stamping(mut self, stamp: impl IntoIterator<Item = (String, String)>) -> Self {
130 self.stamp = stamp.into_iter().collect();
131 self
132 }
133
134 fn saw(&self, fact: impl FnOnce() -> Fact) {
137 if let Some(watcher) = self.watcher {
138 watcher.saw(&fact());
139 }
140 }
141
142 pub fn run(&self, plan: &Plan, input: Value) -> Result<Value, RunError> {
146 let began = Instant::now();
147 let named = self.keeper.and_then(|keeper| keeper.key_of(&input));
150 let walking = self.since(began).fed(named);
151 let answer = walking.running(plan, input);
152 match &answer {
153 Ok(_) => walking.saw(|| Fact::Finished {
154 took: began.elapsed(),
155 }),
156 Err(why) => walking.saw(|| Fact::Broke {
157 why: why.to_string(),
158 }),
159 }
160 answer
161 }
162
163 fn since(&self, began: Instant) -> Self {
166 Self {
167 catalog: self.catalog,
168 placement: self.placement,
169 memory: self.memory,
170 keeper: self.keeper,
171 transports: self.transports.clone(),
172 watcher: self.watcher,
173 since: Some(began),
174 stamp: self.stamp.clone(),
175 input: None,
178 }
179 }
180
181 fn fed(mut self, input: Option<Key>) -> Self {
183 self.input = input;
184 self
185 }
186
187 fn so_far(&self) -> std::time::Duration {
189 self.since.map(|began| began.elapsed()).unwrap_or_default()
190 }
191
192 fn running(&self, plan: &Plan, input: Value) -> Result<Value, RunError> {
195 let mut produced: HashMap<NodeId, Value> = HashMap::new();
196 let (mut keys, unneeded) = self.foreseen(plan, &input);
198 let last = self.walk(plan, &input, &mut produced, &mut keys, &unneeded)?;
199
200 let leaves = terminals(plan);
203 Ok(match leaves.as_slice() {
204 [] | [_] => last,
205 many => Value::map(
206 many.iter()
207 .map(|id| {
208 let value = produced
209 .get(id)
210 .cloned()
211 .expect("the walk executed every step of the plan");
212 (id.to_string(), value)
213 })
214 .collect::<Vec<_>>(),
215 ),
216 })
217 }
218
219 pub fn resume(
224 &self,
225 plan: &Plan,
226 input: Value,
227 known: Vec<(NodeId, Value)>,
228 named: Vec<(NodeId, Keys)>,
229 ) -> Result<Outcome, RunError> {
230 let walking = self.since(Instant::now());
233 let mut produced: HashMap<NodeId, Value> = known.into_iter().collect();
234 let mut keys: HashMap<NodeId, Keys> = named.into_iter().collect();
235 let brought: Vec<NodeId> = produced.keys().cloned().collect();
236 let named: Vec<NodeId> = keys.keys().cloned().collect();
237
238 let last = walking.walk(plan, &input, &mut produced, &mut keys, &HashSet::new())?;
241
242 produced.retain(|id, _| !brought.contains(id));
243 keys.retain(|id, _| !named.contains(id));
244 Ok(Outcome {
245 last,
246 produced: sorted(produced),
247 keys: sorted(keys),
248 })
249 }
250
251 fn walk(
254 &self,
255 plan: &Plan,
256 graph_input: &Value,
257 produced: &mut HashMap<NodeId, Value>,
258 keys: &mut HashMap<NodeId, Keys>,
259 unneeded: &HashSet<NodeId>,
260 ) -> Result<Value, RunError> {
261 match plan {
262 Plan::Empty => Ok(graph_input.clone()),
263 Plan::Execute { node, .. } if unneeded.contains(node) => {
266 self.saw(|| Fact::Spared { node: node.clone() });
267 Ok(Value::Null)
268 }
269 Plan::Execute { node, from } if self.maps(node) => {
270 self.over_items(node, from, graph_input, produced, keys)
271 }
272 Plan::Execute { node, from } => {
273 let key = match keys.get(node) {
276 Some(Keys::One(named)) => Some(named.clone()),
277 _ => self.key_for(node, from, graph_input, keys),
278 };
279 if let Some(key) = &key {
280 keys.insert(node.clone(), Keys::One(key.clone()));
281 }
282 let output = match self.recalled(node, key.as_ref()) {
285 Some(kept) => kept,
286 None if from.iter().any(|id| unneeded.contains(id)) => {
290 return Err(RunError::Vanished { node: node.clone() });
291 }
292 None => {
293 let input = gather(node, from, graph_input, produced)?;
294 let output = self.advance(node, input)?;
295 self.keep(node, key.as_ref(), &output);
296 output
297 }
298 };
299 produced.insert(node.clone(), output.clone());
300 Ok(output)
301 }
302 Plan::Sequence(plans) => {
303 let mut last = graph_input.clone();
304 for plan in plans {
305 last = self.walk(plan, graph_input, produced, keys, unneeded)?;
306 }
307 Ok(last)
308 }
309 Plan::Wave(branches) => self.at_once(branches, graph_input, produced, keys, unneeded),
310 Plan::Remote { inner, .. }
313 if inner.steps().all(|step| unneeded.contains(step.node)) =>
314 {
315 for step in inner.steps() {
316 self.saw(|| Fact::Spared {
317 node: step.node.clone(),
318 });
319 }
320 Ok(Value::Null)
321 }
322 Plan::Remote { host, inner } => {
323 self.elsewhere(host, inner, graph_input, produced, keys)
324 }
325 }
326 }
327
328 fn maps(&self, node: &NodeId) -> bool {
330 self.memory.is_some_and(|memory| memory.is_mapped(node))
331 }
332
333 fn over_items(
337 &self,
338 node: &NodeId,
339 from: &[NodeId],
340 graph_input: &Value,
341 produced: &mut HashMap<NodeId, Value>,
342 keys: &mut HashMap<NodeId, Keys>,
343 ) -> Result<Value, RunError> {
344 let input = gather(node, from, graph_input, produced)?;
345 let Value::List(items) = &input else {
346 return Err(RunError::NotItems {
347 node: node.clone(),
348 given: input.type_name().to_string(),
349 });
350 };
351
352 let mine = self.keys_for_items(node, from, items, keys);
353 let kept: Vec<Option<Value>> = match &mine {
354 Some(mine) => self.recalled_items(node, mine),
355 None => vec![None; items.len()],
356 };
357 let missing: Vec<usize> = (0..items.len()).filter(|i| kept[*i].is_none()).collect();
358 self.saw(|| Fact::Items {
359 node: node.clone(),
360 of: items.len(),
361 recalled: items.len() - missing.len(),
362 });
363
364 let mut answers = Vec::new();
367 if !missing.is_empty() {
368 let asked = Value::list(
369 missing
370 .iter()
371 .map(|i| items[*i].clone())
372 .collect::<Vec<_>>(),
373 );
374 let output = self.advance(node, asked)?;
375 let Value::List(back) = &output else {
376 return Err(RunError::NotItems {
377 node: node.clone(),
378 given: output.type_name().to_string(),
379 });
380 };
381 if back.len() != missing.len() {
382 return Err(RunError::Uncounted {
383 node: node.clone(),
384 asked: missing.len(),
385 answered: back.len(),
386 });
387 }
388 answers = back.to_vec();
389 }
390
391 let mut out = Vec::with_capacity(items.len());
392 let mut answered = answers.into_iter();
393 for (i, was) in kept.into_iter().enumerate() {
394 match was {
395 Some(value) => out.push(value),
396 None => {
397 let value = answered.next().expect("one answer per item asked for");
398 if let Some(mine) = &mine {
399 self.keep(node, Some(&mine[i]), &value);
400 }
401 out.push(value);
402 }
403 }
404 }
405
406 let output = Value::list(out);
407 if let Some(mine) = mine {
408 keys.insert(node.clone(), Keys::PerItem(mine));
409 }
410 produced.insert(node.clone(), output.clone());
411 Ok(output)
412 }
413
414 fn keys_for_items(
420 &self,
421 node: &NodeId,
422 from: &[NodeId],
423 items: &[Value],
424 keys: &HashMap<NodeId, Keys>,
425 ) -> Option<Vec<Key>> {
426 let (keeper, memory) = (self.keeper?, self.memory?);
427 let identity = memory.identity_of(node)?;
428 let above: Vec<Key> = match from {
429 [one] => match keys.get(one) {
430 Some(Keys::PerItem(each)) if each.len() == items.len() => each.clone(),
431 _ => items
432 .iter()
433 .map(|item| keeper.key_of(item))
434 .collect::<Option<Vec<_>>>()?,
435 },
436 _ => items
437 .iter()
438 .map(|item| keeper.key_of(item))
439 .collect::<Option<Vec<_>>>()?,
440 };
441 Some(
442 above
443 .iter()
444 .map(|one| {
445 keeper.combine(&[
446 identity,
447 memory.state_of(node).unwrap_or(""),
448 memory.salt_of(node).unwrap_or(""),
449 one.as_str(),
450 ])
451 })
452 .collect(),
453 )
454 }
455
456 fn recalled_items(&self, node: &NodeId, mine: &[Key]) -> Vec<Option<Value>> {
459 let nothing = vec![None; mine.len()];
460 let Some((keeper, memory)) = self.keeper.zip(self.memory) else {
461 return nothing;
462 };
463 if !memory.is_cached(node) {
464 return nothing;
465 }
466 match keeper.recall(&mine.iter().collect::<Vec<_>>()) {
467 Ok(answers) => answers
468 .into_iter()
469 .map(|kept| kept.map(|kept| kept.value))
470 .collect(),
471 Err(why) => {
472 eprintln!("what `{node}` produced could not be looked up: {why}");
473 nothing
474 }
475 }
476 }
477
478 fn at_once(
483 &self,
484 branches: &[Plan],
485 graph_input: &Value,
486 produced: &mut HashMap<NodeId, Value>,
487 keys: &mut HashMap<NodeId, Keys>,
488 unneeded: &HashSet<NodeId>,
489 ) -> Result<Value, RunError> {
490 let earlier: &HashMap<NodeId, Value> = produced;
491 let named: &HashMap<NodeId, Keys> = keys;
492 let outcomes = std::thread::scope(|scope| {
493 let running: Vec<_> = branches
494 .iter()
495 .map(|branch| {
496 scope.spawn(move || {
497 let mut mine = earlier.clone();
498 let mut mine_keys = named.clone();
499 let last =
500 self.walk(branch, graph_input, &mut mine, &mut mine_keys, unneeded)?;
501 mine.retain(|id, _| !earlier.contains_key(id));
502 mine_keys.retain(|id, _| !named.contains_key(id));
503 Ok::<_, RunError>((last, mine, mine_keys))
504 })
505 })
506 .collect();
507 running
508 .into_iter()
509 .map(|handle| match handle.join() {
510 Ok(outcome) => outcome,
511 Err(panic) => std::panic::resume_unwind(panic),
513 })
514 .collect::<Vec<_>>()
515 });
516
517 for outcome in outcomes {
518 let (_, mine, mine_keys) = outcome?;
520 produced.extend(mine);
521 keys.extend(mine_keys);
522 }
523
524 Ok(Value::Null)
526 }
527
528 fn elsewhere(
531 &self,
532 host: &Host,
533 inner: &Plan,
534 graph_input: &Value,
535 produced: &mut HashMap<NodeId, Value>,
536 keys: &mut HashMap<NodeId, Keys>,
537 ) -> Result<Value, RunError> {
538 let transport = self
539 .transports
540 .iter()
541 .find(|(known, _)| known == host)
542 .map(|(_, transport)| *transport)
543 .ok_or_else(|| RunError::NoTransport(host.clone()))?;
544
545 let reads = needs(inner);
546 let known: Vec<(NodeId, Value)> = reads
547 .iter()
548 .filter_map(|id| produced.get(id).map(|value| (id.clone(), value.clone())))
549 .collect();
550 let named: Vec<(NodeId, Keys)> = reads
553 .iter()
554 .filter_map(|id| keys.get(id).map(|key| (id.clone(), key.clone())))
555 .collect();
556
557 let nowhere = Placement::new();
558 let nothing = Memory::new();
559 let cargo = Cargo {
560 input: graph_input,
561 known: &known,
562 keys: &named,
563 placement: self.placement.unwrap_or(&nowhere),
564 memory: self.memory.unwrap_or(¬hing),
567 };
568 let attributed = self.watcher.map(|to| Attributed {
571 host: host.clone(),
572 to,
573 });
574 let at = self.so_far();
575 let began = Instant::now();
576 let outcome = transport
577 .dispatch(
578 inner,
579 &cargo,
580 attributed.as_ref().map(|one| one as &dyn Watcher),
581 )
582 .map_err(|source| RunError::Transport {
583 host: host.clone(),
584 source,
585 })?;
586 self.saw(|| Fact::Left {
587 host: host.clone(),
588 began: at,
589 took: began.elapsed(),
590 });
591
592 produced.extend(outcome.produced);
593 keys.extend(outcome.keys);
594 Ok(outcome.last)
595 }
596
597 fn advance(&self, node: &NodeId, input: Value) -> Result<Value, RunError> {
601 let ctx = Ctx {
602 device: self.device(node),
603 };
604 let at = self.so_far();
607 let began = Instant::now();
608 let answer = self.implementation(node)?.forward(&input, &ctx);
609 let took = began.elapsed();
610 match answer {
611 Ok(output) => {
612 self.saw(|| Fact::Ran {
613 node: node.clone(),
614 began: at,
615 took,
616 device: self.device(node).cloned(),
617 });
618 Ok(output)
619 }
620 Err(source) => {
621 self.saw(|| Fact::Failed {
624 node: node.clone(),
625 why: source.to_string(),
626 });
627 Err(RunError::Node {
628 node: node.clone(),
629 source,
630 })
631 }
632 }
633 }
634
635 fn key_for(
641 &self,
642 node: &NodeId,
643 from: &[NodeId],
644 graph_input: &Value,
645 keys: &HashMap<NodeId, Keys>,
646 ) -> Option<Key> {
647 let (keeper, memory) = (self.keeper?, self.memory?);
648 let identity = memory.identity_of(node)?;
649 let keeper: &dyn Keeper = keeper;
650 let above: Vec<Key> = match from {
660 [] => vec![match &self.input {
661 Some(named) => named.clone(),
662 None => keeper.key_of(graph_input)?,
663 }],
664 many => many
665 .iter()
666 .map(|id| keys.get(id).map(|keys| whole(keeper, keys)))
667 .collect::<Option<Vec<_>>>()?,
668 };
669
670 let mut parts = vec![
673 identity,
674 memory.declaration_of(node).unwrap_or(""),
675 memory.state_of(node).unwrap_or(""),
676 memory.salt_of(node).unwrap_or(""),
677 ];
678 parts.extend(above.iter().map(Key::as_str));
679 Some(keeper.combine(&parts))
680 }
681
682 pub fn foreseen(
695 &self,
696 plan: &Plan,
697 graph_input: &Value,
698 ) -> (HashMap<NodeId, Keys>, HashSet<NodeId>) {
699 let nothing = (HashMap::new(), HashSet::new());
700 let (Some(keeper), Some(memory)) = (self.keeper, self.memory) else {
701 return nothing;
702 };
703
704 let mut named: HashMap<NodeId, Keys> = HashMap::new();
706 let mut asked: Vec<(NodeId, Key)> = Vec::new();
707 for step in plan.steps() {
708 if self.maps(step.node) {
709 continue;
710 }
711 let Some(key) = self.key_for(step.node, step.from, graph_input, &named) else {
712 continue;
713 };
714 if memory.is_cached(step.node) {
715 asked.push((step.node.clone(), key.clone()));
716 }
717 named.insert(step.node.clone(), Keys::One(key));
718 }
719 if asked.is_empty() {
720 return (named, HashSet::new());
721 }
722
723 let keys: Vec<&Key> = asked.iter().map(|(_, key)| key).collect();
724 let there = match keeper.present(&keys) {
725 Ok(there) => there,
726 Err(why) => {
729 eprintln!("what is already kept could not be looked up: {why}");
730 return (named, HashSet::new());
731 }
732 };
733 let kept: HashSet<&NodeId> = asked
734 .iter()
735 .zip(&there)
736 .filter(|(_, there)| **there)
737 .map(|((node, _), _)| node)
738 .collect();
739
740 let mut needed: HashSet<NodeId> = HashSet::new();
741 let mut asking: Vec<NodeId> = terminals(plan);
742 while let Some(node) = asking.pop() {
743 if !needed.insert(node.clone()) || kept.contains(&node) {
744 continue;
745 }
746 for step in plan.steps().filter(|step| *step.node == node) {
747 asking.extend(step.from.iter().cloned());
748 }
749 }
750 let unneeded = plan
751 .steps()
752 .map(|step| step.node)
753 .filter(|node| !needed.contains(*node))
754 .cloned()
755 .collect();
756 (named, unneeded)
757 }
758
759 fn recalled(&self, node: &NodeId, key: Option<&Key>) -> Option<Value> {
763 let (keeper, memory) = (self.keeper?, self.memory?);
764 let key = key?;
765 if !memory.is_cached(node) {
766 return None;
767 }
768 let kept = match keeper.recall(&[key]) {
769 Ok(answers) => answers.into_iter().next().flatten()?,
770 Err(why) => {
771 eprintln!("what `{node}` produced could not be looked up: {why}");
772 return None;
773 }
774 };
775
776 if let (Some(declared), Some(written)) = (memory.fingerprint_of(node), fingerprint(&kept))
779 && declared != written
780 {
781 eprintln!(
782 "`{node}` was kept by code fingerprinted `{written}` and this graph declares \
783 `{declared}`: using what is kept, since the fingerprint is not part of the key"
784 );
785 }
786 self.saw(|| Fact::Recalled {
787 node: node.clone(),
788 key: key.clone(),
789 });
790 Some(kept.value)
791 }
792
793 fn keep(&self, node: &NodeId, key: Option<&Key>, output: &Value) {
796 let (Some(keeper), Some(memory), Some(key)) = (self.keeper, self.memory, key) else {
797 return;
798 };
799 if !memory.is_cached(node) {
800 return;
801 }
802 let mut meta = vec![(NODE, node.as_str())];
803 if let Some(written) = memory.fingerprint_of(node) {
804 meta.push((FINGERPRINT, written));
805 }
806 if let Some(fed) = &self.input {
807 meta.push((INPUT, fed.as_str()));
808 }
809 meta.extend(
814 self.stamp
815 .iter()
816 .filter(|(what, _)| !OURS.contains(&what.as_str()))
817 .map(|(what, said)| (what.as_str(), said.as_str())),
818 );
819 match keeper.keep(key, output, &meta) {
820 Ok(()) => self.saw(|| Fact::Kept {
821 node: node.clone(),
822 key: key.clone(),
823 }),
824 Err(why) => eprintln!("what `{node}` produced could not be kept: {why}"),
825 }
826 }
827
828 fn device(&self, node: &NodeId) -> Option<&'a Device> {
830 self.placement.and_then(|placement| placement.of(node))
831 }
832
833 fn implementation(&self, node: &NodeId) -> Result<&std::sync::Arc<dyn crate::Node>, RunError> {
835 self.catalog
836 .get(node)
837 .ok_or_else(|| RunError::NoImplementation(node.clone()))
838 }
839}
840
841struct Attributed<'a> {
845 host: Host,
846 to: &'a dyn Watcher,
847}
848
849impl Watcher for Attributed<'_> {
850 fn saw(&self, fact: &Fact) {
851 self.to.saw(&Fact::Elsewhere {
852 host: self.host.clone(),
853 saw: Box::new(fact.clone()),
854 });
855 }
856}
857
858#[derive(Debug, Clone, PartialEq, Eq)]
861pub enum RunError {
862 NoImplementation(NodeId),
864 Node {
866 node: NodeId,
868 source: NodeError,
870 },
871 NoTransport(Host),
873 Transport {
875 host: Host,
877 source: TransportError,
879 },
880 NotItems {
883 node: NodeId,
885 given: String,
887 },
888 Uncounted {
891 node: NodeId,
893 asked: usize,
895 answered: usize,
897 },
898 Vanished {
901 node: NodeId,
903 },
904 Lost {
907 node: NodeId,
909 from: NodeId,
911 },
912}
913
914impl fmt::Display for RunError {
915 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
916 match self {
917 Self::NoImplementation(id) => {
918 write!(f, "node `{id}` has no registered implementation")
919 }
920 Self::Node { node, source } => write!(f, "node `{node}` failed: {source}"),
921 Self::NotItems { node, given } => write!(
922 f,
923 "`{node}` maps over the items of its input, so what reaches it and \
924 what it answers with are lists; a `{given}` is one thing and has \
925 no items. Either it does not map, or whoever feeds it should be \
926 handing it a list"
927 ),
928 Self::Uncounted {
929 node,
930 asked,
931 answered,
932 } => write!(
933 f,
934 "`{node}` was handed {asked} items and answered with {answered}: a \
935 node that maps gives back one for each, in order, or nobody can \
936 tell which answer belongs to which item"
937 ),
938 Self::NoTransport(host) => write!(
939 f,
940 "there is a slice placed on `{host}` and this executor cannot reach it"
941 ),
942 Self::Transport { host, source } => write!(f, "carrying a slice to `{host}`: {source}"),
943 Self::Vanished { node } => write!(
944 f,
945 "what was kept for `{node}` was there when the store was asked and gone when \
946 it was read, and what feeds it was not run because of that answer. Nothing \
947 was lost — run it again"
948 ),
949 Self::Lost { node, from } => write!(
950 f,
951 "`{node}` reads what `{from}` produced, and that stayed where it ran: \
952 only what can leave a process comes back from one"
953 ),
954 }
955 }
956}
957
958impl std::error::Error for RunError {}
959
960fn gather(
963 node: &NodeId,
964 from: &[NodeId],
965 graph_input: &Value,
966 produced: &HashMap<NodeId, Value>,
967) -> Result<Value, RunError> {
968 let recall = |id: &NodeId| {
971 produced.get(id).cloned().ok_or_else(|| RunError::Lost {
972 node: node.clone(),
973 from: id.clone(),
974 })
975 };
976 match from {
977 [] => Ok(graph_input.clone()),
978 [single] => recall(single),
979 many => Ok(Value::map(
980 many.iter()
981 .map(|id| Ok((id.to_string(), recall(id)?)))
982 .collect::<Result<Vec<_>, RunError>>()?,
983 )),
984 }
985}
986
987fn whole(keeper: &dyn Keeper, keys: &Keys) -> Key {
991 match keys {
992 Keys::One(key) => key.clone(),
993 Keys::PerItem(each) => keeper.combine(&each.iter().map(Key::as_str).collect::<Vec<_>>()),
994 }
995}
996
997fn sorted<T>(table: HashMap<NodeId, T>) -> Vec<(NodeId, T)> {
1000 let mut out: Vec<(NodeId, T)> = table.into_iter().collect();
1001 out.sort_by(|(a, _), (b, _)| a.cmp(b));
1002 out
1003}
1004
1005fn fingerprint(kept: &Kept) -> Option<&str> {
1007 kept.meta
1008 .iter()
1009 .find(|(what, _)| what == FINGERPRINT)
1010 .map(|(_, written)| written.as_str())
1011}
1012
1013fn needs(plan: &Plan) -> Vec<NodeId> {
1015 let produced: Vec<&NodeId> = plan.steps().map(|step| step.node).collect();
1016 let mut out: Vec<NodeId> = Vec::new();
1017 for id in plan.steps().flat_map(|step| step.from) {
1018 if !produced.contains(&id) && !out.contains(id) {
1019 out.push(id.clone());
1020 }
1021 }
1022 out
1023}
1024
1025fn terminals(plan: &Plan) -> Vec<NodeId> {
1027 let consumed: Vec<&NodeId> = plan.steps().flat_map(|step| step.from).collect();
1028 plan.steps()
1029 .map(|step| step.node)
1030 .filter(|node| !consumed.contains(node))
1031 .cloned()
1032 .collect()
1033}