Skip to main content

geop_core_program/
target.rs

1//! What a part holds: targets, by name ([`TargetReference`]), each the value
2//! of a [`Target`] type, made by the steps that produce it — one node per
3//! step and target ([`Node`]) — and what a node read while it was made
4//! ([`Read`]), which says when it can be reused.
5
6use std::{
7    any::{Any, TypeId},
8    cell::Cell,
9    rc::Rc,
10};
11
12use geop_core_math::geop_error::{GeopError, GeopResult};
13
14use crate::{
15    kept::{Derivation, Digest, Keeper, Pool},
16    placed::{Located, key_behind},
17    values::{Setting, StateValue},
18};
19
20/// A type a part holds targets of. A plain value: compared by `PartialEq`,
21/// to tell a node made again into an equal value — whose readers are then
22/// reused — from one that changed.
23pub trait Target: PartialEq + Sized + 'static {
24    /// Whether several steps may produce a target of this type, each from
25    /// what the steps before left (see [`crate::reader::Reader::previous`]):
26    /// the model, the datums, the parameters' values, the listings — what
27    /// a program accumulates. A target that is not a chain — a sketch, a
28    /// placed part — has one producer, and a second is an error naming
29    /// both.
30    const CHAIN: bool = false;
31
32    /// How its values are written to be kept between processes (see
33    /// [`crate::kept`]): none for a target made again in every process —
34    /// one that is cheap to make, or holds what cannot be written.
35    const KEEPER: Option<Keeper<Self>> = None;
36}
37
38/// Plain values a target may hold: what a test produces, or a step that
39/// produces a number or a text of its own.
40macro_rules! plain_targets {
41    ($($t:ty),*) => {$(impl Target for $t {})*};
42}
43
44plain_targets!(bool, u32, u64, usize, i32, i64, f64, String);
45
46impl<T: PartialEq + 'static> Target for Vec<T> {}
47
48impl<T: PartialEq + 'static> Target for Option<T> {}
49
50/// The stable name of a target, as programs and arguments refer to it — of
51/// the part itself, or of a part placed in it, behind the instance's path
52/// ([`Located`]): `/:model`, `/bolt:model` — and the type of its value.
53pub struct TargetReference<T>(Located, std::marker::PhantomData<fn() -> T>);
54
55// By hand, since a derive would ask the same of `T`, and a reference is only
56// a name.
57impl<T> Clone for TargetReference<T> {
58    fn clone(&self) -> Self {
59        Self::located(self.0.clone())
60    }
61}
62
63impl<T> PartialEq for TargetReference<T> {
64    fn eq(&self, other: &Self) -> bool {
65        self.0 == other.0
66    }
67}
68
69impl<T> Eq for TargetReference<T> {}
70
71impl<T> std::fmt::Debug for TargetReference<T> {
72    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
73        write!(f, "TargetReference({:?})", self.0)
74    }
75}
76
77impl<T> std::fmt::Display for TargetReference<T> {
78    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
79        self.0.fmt(f)
80    }
81}
82
83impl<T> TargetReference<T> {
84    /// The target `name` of the part itself.
85    pub fn new(name: impl Into<String>) -> Self {
86        Self::located(Located::new(name))
87    }
88
89    /// The target `located` names.
90    pub fn located(located: Located) -> Self {
91        Self(located, std::marker::PhantomData)
92    }
93
94    /// The target `text` spells ([`Located::parse`]).
95    pub fn parse(text: &str) -> GeopResult<Self> {
96        Located::parse(text).map(Self::located)
97    }
98
99    /// Its name in the part it is of.
100    pub fn name(&self) -> &str {
101        self.0.name()
102    }
103
104    /// Its name, behind the path of the instance it is of.
105    pub fn location(&self) -> &Located {
106        &self.0
107    }
108
109    /// The path of the instance it is of: empty for the part itself.
110    pub fn instance(&self) -> &str {
111        self.0.path()
112    }
113
114    /// The same target as its instance's own part names it.
115    pub fn local(&self) -> Self {
116        Self::located(self.0.local())
117    }
118
119    /// The same target of the instance `instance` of the part (see
120    /// [`Located::behind`]).
121    pub fn behind(&self, instance: &str) -> Self {
122        Self::located(self.0.behind(instance))
123    }
124}
125
126/// What a node read while it was made: what has to hold now for it to be
127/// reused.
128#[derive(Clone, Debug, PartialEq)]
129pub enum Read {
130    /// The target `name` as the step `producer` produced it, in `version`.
131    Target {
132        name: String,
133        producer: String,
134        version: u64,
135    },
136    /// That no step before produced the target `name`.
137    Absent(String),
138    /// The value of the run's input `name`, itself: none if none is given.
139    Input(String, Option<Setting>),
140    /// The value of the state `name`, itself: none if none is given.
141    State(String, Option<StateValue>),
142    /// The file at `path` of the workspace, in `revision`.
143    File(String, u64),
144    /// Which targets of one type there are, in order, and who produced
145    /// each: what listing them read ([`crate::reader::Reader::targets_of`]).
146    /// A target that is added or removed changes it. The type by its name,
147    /// which another process knows it by too.
148    Listed {
149        kind: Rc<str>,
150        targets: Vec<(String, String)>,
151    },
152    /// `read`, in the part placed as `instance`.
153    Inside { instance: String, read: Box<Read> },
154}
155
156impl Read {
157    /// What tells two reads of one make apart: within one make the part
158    /// does not change, so two reads of the same thing read the same value.
159    pub(crate) fn key(&self) -> String {
160        match self {
161            Read::Target { name, .. } | Read::Absent(name) => format!("t:{name}"),
162            Read::Input(name, _) => format!("i:{name}"),
163            Read::State(name, _) => format!("s:{name}"),
164            Read::File(path, _) => format!("f:{path}"),
165            Read::Listed { kind, .. } => format!("l:{kind}"),
166            Read::Inside { instance, read } => key_behind(instance, &read.key()),
167        }
168    }
169}
170
171thread_local! {
172    /// The last version stamp given out.
173    static VERSIONS: Cell<u64> = const { Cell::new(0) };
174}
175
176/// A version no node has had: what a node gets when its value changes. One
177/// counter for every part, so that two parts' versions of one target — the
178/// previous and the next build of a placed part — are equal only where one
179/// carried the value over from the other.
180pub(crate) fn next_version() -> u64 {
181    VERSIONS.with(|versions| {
182        let next = versions.get() + 1;
183        versions.set(next);
184        next
185    })
186}
187
188/// What one step produced of one target: its value, the arguments it was
189/// made from and what it read.
190#[derive(Clone)]
191pub(crate) struct Node {
192    /// The step that produced it.
193    pub(crate) step: String,
194    /// The arguments it was made from: what a reuse compares.
195    args: Rc<dyn Any>,
196    pub(crate) reads: Rc<[Read]>,
197    /// The value, or what made making it fail.
198    pub(crate) value: Result<Rc<dyn Any>, String>,
199    /// The type of value it is produced to hold, whether or not it failed.
200    pub(crate) kind: TypeId,
201    /// Whether two values of that type are equal, by its `PartialEq`.
202    equal: fn(&dyn Any, &dyn Any) -> bool,
203    /// Changes exactly when its value does (see [`next_version`]).
204    pub(crate) version: u64,
205    /// The run of its part that produced it last, made or reused: what
206    /// tells a node of the step being run from one of an earlier run.
207    pub(crate) produced_in: u64,
208    /// A state it read, directly or through what it read, if it read any:
209    /// what makes it move with the state.
210    pub(crate) state_read: Option<String>,
211    /// How it was made, and the digest of that, as another process would
212    /// make it (see [`crate::kept`]): none if it read what has none — a
213    /// file without a workspace, or a node that has none.
214    pub(crate) kept: Option<(Rc<Derivation>, Digest)>,
215    /// The name of its type, which another process knows it by.
216    pub(crate) kind_name: &'static str,
217    /// How its value is written to be kept, if its type is kept.
218    pub(crate) write: Option<WriteKept>,
219    /// Whether the cache of its workspace has been told of it, to keep it:
220    /// a node is noted once, however often its part is run.
221    pub(crate) noted: Cell<bool>,
222}
223
224/// What a node of a kept type writes its value with: [`write_kept`] of its
225/// type.
226pub(crate) type WriteKept = fn(&dyn Any, &mut Pool) -> GeopResult<String>;
227
228/// `value`, a `T`, written by `T`'s keeper: what a node of a kept type
229/// writes its value with.
230fn write_kept<T: Target>(value: &dyn Any, pool: &mut Pool) -> GeopResult<String> {
231    match (value.downcast_ref::<T>(), T::KEEPER) {
232        (Some(value), Some(keeper)) => (keeper.write)(value, pool),
233        _ => Err(GeopError::new(format!(
234            "a value kept as a `{}` is not one",
235            std::any::type_name::<T>()
236        ))),
237    }
238}
239
240/// What a node was made from, besides its arguments: what it read, the
241/// state it read through that, and how another process would make it.
242pub(crate) struct Provenance {
243    pub reads: Vec<Read>,
244    pub state_read: Option<String>,
245    pub kept: Option<(Rc<Derivation>, Digest)>,
246}
247
248impl Node {
249    /// The node `step` made of `args` and what `from` says: `made`, in a
250    /// new version unless it equals `previous`'s value.
251    pub(crate) fn new<Out: Target, Args: PartialEq + 'static>(
252        step: &str,
253        args: Args,
254        made: Result<Out, String>,
255        from: Provenance,
256        previous: Option<&Node>,
257        produced_in: u64,
258    ) -> Self {
259        let Provenance {
260            reads,
261            state_read,
262            kept,
263        } = from;
264        let value = made.map(|value| Rc::new(value) as Rc<dyn Any>);
265        let equal: fn(&dyn Any, &dyn Any) -> bool =
266            |a, b| (a.downcast_ref::<Out>()).is_some_and(|a| b.downcast_ref::<Out>() == Some(a));
267        let same = previous.is_some_and(|old| {
268            old.kind == TypeId::of::<Out>()
269                && match (&old.value, &value) {
270                    (Ok(a), Ok(b)) => equal(a.as_ref(), b.as_ref()),
271                    (Err(a), Err(b)) => a == b,
272                    _ => false,
273                }
274        });
275        Self {
276            step: step.to_string(),
277            args: Rc::new(args),
278            reads: reads.into(),
279            value,
280            kind: TypeId::of::<Out>(),
281            equal,
282            version: match (same, previous) {
283                (true, Some(old)) => old.version,
284                _ => next_version(),
285            },
286            produced_in,
287            state_read,
288            // Equal to what it was, it is what it was: what read it keeps
289            // its digest too, as it keeps its version.
290            kept: match (same, previous) {
291                (true, Some(old)) => match (old.digest(), kept) {
292                    (Some(digest), Some((derivation, _))) => Some((derivation, digest)),
293                    _ => None,
294                },
295                _ => kept,
296            },
297            kind_name: std::any::type_name::<Out>(),
298            write: Out::KEEPER.map(|_| write_kept::<Out> as WriteKept),
299            noted: Cell::new(false),
300        }
301    }
302
303    /// Its digest, as another process would make it (see [`crate::kept`]).
304    pub(crate) fn digest(&self) -> Option<Digest> {
305        self.kept.as_ref().map(|(_, digest)| *digest)
306    }
307
308    /// Whether it was made, as an `Out`, from `args`, and holds a value: a
309    /// failed node is made again.
310    pub(crate) fn made_from<Out: 'static, Args: PartialEq + 'static>(&self, args: &Args) -> bool {
311        self.value.is_ok()
312            && self.kind == TypeId::of::<Out>()
313            && self.args.downcast_ref::<Args>() == Some(args)
314    }
315
316    /// Its value, a `T` — or why there is none: it failed, or it is no `T`.
317    /// `name` is the target's, for the error.
318    pub(crate) fn get<T: 'static>(&self, name: &str) -> GeopResult<&T> {
319        let value = self.value.as_ref().map_err(|e| self.failed(name, e))?;
320        value.downcast_ref::<T>().ok_or_else(|| {
321            GeopError::new(format!(
322                "target `{name}` is not a `{}`",
323                std::any::type_name::<T>()
324            ))
325        })
326    }
327
328    /// Why it failed, if it did. `name` is the target's, for the error.
329    pub(crate) fn failure(&self, name: &str) -> Option<GeopError> {
330        self.value.as_ref().err().map(|e| self.failed(name, e))
331    }
332
333    /// That it failed, for `e`.
334    fn failed(&self, name: &str, e: &str) -> GeopError {
335        GeopError::new(format!(
336            "target `{name}` failed in step `{}`: {e}",
337            self.step
338        ))
339    }
340
341    /// Whether it holds what `other` holds — a node of another part, say
342    /// — by the `PartialEq` of its type: equal values, or failures for the
343    /// same reason.
344    pub(crate) fn same_as(&self, other: &Node) -> bool {
345        self.kind == other.kind
346            && match (&self.value, &other.value) {
347                (Ok(a), Ok(b)) => Rc::ptr_eq(a, b) || (self.equal)(a.as_ref(), b.as_ref()),
348                (Err(a), Err(b)) => a == b,
349                _ => false,
350            }
351    }
352}
353
354#[cfg(test)]
355mod tests {
356    use super::*;
357
358    /// A target of the part itself is `/:name`, one of a placed part
359    /// `/path:name`; it reads back as it is written, and moves behind an
360    /// instance and back.
361    #[test]
362    fn a_target_says_which_part_it_is_of() {
363        let model = TargetReference::<f64>::new("model");
364        assert_eq!(model.to_string(), "/:model");
365        assert_eq!(TargetReference::parse("/:model").unwrap(), model);
366        let deep = TargetReference::<f64>::parse("/a/b:model").unwrap();
367        assert_eq!((deep.instance(), deep.name()), ("a/b", "model"));
368        assert_eq!(deep.to_string(), "/a/b:model");
369        assert_eq!(model.behind("b").behind("a"), deep);
370        assert_eq!(deep.local(), model);
371        // Equal by where and what, not by spelling alone.
372        assert_ne!(model.behind("a"), model);
373        assert!(TargetReference::<f64>::parse("model").is_err());
374        assert!(TargetReference::<f64>::parse("a/b:model").is_err());
375        let colon = TargetReference::<f64>::parse("/a:b:c").unwrap();
376        assert_eq!((colon.instance(), colon.name()), ("a", "b:c"));
377    }
378}