Skip to repository content237 lines · 7.4 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T02:32:04.796Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
job_debug_queue.rs
1use std::{collections::VecDeque, time::Instant};
2
3use gpui::SharedString;
4
5use super::JobId;
6
7pub struct GitJobDebugQueue {
8 pending: VecDeque<PendingJob>,
9 running: VecDeque<RunningJob>,
10 completed: VecDeque<CompletedJob>,
11}
12
13const MAX_COMPLETED_JOBS: usize = 500;
14
15#[derive(Clone, Debug)]
16pub struct PendingJob {
17 pub id: JobId,
18 pub description: SharedString,
19 pub key: Option<SharedString>,
20 pub enqueued_at: Instant,
21}
22
23#[derive(Clone, Debug)]
24pub struct RunningJob {
25 pub id: JobId,
26 pub description: SharedString,
27 pub key: Option<SharedString>,
28 pub enqueued_at: Instant,
29 pub started_at: Instant,
30}
31
32#[derive(Clone, Debug)]
33pub struct CompletedJob {
34 pub id: JobId,
35 pub description: SharedString,
36 pub key: Option<SharedString>,
37 pub enqueued_at: Instant,
38 pub started_at: Option<Instant>,
39 pub completed_at: Instant,
40 pub status: CompletedJobStatus,
41}
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub enum CompletedJobStatus {
45 Finished,
46 Skipped,
47}
48
49impl GitJobDebugQueue {
50 pub fn new() -> Self {
51 Self {
52 pending: VecDeque::new(),
53 running: VecDeque::new(),
54 completed: VecDeque::new(),
55 }
56 }
57
58 pub fn add(&mut self, id: JobId, description: &'static str, key: Option<SharedString>) {
59 self.pending.push_back(PendingJob {
60 id,
61 description: description.into(),
62 key,
63 enqueued_at: Instant::now(),
64 });
65 }
66
67 pub fn mark_running(&mut self, id: JobId) {
68 let Some(index) = self.pending.iter().position(|job| job.id == id) else {
69 return;
70 };
71 // Safe to unwrap: `index` was just found by `position()`, so it's in bounds.
72 let pending = self.pending.remove(index).unwrap();
73
74 self.running.push_back(RunningJob {
75 id: pending.id,
76 description: pending.description,
77 key: pending.key,
78 enqueued_at: pending.enqueued_at,
79 started_at: Instant::now(),
80 });
81 }
82
83 pub fn mark_unfinished_complete(&mut self, status: CompletedJobStatus) {
84 let ids = self
85 .pending
86 .iter()
87 .map(|job| job.id)
88 .chain(self.running.iter().map(|job| job.id))
89 .collect::<Vec<_>>();
90 for id in ids {
91 self.mark_complete(id, status);
92 }
93 }
94
95 pub fn mark_complete(&mut self, id: JobId, status: CompletedJobStatus) {
96 let (enqueued_at, started_at, description, key) =
97 if let Some(index) = self.running.iter().position(|job| job.id == id) {
98 let running = self.running.remove(index).unwrap();
99 (
100 running.enqueued_at,
101 Some(running.started_at),
102 running.description,
103 running.key,
104 )
105 } else if let Some(index) = self.pending.iter().position(|job| job.id == id) {
106 let pending = self.pending.remove(index).unwrap();
107 (pending.enqueued_at, None, pending.description, pending.key)
108 } else {
109 return;
110 };
111
112 self.completed.push_back(CompletedJob {
113 id,
114 description,
115 key,
116 enqueued_at,
117 started_at,
118 completed_at: Instant::now(),
119 status,
120 });
121
122 while self.completed.len() > MAX_COMPLETED_JOBS {
123 self.completed.pop_front();
124 }
125 }
126
127 pub fn to_debug_string(&self) -> String {
128 serde_json::to_string_pretty(&self.to_debug_value()).unwrap_or_default()
129 }
130
131 pub fn to_debug_value(&self) -> serde_json::Value {
132 let mut entries = Vec::new();
133
134 let mut pending_count = 0u64;
135 let mut running_count = 0u64;
136 let mut finished_count = 0u64;
137 let mut skipped_count = 0u64;
138
139 for job in &self.pending {
140 pending_count += 1;
141 entries.push((job.enqueued_at, self.format_pending(job)));
142 }
143 for job in &self.running {
144 running_count += 1;
145 entries.push((job.enqueued_at, self.format_running(job)));
146 }
147 for job in &self.completed {
148 match job.status {
149 CompletedJobStatus::Finished => finished_count += 1,
150 CompletedJobStatus::Skipped => skipped_count += 1,
151 }
152 entries.push((job.enqueued_at, self.format_completed(job)));
153 }
154
155 entries.sort_by_key(|(enqueued_at, _)| *enqueued_at);
156
157 let json_entries: Vec<serde_json::Value> =
158 entries.into_iter().map(|(_, json)| json).collect();
159
160 serde_json::json!({
161 "summary": {
162 "pending": pending_count,
163 "running": running_count,
164 "finished": finished_count,
165 "skipped": skipped_count,
166 },
167 "entries": json_entries,
168 })
169 }
170
171 fn format_pending(&self, job: &PendingJob) -> serde_json::Value {
172 serde_json::json!({
173 "id": job.id,
174 "description": job.description.as_ref(),
175 "key": job.key.as_ref().map(|k| k.as_ref()),
176 "status": "Pending",
177 "enqueued": format!("{} ago", format_duration(job.enqueued_at.elapsed())),
178 })
179 }
180
181 fn format_running(&self, job: &RunningJob) -> serde_json::Value {
182 serde_json::json!({
183 "id": job.id,
184 "description": job.description.as_ref(),
185 "key": job.key.as_ref().map(|k| k.as_ref()),
186 "status": "Running",
187 "enqueued": format!("{} ago", format_duration(job.enqueued_at.elapsed())),
188 "wait_time": format_duration(job.started_at.duration_since(job.enqueued_at)),
189 "run_time": format!("{} (still running)", format_duration(job.started_at.elapsed())),
190 })
191 }
192
193 fn format_completed(&self, job: &CompletedJob) -> serde_json::Value {
194 let status = match job.status {
195 CompletedJobStatus::Finished => "Finished",
196 CompletedJobStatus::Skipped => "Skipped",
197 };
198
199 let (wait_time, run_time) = if let Some(started) = job.started_at {
200 let wait = format_duration(started.duration_since(job.enqueued_at));
201 let run = format_duration(job.completed_at.duration_since(started));
202 (wait, Some(run))
203 } else {
204 let wait = format!(
205 "{} (skipped)",
206 format_duration(job.completed_at.duration_since(job.enqueued_at))
207 );
208 (wait, None)
209 };
210
211 serde_json::json!({
212 "id": job.id,
213 "description": job.description.as_ref(),
214 "key": job.key.as_ref().map(|k| k.as_ref()),
215 "status": status,
216 "enqueued": format!("{} ago", format_duration(job.enqueued_at.elapsed())),
217 "wait_time": wait_time,
218 "run_time": run_time,
219 })
220 }
221}
222
223fn format_duration(duration: std::time::Duration) -> String {
224 let secs = duration.as_secs_f64();
225 if secs < 0.001 {
226 format!("{:.0}us", secs * 1_000_000.0)
227 } else if secs < 1.0 {
228 format!("{:.0}ms", secs * 1000.0)
229 } else if secs < 60.0 {
230 format!("{:.0}s", secs)
231 } else if secs < 3600.0 {
232 format!("{:.0}m", secs / 60.0)
233 } else {
234 format!("{:.0}h", secs / 3600.0)
235 }
236}
237