Skip to main content

geop_core_program/
part.rs

1//! [`Part`]: what a program builds, as a memo table — one node per step and
2//! target it produces — and [`StepContext`], what a step declares its
3//! nodes through ([`StepContext::produce`]).
4//!
5//! A make runs when its node is new, its arguments differ, or something it
6//! read changed; nothing else ever runs. Each node keeps what it read
7//! ([`Read`]); it is reused while its arguments are equal and every read
8//! holds now. A node made again into a value equal to the last keeps its
9//! version, so what read it is reused in turn. A node of a target that is
10//! kept between processes is taken from what an earlier process kept, if
11//! it was made so there, before it is made (see [`crate::kept`]).
12
13use 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
33/// What the target a step's [`StepContext::active`] comes to is named, before
34/// the step's id.
35pub const ACTIVE: &str = "active:";
36
37/// What makes a node's value from its arguments and what it reads. A plain
38/// function, not a closure: what it makes may depend only on its
39/// arguments, which a reuse compares, and on what it reads, which is
40/// recorded. A value captured from outside would be neither, and the node
41/// would be reused after it changed:
42///
43/// ```compile_fail
44/// # use geop_core_program::{part::StepContext, target::TargetReference};
45/// fn produce(ctx: &mut StepContext, outside: u32) {
46///     let reference = TargetReference::<u32>::new("t");
47///     ctx.produce(&reference, &(), |_, _| Ok(outside));
48/// }
49/// ```
50///
51/// For the same reason arguments and values compare by value: arguments
52/// holding an `Rc` or a closure, compared by address, would make a node
53/// again for arguments alike, and a value compared so would count as
54/// changed when it is not, and could never be checked against a part
55/// built from scratch (`testing::assert_incremental`).
56pub type Make<Args, Out> = fn(&Reader, &Args) -> GeopResult<Out>;
57
58/// What a program builds: a node per step and target it produced, the
59/// order of the steps it last ran, and the inputs and state it ran with.
60/// Read only through a [`Reader`]: [`Part::current`], [`Part::before`],
61/// [`Part::after`].
62#[derive(Clone, Default)]
63pub struct Part {
64    /// The nodes of each target, one per step that produces it.
65    nodes: HashMap<String, Vec<Node>>,
66    /// The targets each step produced: what a step's end and a run's
67    /// sweep look at.
68    produced: HashMap<String, BTreeSet<String>>,
69    /// The steps of the program it last ran, in order — those after where
70    /// it stopped too.
71    order: Vec<String>,
72    positions: HashMap<String, usize>,
73    /// How many steps of `order` ran: the rest, after the marker, keep
74    /// their nodes for when they run again.
75    ran: usize,
76    /// The position of the step running, during a run.
77    running: Option<usize>,
78    /// Counts its runs: what tells a node the step running produced from
79    /// one of an earlier run.
80    run: u64,
81    pub(crate) inputs: Settings,
82    pub(crate) state: StateValues,
83    /// The steps of the last run that failed, with why.
84    failures: Vec<(String, GeopError)>,
85    /// The steps of the run going on, or of the last, that are suppressed
86    /// (see [`crate::program::ProgramStep::active`]), in order.
87    suppressed: Vec<String>,
88    /// Changes whenever a run changes any node (see [`Part::revision`]).
89    revision: u64,
90    changed: bool,
91    /// The state it read, by name: what a part placing it reads of its own
92    /// state for it.
93    pub(crate) states_read: BTreeSet<String>,
94    /// The files it read, and in which revision.
95    pub(crate) files_read: BTreeMap<String, u64>,
96    /// What it read of the part it was placed in, in context (see
97    /// [`crate::reader::Reader::outside`]), as read there: what the part
98    /// placing it reads for it, and what a build of it is shared by.
99    pub(crate) outside_read: Vec<Read>,
100    /// What was made of it whole, once for every reader of it (see
101    /// [`Reader::shared`]).
102    pub(crate) shared: Shared,
103    /// The steps its last run ran, and up to where, if it ran them all
104    /// without a failure: what a run of the same, with the same inputs and
105    /// state and every file as it read it, finds nothing to do for
106    /// ([`Part::ran_as`]).
107    ran_steps: Option<(Vec<ProgramStep>, usize)>,
108}
109
110/// What was made of a part whole, by what made it, with what that read
111/// ([`Reader::shared`]): a part of equal builds — every screw of a size —
112/// is drawn once for every instance of it. A copy of the part, to be run
113/// again, holds none of it, and neither does a part once it is run again.
114#[derive(Default)]
115pub(crate) struct Shared(std::cell::RefCell<HashMap<&'static str, SharedMade>>);
116
117/// What was made of a part whole, and what making it read.
118pub(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    /// An empty part, to be run with `inputs` and `state`: what a test
138    /// running steps by hand ([`Runner::run_step`]) starts from. A run of a
139    /// program gives its own.
140    pub fn given(inputs: Settings, state: StateValues) -> Self {
141        Self {
142            inputs,
143            state,
144            ..Self::default()
145        }
146    }
147
148    /// The steps of the program it last ran, in order.
149    pub fn order(&self) -> &[String] {
150        &self.order
151    }
152
153    /// The part as it is now: during a run, with what the steps so far
154    /// produced; after one, the finished part.
155    pub fn current(&self) -> Reader<'_> {
156        Reader::new(self, self.ran)
157    }
158
159    /// The part as it is before the step `step` of the last run: what that
160    /// step is edited against. The finished part for a step that did not
161    /// run.
162    pub fn before(&self, step: &str) -> Reader<'_> {
163        Reader::new(self, self.position(step).unwrap_or(self.ran))
164    }
165
166    /// The part as it is after the step `step` of the last run, with what it
167    /// produced. The finished part for a step that did not run.
168    pub fn after(&self, step: &str) -> Reader<'_> {
169        Reader::new(self, self.position(step).map_or(self.ran, |at| at + 1))
170    }
171
172    /// Changes exactly when a run changes any of its nodes: what tells a
173    /// view of it to draw again. Equal for two parts only where one is a
174    /// copy of the other since.
175    pub fn revision(&self) -> u64 {
176        self.revision
177    }
178
179    /// The steps of its last run that failed, in order, with why.
180    pub fn failures(&self) -> &[(String, GeopError)] {
181        &self.failures
182    }
183
184    /// The steps of the last run that are suppressed — that ran, and found
185    /// they were not to — in order (see
186    /// [`crate::program::ProgramStep::active`]).
187    pub fn suppressed(&self) -> &[String] {
188        &self.suppressed
189    }
190
191    /// The steps suppressed before position `before`, in order.
192    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    /// Notes that the step running is suppressed.
200    pub(crate) fn suppress(&mut self, step: &str) {
201        self.suppressed.push(step.to_string());
202    }
203
204    /// How many steps of the last run ran.
205    pub(crate) fn ran(&self) -> usize {
206        self.ran
207    }
208
209    /// The position of the step `step` in the last run.
210    pub(crate) fn position(&self, step: &str) -> Option<usize> {
211        self.positions.get(step).copied()
212    }
213
214    /// The node `step` produced of `name`.
215    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    /// The node of `name` a reader before position `before` sees: of the
220    /// steps before it, the one last in the program — and, for the node
221    /// being made, `own`, the nodes its own step produced already in this
222    /// run, of the other targets. With its position.
223    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    /// The version of every target a reader before `before` sees, by
243    /// name (see [`Reader::seen`]).
244    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    /// Every target a reader before `before` sees a node of a `T` of,
256    /// produced or failed, in the order the steps produced them — of one
257    /// step, by name.
258    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    /// [`Part::listed`], of the targets whose node `of_kind` takes.
268    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    /// The parts placed in the finished part — not in those — in the order
287    /// the steps placed them: each by its instance name, with the step that
288    /// placed it and its build.
289    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    /// The part placed at `path` — `bolt`, or `asm/bolt` for one placed in
302    /// a placed part, however deep — as the last run built it: none, for no
303    /// part placed so.
304    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    /// The part as the step that placed the part at `path` — `bolt`, or
316    /// `asm/bolt` — in the last run left it: what a step of the part placed,
317    /// edited where it is placed, reads in context (see
318    /// [`Reader::placed_in`]) — the parts placed before it, and where it is
319    /// itself — placed in turn where the part placing it is, so that it
320    /// reads through every level, `../../bracket`. None, for no part placed
321    /// so.
322    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    /// The part placed as `instance` that a reader before `before` sees.
342    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    /// Whether `read`, made by a reader before `before` for the node `own`,
357    /// reads the same now. A file is read through a runner: without one, it
358    /// is not known to read the same; what was read of the part it is placed
359    /// in, through `outside`, the part placing it as it reads it — without
360    /// one, neither.
361    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    /// A state `read`, made before `before` for the node `own`, made its
396    /// reader read — itself, or through the target it read — if any.
397    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            // What it read of the part it is placed in is no state of its
407            // own: the part placing it reads it for it.
408            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    /// What `read`, made by a reader before `before` for the node `own`,
419    /// read, as another process would say it, with the digest of what it
420    /// read (see [`crate::kept`]): none for what has no digest — a node that
421    /// has none, a file read without a workspace.
422    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    /// What `kept`, a read made in another process, reads for a reader
466    /// before `before` for the node `own` now, as a read of this part, with
467    /// the digest of what it reads: [`Part::read_digest`] undone. None for
468    /// what has no digest.
469    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    /// The digest of the node of `name` a reader before `before` sees, or
544    /// that it sees none.
545    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    /// Every node of a kept type it holds that has a digest and a value,
561    /// with its target's name: what is kept of it between processes.
562    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    /// Starts a run of the steps `order`, given `inputs` and `state`.
569    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    /// Starts the step at position `at`.
591    pub(crate) fn begin_step(&mut self, at: usize) {
592        self.ran = at;
593        self.running = Some(at);
594    }
595
596    /// Ends the step at position `at`: drops the nodes it did not produce
597    /// in this run.
598    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    /// Drops the nodes of `step` that `stale` says, and forgets the step if
609    /// none is left.
610    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    /// Whether its last run ran `steps` up to `end`, with `inputs` and
637    /// `state`, and none failed: run so again, with every file it read as
638    /// it read it, every step would reuse what it made.
639    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    /// Notes that its run ran `steps` up to `end` (see [`Part::ran_as`]).
653    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    /// Ends the run: drops the nodes of steps no longer in the program —
658    /// those after where it stopped stay — and notes what it read from
659    /// outside and how its steps failed.
660    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        // In an order that is the same for every build of the same reads.
694        self.outside_read.sort_by_key(Read::key);
695    }
696
697    /// Stores `node` as what its step produced of `name`.
698    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    /// Where the part, as it is now, differs from `other`: the name of
708    /// every target whose current node holds a value — or a failure —
709    /// other than the other part's, by the `PartialEq` of its type, or that
710    /// only one of them has; and so in the part placed by every placed
711    /// node, behind its name. What tells a part a run reused nodes in from
712    /// one built from scratch (see `testing::assert_incremental`).
713    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    /// Whether it holds what `other` holds: every target's current node the
748    /// same. What tells two builds of a placed part apart.
749    pub(crate) fn same_as(&self, other: &Part) -> bool {
750        self.differences(other).is_empty()
751    }
752}
753
754/// What a step is run with: the part, the runner running it, and its own
755/// id — what everything it produces is named after. A step only declares
756/// what it produces ([`StepContext::produce`]); what takes work is done in
757/// the makes.
758pub struct StepContext<'a> {
759    runner: &'a Runner,
760    part: &'a mut Part,
761    step: &'a str,
762    position: usize,
763    /// The part the part is placed in, while it is built there.
764    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    /// The id of the step.
785    pub fn step(&self) -> &'a str {
786        self.step
787    }
788
789    /// The runner running the step: the files around its program.
790    pub fn runner(&self) -> &'a Runner {
791        self.runner
792    }
793
794    /// The part as the step sees it so far, recording nothing: what a step
795    /// that declares one node per element of something asks.
796    pub fn reader(&self) -> Reader<'_> {
797        Reader::new(self.part, self.position)
798    }
799
800    /// Whether the step runs, as `active` says: a formula of the program's
801    /// parameters, not 0 where it does (see
802    /// [`crate::program::ProgramStep::active`]); always, for none. What the
803    /// formula comes to is a node of the step's own (`active:<step>`), so a
804    /// parameter it reads that changes runs the step again or suppresses
805    /// it, and nothing else does. Fails, naming the formula, where it does
806    /// not come to a number.
807    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    /// Produces the step's node of `target`: reused if it was made from
821    /// equal `args` and everything it read reads the same now, else made by
822    /// `make`, at once, so that a later node of the step can read it. Fails
823    /// as `make` fails, or if the step produced `target` already, or — for
824    /// a target that is not a chain ([`Target::CHAIN`]) — if a step before
825    /// produced it, naming both.
826    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    /// The node of `name` an earlier process kept for the program the step
920    /// is of, made of arguments of the digest `args_digest` — `args` — if
921    /// it kept one and every read it was made from reads the same now (see
922    /// [`crate::kept`]): the one used last, of those that do.
923    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            // Kept already: nothing to tell the cache of it.
961            node.noted.set(true);
962            return Some(node);
963        }
964        None
965    }
966}
967
968/// The digest of a listing of targets: each by its name and the step that
969/// produced it, as [`Read::Listed`] holds them.
970fn 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}