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