1use std::{any::TypeId, cell::RefCell, collections::HashSet, rc::Rc};
6
7use geop_core_math::geop_error::{GeopError, GeopResult};
8use serde::{Serialize, de::DeserializeOwned};
9
10use crate::{
11 element::{ElementReference, HasElements, Kind},
12 part::Part,
13 placed::{
14 INSTANCE_SEPARATOR, InInstance, Located, OUTSIDE, PLACED, PlacedPart, key_behind,
15 outermost_instance,
16 },
17 program::Program,
18 runner::{Runner, quoted},
19 target::{Node, Read, TargetReference},
20 values::{Setting, Settings, StateValue, StateValues},
21 workspace::Location,
22};
23
24#[derive(Default)]
26pub(crate) struct Recording {
27 reads: RefCell<Vec<Read>>,
28 keys: RefCell<HashSet<String>>,
29}
30
31impl Recording {
32 fn record(&self, read: Read) {
33 if self.keys.borrow_mut().insert(read.key()) {
34 self.reads.borrow_mut().push(read);
35 }
36 }
37
38 pub(crate) fn take(self) -> Vec<Read> {
39 self.reads.into_inner()
40 }
41}
42
43#[derive(Clone)]
61pub struct Reader<'a> {
62 part: &'a Part,
63 before: usize,
65 own: Option<&'a str>,
68 last: Option<&'a Node>,
71 runner: Option<&'a Runner>,
72 recording: Option<&'a Recording>,
73 path: Vec<String>,
76 outside: Option<Rc<Reader<'a>>>,
80 trying: Option<(&'a StateValues, String)>,
85}
86
87impl<'a> Reader<'a> {
88 pub(crate) fn new(part: &'a Part, before: usize) -> Self {
91 Self {
92 part,
93 before,
94 own: None,
95 last: None,
96 runner: None,
97 recording: None,
98 path: Vec::new(),
99 outside: None,
100 trying: None,
101 }
102 }
103
104 pub fn trying(&self, state: &'a StateValues) -> Reader<'a> {
113 Reader {
114 trying: Some((state, String::new())),
115 ..self.clone()
116 }
117 }
118
119 pub fn after_step(&self, step: &str) -> Reader<'a> {
123 Reader {
124 before: (self.part.position(step)).map_or(self.part.ran(), |at| at + 1),
125 own: None,
126 last: None,
127 ..self.clone()
128 }
129 }
130
131 pub(crate) fn making(
135 part: &'a Part,
136 before: usize,
137 own: &'a str,
138 last: Option<&'a Node>,
139 runner: &'a Runner,
140 recording: &'a Recording,
141 outside: Option<Rc<Reader<'a>>>,
142 ) -> Self {
143 Self {
144 part,
145 before,
146 own: Some(own),
147 last,
148 runner: Some(runner),
149 recording: Some(recording),
150 path: Vec::new(),
151 outside,
152 trying: None,
153 }
154 }
155
156 fn record(&self, read: Read) {
158 if let Some(recording) = self.recording {
159 let read = (self.path.iter().rev()).fold(read, |read, instance| Read::Inside {
160 instance: instance.clone(),
161 read: Box::new(read),
162 });
163 recording.record(read);
164 }
165 }
166
167 pub(crate) fn part(&self) -> &'a Part {
169 self.part
170 }
171
172 pub(crate) fn position(&self) -> usize {
174 self.before
175 }
176
177 pub fn seen(&self) -> Seen {
182 Seen {
183 versions: self.part.versions(self.before, self.own),
184 inputs: self.part.inputs.clone(),
185 state: self.part.state.clone(),
186 }
187 }
188
189 pub fn recorded<T>(&self, read: impl FnOnce(&Reader) -> T) -> (T, Reads) {
194 let recording = Recording::default();
195 let reader = Reader {
196 recording: Some(&recording),
197 path: Vec::new(),
198 ..self.clone()
199 };
200 let made = read(&reader);
201 (made, Reads(recording.take()))
202 }
203
204 pub fn reads_same(&self, reads: &Reads) -> bool {
207 (reads.0.iter()).all(|read| {
208 (self.part).holds(
209 read,
210 self.before,
211 self.own,
212 self.runner,
213 self.outside_reader(),
214 )
215 })
216 }
217
218 pub(crate) fn own(&self) -> Option<&'a str> {
220 self.own
221 }
222
223 pub(crate) fn for_make_of(mut self, own: &'a str) -> Self {
226 self.own = Some(own);
227 self
228 }
229
230 pub(crate) fn last(&self) -> Option<&'a Node> {
232 self.last
233 }
234
235 pub(crate) fn outside_reader(&self) -> Option<&Reader<'a>> {
237 self.outside.as_deref()
238 }
239
240 pub fn placed_in(mut self, outside: Reader<'a>) -> Self {
245 self.outside = Some(Rc::new(outside));
246 self
247 }
248
249 pub(crate) fn runner(&self) -> GeopResult<&'a Runner> {
252 self.runner
253 .ok_or_else(|| GeopError::new("only a make reads files and places parts"))
254 }
255
256 pub(crate) fn record_raw(&self, read: Read) {
259 self.record(read);
260 }
261
262 pub fn revision(&self) -> u64 {
265 self.part.revision()
266 }
267
268 fn node(&self, name: &str) -> GeopResult<&'a Node> {
270 match self.part.visible(name, self.before, self.own) {
271 Some((_, node)) => {
272 self.record(Read::Target {
273 name: name.to_string(),
274 producer: node.step.clone(),
275 version: node.version,
276 });
277 Ok(node)
278 }
279 None => {
280 self.record(Read::Absent(name.to_string()));
281 let seen: Vec<String> = (self.part.versions(self.before, self.own).into_iter())
282 .map(|(seen, _)| format!("`{seen}`"))
283 .collect();
284 let seen = match seen.is_empty() {
285 true => "none".to_string(),
286 false => seen.join(", "),
287 };
288 let suppressed = self.part.suppressed_before(self.before);
289 let suppressed = match suppressed.is_empty() {
290 true => String::new(),
291 false => format!("; suppressed before here: {}", quoted(&suppressed)),
292 };
293 Err(GeopError::new(format!(
294 "no step before here produces target `{name}` (the targets before here: \
295 {seen}{suppressed})"
296 )))
297 }
298 }
299 }
300
301 pub(crate) fn target<T: 'static>(&self, reference: &TargetReference<T>) -> GeopResult<&'a T> {
303 let name = reference.name();
304 self.node(name)?.get(name)
305 }
306
307 pub fn previous<T: Clone + Default + 'static>(&self) -> GeopResult<T> {
311 let own = self
312 .own
313 .filter(|_| self.path.is_empty())
314 .ok_or_else(|| GeopError::new("only a target being made has a previous version"))?;
315 if self.part.visible(own, self.before, self.own).is_none() {
316 self.record(Read::Absent(own.to_string()));
317 return Ok(T::default());
318 }
319 self.target(&TargetReference::<T>::new(own)).cloned()
320 }
321
322 pub fn retrieve_element<T: HasElements<K>, K: Kind>(
326 &self,
327 reference: &ElementReference<T, K>,
328 ) -> GeopResult<T::Element<'a>> {
329 let placed = self.retrieve_placed(reference)?;
330 match placed.instance.is_empty() {
331 true => Ok(placed.element),
332 false => Err(GeopError::new(format!(
333 "{reference:?} is of the placed part `{}`: this takes elements of the part itself",
334 placed.instance
335 ))),
336 }
337 }
338
339 pub fn retrieve_placed<T: HasElements<K>, K: Kind>(
344 &self,
345 reference: &ElementReference<T, K>,
346 ) -> GeopResult<InInstance<T::Element<'a>>> {
347 let (path, target) = (reference.instance(), reference.target().name());
348 let resolve = |reader: &Reader<'a>| {
349 let value = match (
350 reader.part.visible(target, reader.before, reader.own),
351 T::undefined(),
352 ) {
353 (None, Some(undefined)) => {
354 reader.record(Read::Absent(target.to_string()));
355 undefined
356 }
357 _ => reader.target(&TargetReference::<T>::new(target))?,
358 };
359 value.element(reference.name())
360 };
361 let element = match path.is_empty() {
362 true => resolve(self),
363 false => self.placed_part(path).and_then(|inside| resolve(&inside)),
364 };
365 let element = element.map_err(|e| e.with_context(format!("resolving {reference:?}")))?;
366 Ok(InInstance {
367 element,
368 instance: path.to_string(),
369 })
370 }
371
372 pub fn placed_part(&self, path: &str) -> GeopResult<Reader<'a>> {
378 let ctx = |e: GeopError| e.with_context(format!("the placed part {path:?}"));
379 let mut level = self.clone();
380 for name in path.split(INSTANCE_SEPARATOR) {
381 if name == OUTSIDE {
382 level = level.outside().map_err(ctx)?.0;
383 continue;
384 }
385 let inner = level.part.placed_at(name, level.before, level.own);
386 let inner = match (inner, level.path.last()) {
387 (Ok(inner), _) => inner,
388 (Err(e), Some(last)) if last == OUTSIDE => {
389 let me = (level.own.and_then(|own| own.strip_prefix(PLACED))).unwrap_or("");
390 return Err(ctx(e.with_context(format!(
391 "`{me}` reads `{name}` placed beside it in context: a part reads only \
392 the parts placed before it, so that no two parts read each other, \
393 and `{name}` is placed after `{me}`, or not at all"
394 ))));
395 }
396 (Err(e), _) => return Err(ctx(e)),
397 };
398 let mut inside = Reader::new(inner, inner.ran());
399 inside.runner = level.runner;
400 inside.recording = level.recording;
401 inside.path = level.path.clone();
402 inside.path.push(name.to_string());
403 inside.trying = (level.trying.as_ref())
404 .map(|(state, at)| (*state, crate::placed::instance_path(at, name)));
405 level = inside;
406 }
407 Ok(level)
408 }
409
410 pub fn outside(&self) -> GeopResult<(Reader<'a>, &'a str)> {
415 let outside = self.outside.as_deref().ok_or_else(|| {
416 GeopError::new(format!(
417 "`{OUTSIDE}` is the part this one is placed in, read in context: this one is \
418 built on its own, placed in none — open the part that places it"
419 ))
420 })?;
421 let me = (outside.own.and_then(|own| own.strip_prefix(PLACED)))
422 .ok_or_else(|| GeopError::new("the part this one is placed in places no part here"))?;
423 let mut level = outside.clone();
424 level.recording = self.recording;
425 level.runner = self.runner.or(outside.runner);
426 level.path = self.path.clone();
427 level.path.push(OUTSIDE.to_string());
428 Ok((level, me))
429 }
430
431 pub fn instances(&self) -> Vec<&'a str> {
434 (self.listed::<PlacedPart>().into_iter())
435 .filter(|(_, node)| node.value.is_ok())
436 .filter_map(|(name, _)| name.strip_prefix(PLACED))
437 .collect()
438 }
439
440 pub fn retrieve_input(&self, name: &str) -> Option<Setting> {
444 if let Some((instance, inside)) = outermost_instance(name) {
445 return self.placed_part(instance).ok()?.retrieve_input(inside);
446 }
447 let value = self.part.inputs.get(name).cloned();
448 self.record(Read::Input(name.to_string(), value.clone()));
449 value
450 }
451
452 pub fn retrieve_state(&self, name: &str) -> StateValue {
454 self.given_state(name).unwrap_or_default()
455 }
456
457 pub fn given_state(&self, name: &str) -> Option<StateValue> {
463 let value = self.own_state(name);
464 self.record(Read::State(name.to_string(), value.clone()));
465 match (value, outermost_instance(name)) {
466 (Some(value), _) => Some(value),
467 (None, None) => None,
468 (None, Some((instance, inside))) => {
469 self.placed_part(instance).ok()?.given_state(inside)
470 }
471 }
472 }
473
474 fn own_state(&self, name: &str) -> Option<StateValue> {
478 let tried = (self.trying.as_ref()).and_then(|(state, at)| match at.is_empty() {
479 true => state.get(name),
480 false => state.get(&key_behind(at, name)),
481 });
482 tried.or_else(|| self.part.state.get(name)).cloned()
483 }
484
485 pub fn reads_same_state(&self, reads: &Reads) -> bool {
491 (reads.0.iter()).all(|read| self.state_holds(read))
492 }
493
494 fn state_holds(&self, read: &Read) -> bool {
496 match read {
497 Read::State(name, value) => self.own_state(name) == *value,
498 Read::Inside { instance, read } => {
499 (self.placed_part(instance)).is_ok_and(|inside| inside.state_holds(read))
500 }
501 _ => true,
502 }
503 }
504
505 pub fn has_read_state(&self, name: &str) -> bool {
508 (self.recording).is_some_and(|recording| {
509 let read = Read::State(name.to_string(), None);
510 let key = (self.path.iter().rev())
511 .fold(read.key(), |key, instance| key_behind(instance, &key));
512 recording.has(&key)
513 })
514 }
515
516 pub fn targets_reading_state(&self) -> Vec<(Located, String)> {
520 let Some(recording) = self.recording else {
521 return Vec::new();
522 };
523 let reads = recording.reads.borrow();
524 (reads.iter())
525 .filter(|read| !matches!(innermost(read), Read::State(..)))
526 .filter_map(|read| {
527 let state = self.part.state_read(read, self.before, self.own)?;
528 Some((target_of(read), state))
529 })
530 .collect()
531 }
532
533 pub fn defines<T>(&self, reference: &TargetReference<T>) -> bool {
537 self.node(reference.name()).is_ok()
538 }
539
540 pub fn targets_of<T: 'static>(&self) -> Vec<TargetReference<T>> {
544 (self.listed::<T>().into_iter())
545 .filter(|(_, node)| node.value.is_ok())
546 .map(|(name, _)| TargetReference::new(name))
547 .collect()
548 }
549
550 pub fn failed_targets_of<T: 'static>(&self) -> Vec<GeopError> {
554 (self.listed::<T>().into_iter())
555 .filter_map(|(name, node)| {
556 self.record(Read::Target {
557 name: name.to_string(),
558 producer: node.step.clone(),
559 version: node.version,
560 });
561 node.failure(name)
562 })
563 .collect()
564 }
565
566 fn listed<T: 'static>(&self) -> Vec<(&'a str, &'a Node)> {
568 let listed = self.part.listed(TypeId::of::<T>(), self.before, self.own);
569 self.record(Read::Listed {
570 kind: std::any::type_name::<T>().into(),
571 targets: (listed.iter())
572 .map(|(name, node)| (name.to_string(), node.step.clone()))
573 .collect(),
574 });
575 listed
576 }
577
578 pub fn read_file(&self, name: &str) -> GeopResult<(String, Rc<str>)> {
581 let runner = self.runner()?;
582 let (path, text) = runner.read(name)?;
583 self.record(Read::File(path.clone(), runner.revision(&path)));
584 Ok((path, text))
585 }
586
587 pub fn read_bytes(&self, name: &str) -> GeopResult<(String, Rc<[u8]>)> {
590 let runner = self.runner()?;
591 let (path, bytes) = runner.read_bytes(name)?;
592 self.record(Read::File(path.clone(), runner.revision(&path)));
593 Ok((path, bytes))
594 }
595
596 pub fn read_file_as<T: Serialize + DeserializeOwned + 'static>(
601 &self,
602 name: &str,
603 kind: &'static str,
604 make: impl FnOnce(&str) -> GeopResult<T>,
605 ) -> GeopResult<(String, Rc<T>)> {
606 let (path, text) = self.read_file(name)?;
607 let cache = (self.runner()?.workspace.as_deref()).and_then(|workspace| workspace.cache());
608 let value = match cache {
609 Some(cache) => cache.made_of(kind, &path, &text, make)?,
610 None => Rc::new(make(&text)?),
611 };
612 Ok((path, value))
613 }
614
615 pub fn program(&self, name: &str) -> GeopResult<(Rc<Program>, Location)> {
619 let runner = self.runner()?;
620 let (program, location) = runner.program(name)?;
621 self.record(Read::File(
622 location.file.clone(),
623 runner.revision(&location.file),
624 ));
625 Ok((program, location))
626 }
627}
628
629#[derive(Clone, Default)]
631pub struct Reads(pub(crate) Vec<Read>);
632
633#[derive(Clone, Debug, PartialEq)]
638pub struct Seen {
639 pub versions: Vec<(String, u64)>,
640 pub inputs: Settings,
641 pub state: StateValues,
642}
643
644fn innermost(read: &Read) -> &Read {
646 match read {
647 Read::Inside { read, .. } => innermost(read),
648 read => read,
649 }
650}
651
652fn target_of(read: &Read) -> Located {
655 match read {
656 Read::Target { name, .. } | Read::Absent(name) | Read::Input(name, _) => Located::new(name),
657 Read::State(name, _) | Read::File(name, _) => Located::new(name),
658 Read::Listed { .. } => Located::default(),
659 Read::Inside { instance, read } => target_of(read).behind(instance),
660 }
661}
662
663impl Recording {
664 fn has(&self, key: &str) -> bool {
665 self.keys.borrow().contains(key)
666 }
667}