1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
//! Command queue for managing async operations.
//!
//! Provides a queue for scheduling and managing background Git operations,
//! with priority levels and cancellation support.
use std::collections::VecDeque;
use std::time::Instant;
/// Priority levels for queued commands.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Priority {
/// Low priority (background refresh)
Low = 0,
/// Normal priority (user-initiated)
Normal = 1,
/// High priority (blocking operation)
High = 2,
}
/// State of a queued command.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CommandState {
/// Waiting to be executed
Pending,
/// Currently executing
Running,
/// Completed successfully
Completed,
/// Failed with error
Failed,
/// Cancelled by user
Cancelled,
}
/// A command in the queue.
#[derive(Debug, Clone)]
pub struct QueuedCommand {
/// Unique ID
pub id: u64,
/// Command name/description
pub name: String,
/// Priority level
pub priority: Priority,
/// Current state
pub state: CommandState,
/// When the command was queued
pub queued_at: Instant,
/// When execution started (if any)
pub started_at: Option<Instant>,
/// Error message (if failed)
pub error: Option<String>,
}
/// Command queue manager.
pub struct CommandQueue {
/// Pending commands (ordered by priority)
pending: VecDeque<QueuedCommand>,
/// Currently running command
running: Option<QueuedCommand>,
/// Completed/failed commands (recent history)
history: VecDeque<QueuedCommand>,
/// Max history size
max_history: usize,
/// Next command ID
next_id: u64,
}
impl CommandQueue {
/// Create a new command queue.
pub fn new(max_history: usize) -> Self {
Self {
pending: VecDeque::new(),
running: None,
history: VecDeque::with_capacity(max_history),
max_history,
next_id: 0,
}
}
/// Queue a new command.
pub fn enqueue(&mut self, name: impl Into<String>, priority: Priority) -> u64 {
let id = self.next_id;
self.next_id += 1;
let cmd = QueuedCommand {
id,
name: name.into(),
priority,
state: CommandState::Pending,
queued_at: Instant::now(),
started_at: None,
error: None,
};
// Insert by priority (higher priority first)
let pos = self.pending
.iter()
.position(|c| c.priority < priority)
.unwrap_or(self.pending.len());
self.pending.insert(pos, cmd);
id
}
/// Get next pending command (does not remove it).
pub fn peek(&self) -> Option<&QueuedCommand> {
self.pending.front()
}
/// Start executing the next pending command.
pub fn start_next(&mut self) -> Option<u64> {
if self.running.is_some() {
return None; // Already running
}
if let Some(mut cmd) = self.pending.pop_front() {
cmd.state = CommandState::Running;
cmd.started_at = Some(Instant::now());
let id = cmd.id;
self.running = Some(cmd);
Some(id)
} else {
None
}
}
/// Mark running command as completed.
pub fn complete(&mut self) {
if let Some(mut cmd) = self.running.take() {
cmd.state = CommandState::Completed;
self.add_to_history(cmd);
}
}
/// Mark running command as failed.
pub fn fail(&mut self, error: impl Into<String>) {
if let Some(mut cmd) = self.running.take() {
cmd.state = CommandState::Failed;
cmd.error = Some(error.into());
self.add_to_history(cmd);
}
}
/// Cancel a pending command by ID.
pub fn cancel(&mut self, id: u64) -> bool {
if let Some(pos) = self.pending.iter().position(|c| c.id == id) {
let mut cmd = self.pending.remove(pos).unwrap();
cmd.state = CommandState::Cancelled;
self.add_to_history(cmd);
true
} else {
false
}
}
/// Get pending count.
pub fn pending_count(&self) -> usize {
self.pending.len()
}
/// Check if any command is running.
pub fn is_running(&self) -> bool {
self.running.is_some()
}
/// Get running command.
pub fn running(&self) -> Option<&QueuedCommand> {
self.running.as_ref()
}
fn add_to_history(&mut self, cmd: QueuedCommand) {
if self.history.len() >= self.max_history {
self.history.pop_front();
}
self.history.push_back(cmd);
}
}
impl Default for CommandQueue {
fn default() -> Self {
Self::new(50)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_queue_priority() {
let mut queue = CommandQueue::new(10);
queue.enqueue("low", Priority::Low);
queue.enqueue("high", Priority::High);
queue.enqueue("normal", Priority::Normal);
// High priority should be first
assert_eq!(queue.peek().unwrap().name, "high");
}
#[test]
fn test_queue_lifecycle() {
let mut queue = CommandQueue::new(10);
let id = queue.enqueue("test", Priority::Normal);
assert_eq!(queue.pending_count(), 1);
queue.start_next();
assert!(queue.is_running());
assert_eq!(queue.pending_count(), 0);
queue.complete();
assert!(!queue.is_running());
}
}