1use std::{
14 any::TypeId,
15 collections::{BTreeMap, BTreeSet, HashMap},
16 fmt::Debug,
17 rc::Rc,
18};
19
20use geop_core_math::geop_error::{GeopError, GeopResult};
21
22use crate::{
23 kept::{Derivation, Digest, Hasher, Keeper, KeptRead, digest_debug},
24 numeric::{evaluate, read_parameter},
25 placed::{OUTSIDE, PLACED, PlacedPart, placed, split_instance_path},
26 program::ProgramStep,
27 reader::{Reader, Recording},
28 runner::Runner,
29 target::{Node, Provenance, Read, Target, TargetReference, next_version},
30 values::{Settings, StateValues},
31};
32
33pub const ACTIVE: &str = "active:";
36
37pub type Make<Args, Out> = fn(&Reader, &Args) -> GeopResult<Out>;
57
58#[derive(Clone, Default)]
63pub struct Part {
64 nodes: HashMap<String, Vec<Node>>,
66 produced: HashMap<String, BTreeSet<String>>,
69 order: Vec<String>,
72 positions: HashMap<String, usize>,
73 ran: usize,
76 running: Option<usize>,
78 run: u64,
81 pub(crate) inputs: Settings,
82 pub(crate) state: StateValues,
83 failures: Vec<(String, GeopError)>,
85 suppressed: Vec<String>,
88 revision: u64,
90 changed: bool,
91 pub(crate) states_read: BTreeSet<String>,
94 pub(crate) files_read: BTreeMap<String, u64>,
96 pub(crate) outside_read: Vec<Read>,
100 pub(crate) shared: Shared,
103 ran_steps: Option<(Vec<ProgramStep>, usize)>,
108}
109
110#[derive(Default)]
115pub(crate) struct Shared(std::cell::RefCell<HashMap<&'static str, SharedMade>>);
116
117pub(crate) type SharedMade = (Rc<dyn std::any::Any>, crate::reader::Reads);
119
120impl Clone for Shared {
121 fn clone(&self) -> Self {
122 Self::default()
123 }
124}
125
126impl Shared {
127 pub(crate) fn get(&self, key: &str) -> Option<SharedMade> {
128 self.0.borrow().get(key).cloned()
129 }
130
131 pub(crate) fn insert(&self, key: &'static str, made: SharedMade) {
132 self.0.borrow_mut().insert(key, made);
133 }
134}
135
136impl Part {
137 pub fn given(inputs: Settings, state: StateValues) -> Self {
141 Self {
142 inputs,
143 state,
144 ..Self::default()
145 }
146 }
147
148 pub fn order(&self) -> &[String] {
150 &self.order
151 }
152
153 pub fn current(&self) -> Reader<'_> {
156 Reader::new(self, self.ran)
157 }
158
159 pub fn before(&self, step: &str) -> Reader<'_> {
163 Reader::new(self, self.position(step).unwrap_or(self.ran))
164 }
165
166 pub fn after(&self, step: &str) -> Reader<'_> {
169 Reader::new(self, self.position(step).map_or(self.ran, |at| at + 1))
170 }
171
172 pub fn revision(&self) -> u64 {
176 self.revision
177 }
178
179 pub fn failures(&self) -> &[(String, GeopError)] {
181 &self.failures
182 }
183
184 pub fn suppressed(&self) -> &[String] {
188 &self.suppressed
189 }
190
191 pub(crate) fn suppressed_before(&self, before: usize) -> Vec<&str> {
193 (self.suppressed.iter())
194 .filter(|step| self.position(step).is_some_and(|at| at < before))
195 .map(String::as_str)
196 .collect()
197 }
198
199 pub(crate) fn suppress(&mut self, step: &str) {
201 self.suppressed.push(step.to_string());
202 }
203
204 pub(crate) fn ran(&self) -> usize {
206 self.ran
207 }
208
209 pub(crate) fn position(&self, step: &str) -> Option<usize> {
211 self.positions.get(step).copied()
212 }
213
214 pub(crate) fn node(&self, name: &str, step: &str) -> Option<&Node> {
216 (self.nodes.get(name)?.iter()).find(|node| node.step == step)
217 }
218
219 pub(crate) fn visible(
224 &self,
225 name: &str,
226 before: usize,
227 own: Option<&str>,
228 ) -> Option<(usize, &Node)> {
229 (self.nodes.get(name)?.iter())
230 .filter_map(|node| {
231 let at = self.position(&node.step)?;
232 let seen = at < before.min(self.ran)
233 || (Some(at) == self.running
234 && at == before
235 && node.produced_in == self.run
236 && own != Some(name));
237 seen.then_some((at, node))
238 })
239 .max_by_key(|(at, _)| *at)
240 }
241
242 pub(crate) fn versions(&self, before: usize, own: Option<&str>) -> Vec<(String, u64)> {
245 let mut versions: Vec<(String, u64)> = (self.nodes.keys())
246 .filter_map(|name| {
247 let (_, node) = self.visible(name, before, own)?;
248 Some((name.clone(), node.version))
249 })
250 .collect();
251 versions.sort();
252 versions
253 }
254
255 pub(crate) fn listed(
259 &self,
260 kind: TypeId,
261 before: usize,
262 own: Option<&str>,
263 ) -> Vec<(&str, &Node)> {
264 self.listed_where(|node| node.kind == kind, before, own)
265 }
266
267 fn listed_where(
269 &self,
270 of_kind: impl Fn(&Node) -> bool,
271 before: usize,
272 own: Option<&str>,
273 ) -> Vec<(&str, &Node)> {
274 let mut found: Vec<(usize, &str, &Node)> = (self.nodes.keys())
275 .filter_map(|name| {
276 let (at, node) = self.visible(name, before, own)?;
277 of_kind(node).then_some((at, name.as_str(), node))
278 })
279 .collect();
280 found.sort_by(|a, b| (a.0, a.1).cmp(&(b.0, b.1)));
281 (found.into_iter())
282 .map(|(_, name, node)| (name, node))
283 .collect()
284 }
285
286 pub fn placed(&self) -> Vec<(&str, &str, &PlacedPart)> {
290 (self
291 .listed(TypeId::of::<PlacedPart>(), self.ran, None)
292 .into_iter())
293 .filter_map(|(name, node)| {
294 let instance = name.strip_prefix(PLACED)?;
295 let placed = node.get::<PlacedPart>(name).ok()?;
296 Some((instance, node.step.as_str(), placed))
297 })
298 .collect()
299 }
300
301 pub fn placed_at_path(&self, path: &str) -> Option<&PlacedPart> {
305 let (outer, name) = split_instance_path(path);
306 let part = match outer.is_empty() {
307 true => self,
308 false => &self.placed_at_path(outer)?.part,
309 };
310 (part.placed().into_iter())
311 .find(|(instance, _, _)| *instance == name)
312 .map(|(_, _, placed)| placed)
313 }
314
315 pub fn placing(&self, path: &str) -> Option<Reader<'_>> {
323 let (outer, instance) = split_instance_path(path);
324 let part = match outer.is_empty() {
325 true => self,
326 false => &self.placed_at_path(outer)?.part,
327 };
328 let reference = placed(instance);
329 let (name, nodes) = part.nodes.get_key_value(reference.name())?;
330 let at = (nodes.iter())
331 .filter_map(|node| part.position(&node.step))
332 .filter(|at| *at < part.ran)
333 .max()?;
334 let reader = Reader::new(part, at + 1).for_make_of(name);
335 match outer.is_empty() {
336 true => Some(reader),
337 false => Some(reader.placed_in(self.placing(outer)?)),
338 }
339 }
340
341 pub(crate) fn placed_at(
343 &self,
344 instance: &str,
345 before: usize,
346 own: Option<&str>,
347 ) -> GeopResult<&Rc<Part>> {
348 let reference = placed(instance);
349 let name = reference.name();
350 let (_, node) = self.visible(name, before, own).ok_or_else(|| {
351 GeopError::new(format!("no part is placed as `{instance}` before here"))
352 })?;
353 Ok(&node.get::<PlacedPart>(name)?.part)
354 }
355
356 pub(crate) fn holds(
362 &self,
363 read: &Read,
364 before: usize,
365 own: Option<&str>,
366 runner: Option<&Runner>,
367 outside: Option<&Reader>,
368 ) -> bool {
369 match read {
370 Read::Target {
371 name,
372 producer,
373 version,
374 } => (self.visible(name, before, own))
375 .is_some_and(|(_, node)| node.step == *producer && node.version == *version),
376 Read::Absent(name) => self.visible(name, before, own).is_none(),
377 Read::Input(name, value) => self.inputs.get(name) == value.as_ref(),
378 Read::State(name, value) => self.state.get(name) == value.as_ref(),
379 Read::File(path, revision) => runner.is_some_and(|r| r.revision(path) == *revision),
380 Read::Listed { kind, targets } => {
381 let now = self.listed_where(|node| node.kind_name == &**kind, before, own);
382 now.len() == targets.len()
383 && (now.iter().zip(targets))
384 .all(|((name, node), (was, by))| name == was && node.step == *by)
385 }
386 Read::Inside { instance, read } if instance == OUTSIDE => outside.is_some_and(|o| {
387 (o.part()).holds(read, o.position(), o.own(), runner, o.outside_reader())
388 }),
389 Read::Inside { instance, read } => self
390 .placed_at(instance, before, own)
391 .is_ok_and(|inner| inner.holds(read, inner.ran, None, runner, None)),
392 }
393 }
394
395 pub(crate) fn state_read(
398 &self,
399 read: &Read,
400 before: usize,
401 own: Option<&str>,
402 ) -> Option<String> {
403 match read {
404 Read::State(name, _) => Some(name.clone()),
405 Read::Target { name, producer, .. } => self.node(name, producer)?.state_read.clone(),
406 Read::Inside { instance, .. } if instance == OUTSIDE => None,
409 Read::Inside { instance, read } => {
410 let inner = self.placed_at(instance, before, own).ok()?;
411 let state = inner.state_read(read, inner.ran, None)?;
412 Some(crate::placed::key_behind(instance, &state))
413 }
414 Read::Absent(_) | Read::Input(..) | Read::File(..) | Read::Listed { .. } => None,
415 }
416 }
417
418 pub(crate) fn read_digest(
423 &self,
424 read: &Read,
425 before: usize,
426 own: Option<&str>,
427 runner: &Runner,
428 outside: Option<&Reader>,
429 ) -> Option<(KeptRead, Digest)> {
430 Some(match read {
431 Read::Target { name, .. } | Read::Absent(name) => (
432 KeptRead::Target(name.clone()),
433 self.target_digest(name, before, own)?,
434 ),
435 Read::Input(name, _) => (KeptRead::Input(name.clone()), self.input_digest(name)),
436 Read::State(name, _) => (KeptRead::State(name.clone()), self.state_digest(name)),
437 Read::File(path, _) => (KeptRead::File(path.clone()), runner.file_digest(path)?),
438 Read::Listed { kind, targets } => (
439 KeptRead::Listed(kind.to_string()),
440 listed_digest(
441 targets
442 .iter()
443 .map(|(name, by)| (name.as_str(), by.as_str())),
444 ),
445 ),
446 Read::Inside { instance, read } if instance == OUTSIDE => {
447 let o = outside?;
448 let (read, digest) = (o.part()).read_digest(
449 read,
450 o.position(),
451 o.own(),
452 runner,
453 o.outside_reader(),
454 )?;
455 (KeptRead::Inside(instance.clone(), Box::new(read)), digest)
456 }
457 Read::Inside { instance, read } => {
458 let inner = self.placed_at(instance, before, own).ok()?;
459 let (read, digest) = inner.read_digest(read, inner.ran, None, runner, None)?;
460 (KeptRead::Inside(instance.clone(), Box::new(read)), digest)
461 }
462 })
463 }
464
465 pub(crate) fn resolve(
470 &self,
471 kept: &KeptRead,
472 before: usize,
473 own: Option<&str>,
474 runner: &Runner,
475 outside: Option<&Reader>,
476 ) -> Option<(Read, Digest)> {
477 Some(match kept {
478 KeptRead::Target(name) => {
479 let digest = self.target_digest(name, before, own)?;
480 let read = match self.visible(name, before, own) {
481 Some((_, node)) => Read::Target {
482 name: name.clone(),
483 producer: node.step.clone(),
484 version: node.version,
485 },
486 None => Read::Absent(name.clone()),
487 };
488 (read, digest)
489 }
490 KeptRead::Input(name) => (
491 Read::Input(name.clone(), self.inputs.get(name).cloned()),
492 self.input_digest(name),
493 ),
494 KeptRead::State(name) => (
495 Read::State(name.clone(), self.state.get(name).cloned()),
496 self.state_digest(name),
497 ),
498 KeptRead::File(path) => (
499 Read::File(path.clone(), runner.revision(path)),
500 runner.file_digest(path)?,
501 ),
502 KeptRead::Listed(kind_name) => {
503 let listed = self.listed_where(|node| node.kind_name == kind_name, before, own);
504 let read = Read::Listed {
505 kind: kind_name.as_str().into(),
506 targets: (listed.iter())
507 .map(|(name, node)| (name.to_string(), node.step.clone()))
508 .collect(),
509 };
510 let digest = listed_digest(
511 listed
512 .iter()
513 .map(|(name, node)| (*name, node.step.as_str())),
514 );
515 (read, digest)
516 }
517 KeptRead::Inside(instance, kept) => {
518 let (read, digest) = match instance == OUTSIDE {
519 true => {
520 let o = outside?;
521 (o.part()).resolve(
522 kept,
523 o.position(),
524 o.own(),
525 runner,
526 o.outside_reader(),
527 )?
528 }
529 false => {
530 let inner = self.placed_at(instance, before, own).ok()?;
531 inner.resolve(kept, inner.ran, None, runner, None)?
532 }
533 };
534 let read = Read::Inside {
535 instance: instance.clone(),
536 read: Box::new(read),
537 };
538 (read, digest)
539 }
540 })
541 }
542
543 fn target_digest(&self, name: &str, before: usize, own: Option<&str>) -> Option<Digest> {
546 match self.visible(name, before, own) {
547 Some((_, node)) => node.digest(),
548 None => Some(digest_debug("absent", name)),
549 }
550 }
551
552 fn input_digest(&self, name: &str) -> Digest {
553 digest_debug("input", &self.inputs.get(name))
554 }
555
556 fn state_digest(&self, name: &str) -> Digest {
557 digest_debug("state", &self.state.get(name))
558 }
559
560 pub(crate) fn kept_nodes(&self) -> impl Iterator<Item = (&str, &Node)> {
563 (self.nodes.iter())
564 .flat_map(|(name, nodes)| nodes.iter().map(move |node| (name.as_str(), node)))
565 .filter(|(_, node)| node.write.is_some() && node.kept.is_some() && node.value.is_ok())
566 }
567
568 pub(crate) fn begin(&mut self, order: Vec<String>, inputs: &Settings, state: &StateValues) {
570 self.ran_steps = None;
571 self.shared = Shared::default();
572 self.positions.clear();
573 for (at, step) in order.iter().enumerate() {
574 self.positions.entry(step.clone()).or_insert(at);
575 }
576 self.order = order;
577 self.ran = 0;
578 self.running = None;
579 self.run += 1;
580 self.suppressed.clear();
581 self.changed = false;
582 if self.inputs != *inputs {
583 self.inputs = inputs.clone();
584 }
585 if self.state != *state {
586 self.state = state.clone();
587 }
588 }
589
590 pub(crate) fn begin_step(&mut self, at: usize) {
592 self.ran = at;
593 self.running = Some(at);
594 }
595
596 pub(crate) fn end_step(&mut self, at: usize) {
599 self.running = None;
600 self.ran = at + 1;
601 let Some(step) = self.order.get(at).cloned() else {
602 return;
603 };
604 let run = self.run;
605 self.drop_nodes(&step, |node| node.produced_in != run);
606 }
607
608 fn drop_nodes(&mut self, step: &str, stale: impl Fn(&Node) -> bool) {
611 let Some(names) = self.produced.get_mut(step) else {
612 return;
613 };
614 let mut dropped = Vec::new();
615 for name in names.iter() {
616 if let Some(nodes) = self.nodes.get_mut(name) {
617 let before = nodes.len();
618 nodes.retain(|node| node.step != step || !stale(node));
619 if nodes.len() < before {
620 dropped.push(name.clone());
621 }
622 if nodes.is_empty() {
623 self.nodes.remove(name);
624 }
625 }
626 }
627 for name in &dropped {
628 names.remove(name);
629 }
630 if names.is_empty() {
631 self.produced.remove(step);
632 }
633 self.changed |= !dropped.is_empty();
634 }
635
636 pub(crate) fn ran_as(
640 &self,
641 steps: &[ProgramStep],
642 end: usize,
643 inputs: &Settings,
644 state: &StateValues,
645 ) -> bool {
646 self.failures.is_empty()
647 && self.state == *state
648 && self.inputs == *inputs
649 && (self.ran_steps.as_ref()).is_some_and(|(ran, upto)| *upto == end && ran == steps)
650 }
651
652 pub(crate) fn ran_through(&mut self, steps: &[ProgramStep], end: usize) {
654 self.ran_steps = self.failures.is_empty().then(|| (steps.to_vec(), end));
655 }
656
657 pub(crate) fn end(&mut self, failures: Vec<(String, GeopError)>) {
661 self.running = None;
662 let gone: Vec<String> = (self.produced.keys())
663 .filter(|step| !self.positions.contains_key(*step))
664 .cloned()
665 .collect();
666 for step in gone {
667 self.drop_nodes(&step, |_| true);
668 }
669 if self.changed || self.failures != failures {
670 self.revision = next_version();
671 }
672 self.failures = failures;
673 self.states_read.clear();
674 self.files_read.clear();
675 self.outside_read.clear();
676 let mut outside = std::collections::HashSet::new();
677 for read in (self.nodes.values().flatten()).flat_map(|node| node.reads.iter()) {
678 match read {
679 Read::State(name, _) => {
680 self.states_read.insert(name.clone());
681 }
682 Read::File(path, revision) => {
683 self.files_read.insert(path.clone(), *revision);
684 }
685 Read::Inside { instance, read }
686 if instance == OUTSIDE && outside.insert(read.key()) =>
687 {
688 self.outside_read.push((**read).clone());
689 }
690 _ => {}
691 }
692 }
693 self.outside_read.sort_by_key(Read::key);
695 }
696
697 fn insert(&mut self, name: &str, node: Node) {
699 (self.produced.entry(node.step.clone()).or_default()).insert(name.to_string());
700 let nodes = self.nodes.entry(name.to_string()).or_default();
701 match nodes.iter_mut().find(|old| old.step == node.step) {
702 Some(old) => *old = node,
703 None => nodes.push(node),
704 }
705 }
706
707 pub fn differences(&self, other: &Part) -> Vec<String> {
714 let names: BTreeSet<&str> = (self.nodes.keys().chain(other.nodes.keys()))
715 .map(String::as_str)
716 .collect();
717 let mut differences = Vec::new();
718 for name in names {
719 let own = self.visible(name, self.ran, None).map(|(_, node)| node);
720 let others = other.visible(name, other.ran, None).map(|(_, node)| node);
721 match (own, others) {
722 (Some(a), Some(b)) => {
723 let placed = (a.value.as_ref().ok())
724 .and_then(|a| a.downcast_ref::<PlacedPart>())
725 .zip(
726 b.value
727 .as_ref()
728 .ok()
729 .and_then(|b| b.downcast_ref::<PlacedPart>()),
730 );
731 match placed {
732 Some((a, b)) => differences.extend(
733 (a.part.differences(&b.part).into_iter())
734 .map(|inside| format!("{name}: {inside}")),
735 ),
736 None if !a.same_as(b) => differences.push(name.to_string()),
737 None => {}
738 }
739 }
740 (None, None) => {}
741 _ => differences.push(name.to_string()),
742 }
743 }
744 differences
745 }
746
747 pub(crate) fn same_as(&self, other: &Part) -> bool {
750 self.differences(other).is_empty()
751 }
752}
753
754pub struct StepContext<'a> {
759 runner: &'a Runner,
760 part: &'a mut Part,
761 step: &'a str,
762 position: usize,
763 outside: Option<Rc<Reader<'a>>>,
765}
766
767impl<'a> StepContext<'a> {
768 pub(crate) fn new(
769 runner: &'a Runner,
770 part: &'a mut Part,
771 step: &'a str,
772 position: usize,
773 outside: Option<Rc<Reader<'a>>>,
774 ) -> Self {
775 Self {
776 runner,
777 part,
778 step,
779 position,
780 outside,
781 }
782 }
783
784 pub fn step(&self) -> &'a str {
786 self.step
787 }
788
789 pub fn runner(&self) -> &'a Runner {
791 self.runner
792 }
793
794 pub fn reader(&self) -> Reader<'_> {
797 Reader::new(self.part, self.position)
798 }
799
800 pub(crate) fn active(&mut self, active: Option<&str>) -> GeopResult<bool> {
808 let Some(formula) = active else {
809 return Ok(true);
810 };
811 let target = TargetReference::<bool>::new(format!("{ACTIVE}{}", self.step));
812 let ctx = |e: GeopError| e.with_context(format!("whether the step runs: {formula:?}"));
813 self.produce(&target, &formula.to_string(), |reader, formula| {
814 Ok(evaluate(formula, |name| read_parameter(reader, name))? != 0.0)
815 })
816 .map_err(ctx)?;
817 self.reader().target(&target).copied().map_err(ctx)
818 }
819
820 pub fn produce<T: Target, Args: PartialEq + Clone + Debug + 'static>(
827 &mut self,
828 target: &TargetReference<T>,
829 args: &Args,
830 make: Make<Args, T>,
831 ) -> GeopResult<()> {
832 let (name, step, at) = (target.name(), self.step, self.position);
833 let part = &*self.part;
834 let old = part.node(name, step);
835 if old.is_some_and(|node| node.produced_in == part.run) {
836 return Err(GeopError::new(format!(
837 "target `{name}` is produced twice by step `{step}`"
838 )));
839 }
840 if !T::CHAIN
841 && let Some((_, earlier)) = part.visible(name, at, Some(name))
842 {
843 return Err(GeopError::new(format!(
844 "target `{name}` is produced by step `{}` and again by step `{step}`: only a \
845 chain target may be produced by several steps",
846 earlier.step
847 )));
848 }
849 let reusable = old.is_some_and(|node| {
850 node.made_from::<T, Args>(args)
851 && (node.reads.iter()).all(|read| {
852 part.holds(
853 read,
854 at,
855 Some(name),
856 Some(self.runner),
857 self.outside.as_deref(),
858 )
859 })
860 });
861 if reusable {
862 let run = self.part.run;
863 let nodes = self.part.nodes.get_mut(name).into_iter().flatten();
864 for node in nodes.filter(|node| node.step == step) {
865 node.produced_in = run;
866 }
867 return Ok(());
868 }
869
870 let args_digest = digest_debug("args", args);
871 if let Some(keeper) = T::KEEPER
872 && let Some(node) = self.restore(name, args, args_digest, &keeper)
873 {
874 self.runner.count_restored();
875 let changed = old.is_none_or(|old| old.version != node.version);
876 self.part.changed |= changed;
877 self.part.insert(name, node);
878 return Ok(());
879 }
880
881 self.runner.count_make(T::KEEPER.is_some());
882 let part = &*self.part;
883 let recording = Recording::default();
884 let outside = self.outside.clone();
885 let reader = Reader::making(part, at, name, old, self.runner, &recording, outside);
886 let made = make(&reader, args)
887 .map_err(|e| e.with_context(format!("making target `{name}` in step `{step}`")));
888 let reads = recording.take();
889 let state_read = (reads.iter()).find_map(|read| part.state_read(read, at, Some(name)));
890 let kept = (reads.iter())
891 .map(|read| {
892 part.read_digest(read, at, Some(name), self.runner, self.outside.as_deref())
893 })
894 .collect::<Option<Vec<_>>>()
895 .map(|reads| {
896 let derivation = Derivation {
897 args: args_digest,
898 reads,
899 };
900 let digest = derivation.digest(step, name, std::any::type_name::<T>());
901 (Rc::new(derivation), digest)
902 });
903 let (value, result) = match made {
904 Ok(value) => (Ok(value), Ok(())),
905 Err(e) => (Err(e.root_message().to_string()), Err(e)),
906 };
907 let from = Provenance {
908 reads,
909 state_read,
910 kept,
911 };
912 let node = Node::new(step, args.clone(), value, from, old, part.run);
913 let changed = old.is_none_or(|old| old.version != node.version);
914 self.part.changed |= changed;
915 self.part.insert(name, node);
916 result
917 }
918
919 fn restore<T: Target, Args: PartialEq + Clone + 'static>(
924 &self,
925 name: &str,
926 args: &Args,
927 args_digest: Digest,
928 keeper: &Keeper<T>,
929 ) -> Option<Node> {
930 let (runner, part, step, at) = (self.runner, &*self.part, self.step, self.position);
931 let cache = runner.workspace.as_deref()?.cache()?;
932 let path = &runner.location.file;
933 let records = cache.builds(path, |builds| builds.records(step, name))?;
934 let kind = std::any::type_name::<T>();
935 for record in records.iter().rev() {
936 if record.kind != kind || record.derivation.args != args_digest {
937 continue;
938 }
939 let reads = (record.derivation.reads.iter())
940 .map(|(kept, digest)| {
941 let outside = self.outside.as_deref();
942 let (read, now) = part.resolve(kept, at, Some(name), runner, outside)?;
943 (now == *digest).then_some(read)
944 })
945 .collect::<Option<Vec<_>>>();
946 let Some(reads) = reads else {
947 continue;
948 };
949 let Some(Ok(value)) = cache.builds(path, |builds| builds.value(record, keeper)) else {
950 continue;
951 };
952 cache.builds(path, |builds| builds.used(record));
953 let from = Provenance {
954 state_read: (reads.iter()).find_map(|read| part.state_read(read, at, Some(name))),
955 reads,
956 kept: Some((Rc::new(record.derivation.clone()), record.digest)),
957 };
958 let old = part.node(name, step);
959 let node = Node::new(step, args.clone(), Ok(value), from, old, part.run);
960 node.noted.set(true);
962 return Some(node);
963 }
964 None
965 }
966}
967
968fn listed_digest<'n>(targets: impl Iterator<Item = (&'n str, &'n str)>) -> Digest {
971 let mut hasher = Hasher::default();
972 hasher.text("listed");
973 for (name, by) in targets {
974 hasher.text(name).text(by);
975 }
976 hasher.finish()
977}