1use 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
20pub trait Target: PartialEq + Sized + 'static {
24 const CHAIN: bool = false;
31
32 const KEEPER: Option<Keeper<Self>> = None;
36}
37
38macro_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
50pub struct TargetReference<T>(Located, std::marker::PhantomData<fn() -> T>);
54
55impl<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 pub fn new(name: impl Into<String>) -> Self {
86 Self::located(Located::new(name))
87 }
88
89 pub fn located(located: Located) -> Self {
91 Self(located, std::marker::PhantomData)
92 }
93
94 pub fn parse(text: &str) -> GeopResult<Self> {
96 Located::parse(text).map(Self::located)
97 }
98
99 pub fn name(&self) -> &str {
101 self.0.name()
102 }
103
104 pub fn location(&self) -> &Located {
106 &self.0
107 }
108
109 pub fn instance(&self) -> &str {
111 self.0.path()
112 }
113
114 pub fn local(&self) -> Self {
116 Self::located(self.0.local())
117 }
118
119 pub fn behind(&self, instance: &str) -> Self {
122 Self::located(self.0.behind(instance))
123 }
124}
125
126#[derive(Clone, Debug, PartialEq)]
129pub enum Read {
130 Target {
132 name: String,
133 producer: String,
134 version: u64,
135 },
136 Absent(String),
138 Input(String, Option<Setting>),
140 State(String, Option<StateValue>),
142 File(String, u64),
144 Listed {
149 kind: Rc<str>,
150 targets: Vec<(String, String)>,
151 },
152 Inside { instance: String, read: Box<Read> },
154}
155
156impl Read {
157 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 static VERSIONS: Cell<u64> = const { Cell::new(0) };
174}
175
176pub(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#[derive(Clone)]
191pub(crate) struct Node {
192 pub(crate) step: String,
194 args: Rc<dyn Any>,
196 pub(crate) reads: Rc<[Read]>,
197 pub(crate) value: Result<Rc<dyn Any>, String>,
199 pub(crate) kind: TypeId,
201 equal: fn(&dyn Any, &dyn Any) -> bool,
203 pub(crate) version: u64,
205 pub(crate) produced_in: u64,
208 pub(crate) state_read: Option<String>,
211 pub(crate) kept: Option<(Rc<Derivation>, Digest)>,
215 pub(crate) kind_name: &'static str,
217 pub(crate) write: Option<WriteKept>,
219 pub(crate) noted: Cell<bool>,
222}
223
224pub(crate) type WriteKept = fn(&dyn Any, &mut Pool) -> GeopResult<String>;
227
228fn 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
240pub(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 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 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 pub(crate) fn digest(&self) -> Option<Digest> {
305 self.kept.as_ref().map(|(_, digest)| *digest)
306 }
307
308 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 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 pub(crate) fn failure(&self, name: &str) -> Option<GeopError> {
330 self.value.as_ref().err().map(|e| self.failed(name, e))
331 }
332
333 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 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 #[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 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}