1use std::{
9 cell::RefCell,
10 collections::{BTreeMap, BTreeSet, HashMap, HashSet},
11};
12
13use super::Saved;
14use crate::{
15 config::ProfileKind,
16 graph::EvalStatus,
17 store::RequestView,
18 types::{ArtifactName, EvalId, RequestStatus, RunStatus},
19};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum Source {
24 Executed,
25 Provisional,
26 Reused,
27 Derived,
28 Saved,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
34pub enum Queue {
35 Ready,
37 Jobs,
39 Slot(crate::config::Backend),
41 Joined,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub enum Activity {
47 Running,
48 Human(Option<crate::types::ReviewerId>),
50 Queued(Queue),
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct Waits {
57 pub artifacts: Vec<crate::types::ArtifactName>,
58 pub root: Option<(crate::types::ArtifactName, crate::types::ArtifactName)>,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub enum NotRun {
63 Dependency(Waits),
65 Budget,
66 Stale,
67 Unreviewed,
68 Absent,
70}
71
72#[derive(Debug, Clone, PartialEq, Eq)]
73pub enum EvalView {
74 Done(Source),
75 Failed {
77 verdict: bool,
78 },
79 InProgress(Activity),
80 WaitingOn(Waits),
82 BlockedBy(Vec<crate::types::ArtifactName>),
84 NotRun(NotRun),
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
89pub enum Rank {
90 Failed,
91 InProgress,
92 Blocked,
93 Waiting,
94 NotRun,
95 Done,
96}
97
98impl EvalView {
99 pub fn rank(&self) -> Rank {
100 match self {
101 Self::Failed { .. } => Rank::Failed,
102 Self::InProgress(_) => Rank::InProgress,
103 Self::BlockedBy(_) => Rank::Blocked,
104 Self::WaitingOn(_) => Rank::Waiting,
105 Self::NotRun(_) => Rank::NotRun,
106 Self::Done(_) => Rank::Done,
107 }
108 }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
113pub enum Busy {
114 Running,
115 Human,
116 Queued,
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub enum Completion {
122 Complete,
124 InProgress(Busy),
125 Waiting {
127 blocked: bool,
128 },
129 Failed {
131 verdict: bool,
132 },
133}
134
135impl Completion {
136 fn rank(self) -> Rank {
138 match self {
139 Self::Failed { .. } => Rank::Failed,
140 Self::InProgress(_) => Rank::InProgress,
141 Self::Waiting { .. } => Rank::Waiting,
142 Self::Complete => Rank::Done,
143 }
144 }
145}
146
147#[derive(Debug, Clone, PartialEq, Eq)]
149pub struct Upstream {
150 pub artifact: crate::types::ArtifactName,
151 pub completion: Completion,
152}
153
154pub(super) struct Index<'a> {
157 pub order: Vec<&'a ArtifactName>,
158 position: HashMap<&'a ArtifactName, usize>,
159 requests: HashMap<&'a EvalId, &'a RequestView>,
160 pub evals: BTreeMap<&'a ArtifactName, Vec<&'a EvalId>>,
161 pub upstream: BTreeMap<&'a EvalId, Vec<&'a ArtifactName>>,
163 pub dependency: BTreeSet<&'a EvalId>,
164 pub peers: BTreeMap<&'a ArtifactName, Vec<&'a ArtifactName>>,
166 pub cyclic: BTreeSet<&'a ArtifactName>,
167}
168
169type Component = crate::store::definitions::Component;
170
171fn members(component: &Component) -> Vec<&ArtifactName> {
172 component.artifacts.value().into_iter().flatten().collect()
173}
174
175impl<'a> Index<'a> {
176 pub fn new(saved: &Saved<'a>) -> Self {
177 let order = saved.artifact_ids();
178 let position: HashMap<_, _> = order
179 .iter()
180 .enumerate()
181 .map(|(index, id)| (*id, index))
182 .collect();
183 let mut requests = HashMap::new();
184 for view in saved.requests {
185 requests.entry(&view.request.eval_id).or_insert(view);
186 }
187 let definitions: HashMap<_, _> = saved
188 .eval_definitions()
189 .iter()
190 .map(|eval| (&eval.id, eval))
191 .collect();
192 let mut targets: HashMap<&ArtifactName, Vec<&EvalId>> = HashMap::new();
194 for eval in saved.eval_definitions() {
195 targets.entry(&eval.target).or_default().push(&eval.id);
196 }
197 for view in saved.requests {
198 let request = &view.request;
199 if !definitions.contains_key(&request.eval_id) {
200 let evals = targets.entry(&request.target).or_default();
201 if !evals.contains(&&request.eval_id) {
202 evals.push(&request.eval_id);
203 }
204 }
205 }
206 let mut dependency = BTreeSet::new();
207 let mut evals = BTreeMap::new();
208 for &artifact in &order {
209 let mut ids: Vec<(bool, &EvalId)> = targets
210 .remove(artifact)
211 .unwrap_or_default()
212 .into_iter()
213 .map(|eval| {
214 let kind = definitions
215 .get(eval)
216 .and_then(|eval| eval.declaration.value())
217 .and_then(|declaration| declaration.profile.value())
218 .map(|profile| profile.known.kind())
219 .or_else(|| requests.get(eval).map(|view| view.request.profile.kind()));
220 (kind == Some(ProfileKind::Dependency), eval)
221 })
222 .collect();
223 ids.sort();
225 dependency.extend(
226 ids.iter()
227 .filter(|(derived, _)| *derived)
228 .map(|(_, id)| *id),
229 );
230 evals.insert(artifact, ids.into_iter().map(|(_, id)| id).collect());
231 }
232 let components = saved.components();
233 let by_id: HashMap<crate::graph::ComponentId, &Component> = components
234 .iter()
235 .enumerate()
236 .map(|(index, component)| {
237 let id = component
238 .id
239 .value()
240 .copied()
241 .unwrap_or_else(|| crate::graph::ComponentId::from_position(index));
242 (id, component)
243 })
244 .collect();
245 let mut component_of: HashMap<&ArtifactName, &Component> = HashMap::new();
246 let mut peers = BTreeMap::new();
247 let mut cyclic = BTreeSet::new();
248 for component in components {
249 let mut names = members(component);
250 names.sort_unstable();
251 for &artifact in &names {
252 component_of.entry(artifact).or_insert(component);
253 peers.insert(
254 artifact,
255 names.iter().copied().filter(|id| *id != artifact).collect(),
256 );
257 if component.cyclic.value() == Some(&true) {
258 cyclic.insert(artifact);
259 }
260 }
261 }
262 let mut upstream = BTreeMap::new();
263 for (&artifact, ids) in &evals {
264 let component = component_of.get(artifact).copied();
265 let own = component.map(members).unwrap_or_else(|| vec![artifact]);
266 let gates: Option<Vec<&ArtifactName>> = component.map(|component| {
267 match component.dependencies.value() {
268 Some(dependencies) => dependencies
269 .iter()
270 .filter_map(|id| by_id.get(id))
271 .flat_map(|component| members(component))
272 .collect(),
273 None => saved
275 .definitions()
276 .into_iter()
277 .flat_map(|graph| graph.relations())
278 .filter(|relation| {
279 own.contains(&&relation.target) && !own.contains(&&relation.source)
280 })
281 .flat_map(|relation| {
282 component_of
283 .get(&relation.source)
284 .map(|component| members(component))
285 .unwrap_or_else(|| vec![&relation.source])
286 })
287 .collect(),
288 }
289 });
290 for &eval in ids {
291 let request = requests.get(eval).map(|view| &view.request);
292 let deps: Vec<&ArtifactName> = if dependency.contains(eval) {
293 let definition = definitions
294 .get(eval)
295 .and_then(|definition| definition.deps.value());
296 match (definition, request) {
297 (Some(deps), _) => deps.iter().collect(),
298 (None, Some(request)) => request.deps.iter().collect(),
299 (None, None) => Vec::new(),
300 }
301 } else {
302 match (&gates, request) {
303 (Some(gates), _) => gates.clone(),
304 (None, Some(request)) => request.deps.iter().collect(),
306 (None, None) => Vec::new(),
307 }
308 };
309 let mut deps: Vec<&ArtifactName> = deps
310 .into_iter()
311 .filter(|id| *id != artifact)
312 .filter(|id| dependency.contains(eval) || !own.contains(id))
313 .collect::<BTreeSet<_>>()
314 .into_iter()
315 .collect();
316 deps.sort_by_key(|id| position.get(id).copied().unwrap_or(usize::MAX));
317 upstream.insert(eval, deps);
318 }
319 }
320 Self {
321 order,
322 position,
323 requests,
324 evals,
325 upstream,
326 dependency,
327 peers,
328 cyclic,
329 }
330 }
331}
332
333pub(super) struct States<'a> {
335 run: &'a crate::store::Run,
336 #[cfg(any(test, feature = "test-support"))]
337 pub derivations: std::cell::Cell<usize>,
338 pub index: Index<'a>,
339 pub running: bool,
340 views: RefCell<HashMap<&'a EvalId, Option<EvalView>>>,
341 effective: RefCell<HashMap<&'a EvalId, Option<EvalStatus>>>,
342 completions: RefCell<HashMap<&'a ArtifactName, Completion>>,
343 waits: RefCell<HashMap<&'a ArtifactName, Vec<ArtifactName>>>,
344}
345
346impl<'a> States<'a> {
347 pub fn new(saved: &Saved<'a>) -> Self {
350 let run = &saved.run.run;
351 let states = Self {
352 run,
353 #[cfg(any(test, feature = "test-support"))]
354 derivations: std::cell::Cell::new(0),
355 index: Index::new(saved),
356 running: run.status() == RunStatus::Running,
357 views: RefCell::new(HashMap::new()),
358 effective: RefCell::new(HashMap::new()),
359 completions: RefCell::new(HashMap::new()),
360 waits: RefCell::new(HashMap::new()),
361 };
362 for artifact in states.upstream_first() {
363 for &eval in states.evals(artifact) {
364 states.effective(eval);
365 }
366 states.completion(artifact);
367 states.waits_of(artifact);
368 }
369 states
370 }
371
372 fn upstream_first(&self) -> Vec<&'a ArtifactName> {
375 let mut done = HashSet::new();
376 let mut seen = HashSet::new();
377 let mut ordered = Vec::with_capacity(self.index.order.len());
378 for &start in &self.index.order {
379 if !seen.insert(start) {
380 continue;
381 }
382 let mut stack = vec![(start, self.dependencies(start), 0)];
383 while let Some((artifact, dependencies, next)) = stack.last_mut() {
384 if let Some(&dependency) = dependencies.get(*next) {
385 *next += 1;
386 if seen.insert(dependency) {
387 let dependencies = self.dependencies(dependency);
388 stack.push((dependency, dependencies, 0));
389 }
390 } else {
391 if done.insert(*artifact) {
392 ordered.push(*artifact);
393 }
394 stack.pop();
395 }
396 }
397 }
398 ordered
399 }
400
401 fn dependencies(&self, artifact: &ArtifactName) -> Vec<&'a ArtifactName> {
402 let mut dependencies: Vec<&'a ArtifactName> = Vec::new();
403 for eval in self.evals(artifact) {
404 for &upstream in self.upstream(eval) {
405 if !dependencies.contains(&upstream) {
406 dependencies.push(upstream);
407 }
408 }
409 }
410 dependencies
411 }
412
413 fn key(&self, artifact: &ArtifactName) -> Option<&'a ArtifactName> {
415 self.index
416 .position
417 .get_key_value(artifact)
418 .map(|(&id, _)| id)
419 }
420
421 pub fn request(&self, eval: &EvalId) -> Option<&'a RequestView> {
422 self.index.requests.get(eval).copied()
423 }
424
425 fn outside(&self, eval: &EvalId) -> Option<RequestStatus> {
428 if let Some(status) = self.run.evidence.get(eval) {
429 return Some(*status);
430 }
431 let saved = self.run.validation.snapshot()?.evals.value()?;
432 let status = *saved
433 .iter()
434 .find(|saved| &saved.id == eval)?
435 .status
436 .value()?;
437 matches!(
438 status,
439 RequestStatus::Green | RequestStatus::Red | RequestStatus::Error | RequestStatus::Stale
440 )
441 .then_some(status)
442 }
443
444 pub fn evals(&self, artifact: &ArtifactName) -> &[&'a EvalId] {
445 self.index.evals.get(artifact).map_or(&[], Vec::as_slice)
446 }
447
448 pub fn upstream(&self, eval: &EvalId) -> &[&'a ArtifactName] {
449 self.index.upstream.get(eval).map_or(&[], Vec::as_slice)
450 }
451
452 fn position(&self, artifact: &ArtifactName) -> usize {
453 self.index
454 .position
455 .get(artifact)
456 .copied()
457 .unwrap_or(usize::MAX)
458 }
459
460 pub fn view(&self, eval: &'a EvalId) -> EvalView {
461 if let Some(view) = self.views.borrow().get(eval) {
462 return view.clone().unwrap_or(EvalView::NotRun(NotRun::Unreviewed));
464 }
465 self.views.borrow_mut().insert(eval, None);
466 #[cfg(any(test, feature = "test-support"))]
467 self.derivations.set(self.derivations.get() + 1);
468 let view = self.derive(eval);
469 self.views.borrow_mut().insert(eval, Some(view.clone()));
470 view
471 }
472
473 pub fn effective(&self, eval: &'a EvalId) -> EvalStatus {
476 if let Some(status) = self.effective.borrow().get(eval) {
477 return status.unwrap_or(EvalStatus::Wait);
479 }
480 self.effective.borrow_mut().insert(eval, None);
481 #[cfg(any(test, feature = "test-support"))]
482 self.derivations.set(self.derivations.get() + 1);
483 let status = self.evaluate(eval);
484 self.effective.borrow_mut().insert(eval, Some(status));
485 status
486 }
487
488 fn evaluate(&self, eval: &'a EvalId) -> EvalStatus {
491 let request = self.request(eval).map(|view| &view.request);
492 let status = request
493 .map(|request| request.status())
494 .or_else(|| self.outside(eval));
495 let derived = self.index.dependency.contains(eval);
496 if derived && status == Some(RequestStatus::Error) {
497 return EvalStatus::Error;
498 }
499 let mut unmet = Vec::new();
500 {
501 for artifact in self.upstream(eval) {
502 for &gate in self.evals(artifact) {
503 let status = self.effective(gate);
504 if !status.passes_gate() {
505 unmet.push(status);
506 }
507 }
508 }
509 }
510 if unmet.iter().any(|status| {
511 matches!(status, EvalStatus::Red | EvalStatus::Blocked)
512 || (self.run.modes.speculate && *status == EvalStatus::Error)
513 }) {
514 return EvalStatus::Blocked;
515 }
516 if !unmet.is_empty() {
517 return EvalStatus::Wait;
518 }
519 if derived {
520 return EvalStatus::Green;
521 }
522 match status {
523 Some(RequestStatus::Deferred) => EvalStatus::Deferred,
524 Some(RequestStatus::Provisional) => EvalStatus::Provisional,
525 Some(RequestStatus::Green) => EvalStatus::Green,
526 Some(RequestStatus::Red) => EvalStatus::Red,
527 Some(RequestStatus::Error) => EvalStatus::Error,
528 Some(RequestStatus::Stale) => EvalStatus::Stale,
529 _ => EvalStatus::Unreviewed,
530 }
531 }
532
533 fn gate_met(&self, artifact: &ArtifactName) -> bool {
535 self.evals(artifact)
536 .iter()
537 .all(|eval| self.effective(eval).passes_gate())
538 }
539
540 fn met(&self, artifact: &ArtifactName) -> bool {
541 self.evals(artifact)
542 .iter()
543 .all(|eval| self.effective(eval) == EvalStatus::Green)
544 }
545
546 fn derive(&self, eval: &'a EvalId) -> EvalView {
547 let Some(view) = self.request(eval) else {
548 return match self.outside(eval) {
550 Some(RequestStatus::Green) => EvalView::Done(Source::Saved),
551 Some(RequestStatus::Red) => EvalView::Failed { verdict: true },
552 Some(RequestStatus::Error) => EvalView::Failed { verdict: false },
553 Some(RequestStatus::Stale) => EvalView::NotRun(NotRun::Stale),
554 _ => EvalView::NotRun(NotRun::Absent),
555 };
556 };
557 let request = &view.request;
558 let dependency = request.profile.kind() == ProfileKind::Dependency;
559 match request.status() {
560 RequestStatus::Provisional => EvalView::Done(Source::Provisional),
561 RequestStatus::Green => EvalView::Done(if dependency {
562 Source::Derived
563 } else if crate::query::reused(request) {
564 Source::Reused
565 } else {
566 Source::Executed
567 }),
568 RequestStatus::Red => EvalView::Failed { verdict: true },
569 RequestStatus::Error => EvalView::Failed { verdict: false },
570 RequestStatus::Running => EvalView::InProgress(Activity::Running),
571 RequestStatus::WaitingHuman | RequestStatus::Deferred => EvalView::InProgress(
572 Activity::Human(view.claim.as_ref().map(|claim| claim.reviewer.clone())),
573 ),
574 RequestStatus::BudgetExhausted => EvalView::NotRun(NotRun::Budget),
575 RequestStatus::Stale => EvalView::NotRun(NotRun::Stale),
576 RequestStatus::Queued
577 | RequestStatus::WaitDependency
578 | RequestStatus::Unreviewed
579 | RequestStatus::Blocked => {
580 let unmet = self.unmet(self.upstream(eval));
581 let blocking: Vec<_> = unmet
582 .iter()
583 .filter(|artifact| self.blocks(artifact))
584 .cloned()
585 .collect();
586 if !blocking.is_empty() {
587 return EvalView::BlockedBy(blocking);
588 }
589 if self.run.modes.speculate && !dependency {
590 return self.ready(view);
591 }
592 if !unmet.is_empty() {
593 let waits = Waits {
594 root: self.root(&unmet),
595 artifacts: unmet,
596 };
597 return if self.running {
598 EvalView::WaitingOn(waits)
599 } else {
600 EvalView::NotRun(NotRun::Dependency(waits))
601 };
602 }
603 if dependency {
604 return EvalView::Done(Source::Derived);
605 }
606 self.ready(view)
607 }
608 }
609 }
610
611 fn ready(&self, view: &RequestView) -> EvalView {
613 let request = &view.request;
614 if !self.running || request.status() != RequestStatus::Queued {
615 return EvalView::NotRun(NotRun::Unreviewed);
616 }
617 EvalView::InProgress(Activity::Queued(
618 if let Some(crate::store::QueueReason::Slot { backend }) = request.queue {
619 Queue::Slot(backend)
620 } else if request.execution_id.is_some() {
621 Queue::Joined
622 } else if self.run.jobs.is_some() {
623 Queue::Jobs
624 } else {
625 Queue::Ready
626 },
627 ))
628 }
629
630 pub fn unmet(&self, artifacts: &[&'a ArtifactName]) -> Vec<crate::types::ArtifactName> {
632 let mut unmet: Vec<_> = artifacts
633 .iter()
634 .filter(|artifact| !self.gate_met(artifact))
635 .map(|artifact| (*artifact, self.completion(artifact)))
636 .collect();
637 unmet.sort_by_key(|(artifact, completion)| (completion.rank(), self.position(artifact)));
638 unmet
639 .into_iter()
640 .map(|(artifact, _)| artifact.clone())
641 .collect()
642 }
643
644 fn views_of(&self, artifact: &ArtifactName) -> Vec<EvalView> {
645 self.evals(artifact)
646 .iter()
647 .map(|eval| self.view(eval))
648 .collect()
649 }
650
651 pub fn completion(&self, artifact: &ArtifactName) -> Completion {
654 if let Some(completion) = self.completions.borrow().get(artifact) {
655 return *completion;
656 }
657 let views = self.views_of(artifact);
658 let busy = views
659 .iter()
660 .filter_map(|view| match view {
661 EvalView::InProgress(Activity::Running) => Some(Busy::Running),
662 EvalView::InProgress(Activity::Human(_)) => Some(Busy::Human),
663 EvalView::InProgress(Activity::Queued(_)) => Some(Busy::Queued),
664 _ => None,
665 })
666 .min();
667 let completion = if views
668 .iter()
669 .any(|view| matches!(view, EvalView::Failed { .. }))
670 {
671 Completion::Failed {
673 verdict: !views.contains(&EvalView::Failed { verdict: false }),
674 }
675 } else if let Some(busy) = busy {
676 Completion::InProgress(busy)
677 } else if views.iter().any(|view| !matches!(view, EvalView::Done(_))) || !self.met(artifact)
678 {
679 Completion::Waiting {
680 blocked: self.blocks(artifact)
681 || views
682 .iter()
683 .any(|view| matches!(view, EvalView::BlockedBy(_))),
684 }
685 } else {
686 Completion::Complete
687 };
688 if let Some(id) = self.key(artifact) {
689 self.completions.borrow_mut().insert(id, completion);
690 }
691 completion
692 }
693
694 fn blocks(&self, artifact: &ArtifactName) -> bool {
696 self.evals(artifact).iter().any(|eval| {
697 matches!(self.effective(eval), EvalStatus::Red | EvalStatus::Blocked)
698 || (self.run.modes.speculate && self.effective(eval) == EvalStatus::Error)
699 })
700 }
701
702 fn held(&self, artifact: &ArtifactName, blocked: bool) -> Vec<crate::types::ArtifactName> {
704 let mut x: Vec<crate::types::ArtifactName> = Vec::new();
705 for &eval in self.evals(artifact) {
706 let wanted = if blocked {
707 EvalStatus::Blocked
708 } else {
709 EvalStatus::Wait
710 };
711 if matches!(self.view(eval), EvalView::Done(_)) && self.effective(eval) == wanted {
712 for id in self.unmet(self.upstream(eval)) {
713 if (!blocked || self.blocks(&id)) && !x.contains(&id) {
714 x.push(id);
715 }
716 }
717 }
718 }
719 x
720 }
721
722 pub fn waits_of(&self, artifact: &ArtifactName) -> Vec<crate::types::ArtifactName> {
724 if let Some(x) = self.waits.borrow().get(artifact) {
725 return x.clone();
726 }
727 let mut x = self.held(artifact, false);
728 for view in self.views_of(artifact) {
729 if let EvalView::WaitingOn(waits) | EvalView::NotRun(NotRun::Dependency(waits)) = view {
730 for id in waits.artifacts {
731 if !x.contains(&id) {
732 x.push(id);
733 }
734 }
735 }
736 }
737 x.sort_by_key(|id| (self.completion(id).rank(), self.position(id)));
738 if let Some(id) = self.key(artifact) {
739 self.waits.borrow_mut().insert(id, x.clone());
740 }
741 x
742 }
743
744 pub fn blockers_of(&self, artifact: &ArtifactName) -> Vec<crate::types::ArtifactName> {
746 let mut x = self.held(artifact, true);
747 for view in self.views_of(artifact) {
748 if let EvalView::BlockedBy(blockers) = view {
749 for id in blockers {
750 if !x.contains(&id) {
751 x.push(id);
752 }
753 }
754 }
755 }
756 x
757 }
758
759 fn root(
761 &self,
762 x: &[crate::types::ArtifactName],
763 ) -> Option<(crate::types::ArtifactName, crate::types::ArtifactName)> {
764 if x.iter()
765 .any(|id| !matches!(self.completion(id), Completion::Waiting { blocked: false }))
766 {
767 return None;
768 }
769 let via = x.first()?.clone();
770 let cause = self.waits_of(&via).into_iter().next()?;
771 Some((via, cause))
772 }
773
774 pub fn upstream_states(&self, eval: &EvalId) -> Vec<Upstream> {
776 let mut upstream: Vec<_> = self
777 .upstream(eval)
778 .iter()
779 .map(|artifact| Upstream {
780 artifact: (*artifact).clone(),
781 completion: self.completion(artifact),
782 })
783 .collect();
784 upstream.sort_by_key(|upstream| {
785 (
786 upstream.completion.rank(),
787 self.position(&upstream.artifact),
788 )
789 });
790 upstream
791 }
792}