runnerd_core/api/
slots.rs1use super::{Activity, ProfileStatus, RunnerStatus, SlotStatus, Status, Tone};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum SlotState {
9 Idle,
11 Reseeding,
13 Starting {
15 repository: String,
17 },
18 Warm {
20 repository: String,
22 },
23 Running {
25 repository: String,
27 },
28}
29
30impl SlotState {
31 pub fn of_runner(runner: &RunnerStatus) -> SlotState {
33 let repository = runner.repository.clone();
34 if runner.busy {
35 SlotState::Running { repository }
36 } else if runner.warm {
37 SlotState::Warm { repository }
38 } else {
39 SlotState::Starting { repository }
40 }
41 }
42
43 pub fn word(&self) -> &'static str {
45 match self {
46 SlotState::Idle => "idle",
47 SlotState::Reseeding => "reseeding",
48 SlotState::Starting { .. } => "starting",
49 SlotState::Warm { .. } => "warm",
50 SlotState::Running { .. } => "running",
51 }
52 }
53
54 pub fn line(&self) -> String {
57 match self {
58 SlotState::Idle => "idle".to_owned(),
59 SlotState::Reseeding => "reseeding the snapshot".to_owned(),
60 SlotState::Starting { repository } => {
61 format!("a runner for {repository} waiting for its job")
62 }
63 SlotState::Warm { repository } => {
64 format!("a warm runner for {repository}, waiting for a job")
65 }
66 SlotState::Running { repository } => format!("running a job of {repository}"),
67 }
68 }
69
70 pub fn is_running(&self) -> bool {
72 matches!(self, SlotState::Running { .. })
73 }
74
75 pub fn tone(&self) -> Tone {
77 match self {
78 SlotState::Running { .. } => Tone::Neutral,
79 _ => Tone::Quiet,
80 }
81 }
82}
83
84impl Status {
85 pub fn slot_state(profile: &ProfileStatus, slot: &SlotStatus) -> SlotState {
88 match &slot.runner {
89 Some(runner) => SlotState::of_runner(runner),
90 None if profile.reseeding && slot.slot == 1 => SlotState::Reseeding,
91 None => SlotState::Idle,
92 }
93 }
94
95 pub fn health(&self, now: u64) -> Health {
97 let mut health = Health {
98 slots: 0,
99 running: 0,
100 starting: 0,
101 warm: 0,
102 waiting: self.queued.len(),
103 oldest_wait: self
104 .queued
105 .iter()
106 .filter(|job| job.queued_at > 0)
107 .map(|job| now.saturating_sub(job.queued_at))
108 .max(),
109 };
110 for profile in &self.profiles {
111 for slot in &profile.slots {
112 health.slots += 1;
113 match Status::slot_state(profile, slot) {
114 SlotState::Running { .. } => health.running += 1,
115 SlotState::Starting { .. } => health.starting += 1,
116 SlotState::Warm { .. } => health.warm += 1,
117 SlotState::Idle | SlotState::Reseeding => {}
118 }
119 }
120 }
121 health
122 }
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127pub struct Health {
128 pub slots: usize,
130 pub running: usize,
132 pub starting: usize,
134 pub warm: usize,
136 pub waiting: usize,
138 pub oldest_wait: Option<u64>,
140}
141
142impl Health {
143 pub fn waiting_tone(&self) -> Tone {
145 if self.waiting > 0 {
146 Tone::Warn
147 } else {
148 Tone::Neutral
149 }
150 }
151}
152
153impl Activity {
154 pub fn failed_tone(&self) -> Tone {
157 if self.runner_failed > 0 {
158 Tone::Bad
159 } else {
160 Tone::Neutral
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests;