1use crate::common::{
2 format_bytes, format_duration_seconds, format_unix_timestamp, translate_column, ColumnId,
3 UTC_TIMESTAMP_WIDTH,
4};
5use crate::ui::table::Column as TableColumn;
6use ratatui::prelude::*;
7use std::collections::HashMap;
8
9pub fn init(i18n: &mut HashMap<String, String>) {
10 for col in Column::all() {
11 i18n.entry(col.id().to_string())
12 .or_insert_with(|| col.default_name().to_string());
13 }
14}
15
16#[derive(Debug, Clone)]
17pub struct Queue {
18 pub name: String,
19 pub url: String,
20 pub queue_type: String,
21 pub created_timestamp: String,
22 pub messages_available: String,
23 pub messages_in_flight: String,
24 pub encryption: String,
25 pub content_based_deduplication: String,
26 pub last_modified_timestamp: String,
27 pub visibility_timeout: String,
28 pub message_retention_period: String,
29 pub maximum_message_size: String,
30 pub delivery_delay: String,
31 pub receive_message_wait_time: String,
32 pub high_throughput_fifo: String,
33 pub deduplication_scope: String,
34 pub fifo_throughput_limit: String,
35 pub dead_letter_queue: String,
36 pub messages_delayed: String,
37 pub redrive_allow_policy: String,
38 pub redrive_policy: String,
39 pub redrive_task_id: String,
40 pub redrive_task_start_time: String,
41 pub redrive_task_status: String,
42 pub redrive_task_percent: String,
43 pub redrive_task_destination: String,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq)]
47pub enum Column {
48 Name,
49 Type,
50 Created,
51 MessagesAvailable,
52 MessagesInFlight,
53 Encryption,
54 ContentBasedDeduplication,
55 LastUpdated,
56 VisibilityTimeout,
57 MessageRetentionPeriod,
58 MaximumMessageSize,
59 DeliveryDelay,
60 ReceiveMessageWaitTime,
61 HighThroughputFifo,
62 DeduplicationScope,
63 FifoThroughputLimit,
64}
65
66impl Column {
67 pub fn id(&self) -> ColumnId {
68 match self {
69 Column::Name => "column.sqs.queue.name",
70 Column::Type => "column.sqs.queue.type",
71 Column::Created => "column.sqs.queue.created",
72 Column::MessagesAvailable => "column.sqs.queue.messages_available",
73 Column::MessagesInFlight => "column.sqs.queue.messages_in_flight",
74 Column::Encryption => "column.sqs.queue.encryption",
75 Column::ContentBasedDeduplication => "column.sqs.queue.content_based_deduplication",
76 Column::LastUpdated => "column.sqs.queue.last_updated",
77 Column::VisibilityTimeout => "column.sqs.queue.visibility_timeout",
78 Column::MessageRetentionPeriod => "column.sqs.queue.message_retention_period",
79 Column::MaximumMessageSize => "column.sqs.queue.maximum_message_size",
80 Column::DeliveryDelay => "column.sqs.queue.delivery_delay",
81 Column::ReceiveMessageWaitTime => "column.sqs.queue.receive_message_wait_time",
82 Column::HighThroughputFifo => "column.sqs.queue.high_throughput_fifo",
83 Column::DeduplicationScope => "column.sqs.queue.deduplication_scope",
84 Column::FifoThroughputLimit => "column.sqs.queue.fifo_throughput_limit",
85 }
86 }
87
88 pub fn default_name(&self) -> &'static str {
89 match self {
90 Column::Name => "Name",
91 Column::Type => "Type",
92 Column::Created => "Created",
93 Column::MessagesAvailable => "Messages available",
94 Column::MessagesInFlight => "Messages in flight",
95 Column::Encryption => "Encryption",
96 Column::ContentBasedDeduplication => "Content-based deduplication",
97 Column::LastUpdated => "Last updated",
98 Column::VisibilityTimeout => "Visibility timeout",
99 Column::MessageRetentionPeriod => "Message retention period",
100 Column::MaximumMessageSize => "Maximum message size",
101 Column::DeliveryDelay => "Delivery delay",
102 Column::ReceiveMessageWaitTime => "Receive message wait time",
103 Column::HighThroughputFifo => "High throughput FIFO",
104 Column::DeduplicationScope => "Deduplication scope",
105 Column::FifoThroughputLimit => "FIFO throughput limit",
106 }
107 }
108
109 pub fn name(&self) -> String {
110 translate_column(self.id(), self.default_name())
111 }
112
113 pub fn from_id(id: &str) -> Option<Self> {
114 match id {
115 "column.sqs.queue.name" => Some(Column::Name),
116 "column.sqs.queue.type" => Some(Column::Type),
117 "column.sqs.queue.created" => Some(Column::Created),
118 "column.sqs.queue.messages_available" => Some(Column::MessagesAvailable),
119 "column.sqs.queue.messages_in_flight" => Some(Column::MessagesInFlight),
120 "column.sqs.queue.encryption" => Some(Column::Encryption),
121 "column.sqs.queue.content_based_deduplication" => {
122 Some(Column::ContentBasedDeduplication)
123 }
124 "column.sqs.queue.last_updated" => Some(Column::LastUpdated),
125 "column.sqs.queue.visibility_timeout" => Some(Column::VisibilityTimeout),
126 "column.sqs.queue.message_retention_period" => Some(Column::MessageRetentionPeriod),
127 "column.sqs.queue.maximum_message_size" => Some(Column::MaximumMessageSize),
128 "column.sqs.queue.delivery_delay" => Some(Column::DeliveryDelay),
129 "column.sqs.queue.receive_message_wait_time" => Some(Column::ReceiveMessageWaitTime),
130 "column.sqs.queue.high_throughput_fifo" => Some(Column::HighThroughputFifo),
131 "column.sqs.queue.deduplication_scope" => Some(Column::DeduplicationScope),
132 "column.sqs.queue.fifo_throughput_limit" => Some(Column::FifoThroughputLimit),
133 _ => None,
134 }
135 }
136
137 pub fn all() -> [Column; 16] {
138 [
139 Column::Name,
140 Column::Type,
141 Column::Created,
142 Column::MessagesAvailable,
143 Column::MessagesInFlight,
144 Column::Encryption,
145 Column::ContentBasedDeduplication,
146 Column::LastUpdated,
147 Column::VisibilityTimeout,
148 Column::MessageRetentionPeriod,
149 Column::MaximumMessageSize,
150 Column::DeliveryDelay,
151 Column::ReceiveMessageWaitTime,
152 Column::HighThroughputFifo,
153 Column::DeduplicationScope,
154 Column::FifoThroughputLimit,
155 ]
156 }
157
158 pub fn ids() -> Vec<ColumnId> {
159 Self::all().iter().map(|c| c.id()).collect()
160 }
161}
162
163impl TableColumn<Queue> for Column {
164 fn name(&self) -> &str {
165 Box::leak(translate_column(self.id(), self.default_name()).into_boxed_str())
166 }
167
168 fn width(&self) -> u16 {
169 let translated = translate_column(self.id(), self.default_name());
170 translated.len().max(match self {
171 Column::Name => 40,
172 Column::Type => 10,
173 Column::Created => UTC_TIMESTAMP_WIDTH as usize,
174 Column::MessagesAvailable => 20,
175 Column::MessagesInFlight => 20,
176 Column::Encryption => 12,
177 Column::ContentBasedDeduplication => 30,
178 Column::LastUpdated => UTC_TIMESTAMP_WIDTH as usize,
179 Column::VisibilityTimeout => 20,
180 Column::MessageRetentionPeriod => 25,
181 Column::MaximumMessageSize => 22,
182 Column::DeliveryDelay => 15,
183 Column::ReceiveMessageWaitTime => 25,
184 Column::HighThroughputFifo => 22,
185 Column::DeduplicationScope => 22,
186 Column::FifoThroughputLimit => 22,
187 }) as u16
188 }
189
190 fn render(&self, item: &Queue) -> (String, Style) {
191 let text = match self {
192 Column::Name => item.name.clone(),
193 Column::Type => item.queue_type.clone(),
194 Column::Created => format_unix_timestamp(&item.created_timestamp),
195 Column::MessagesAvailable => item.messages_available.clone(),
196 Column::MessagesInFlight => item.messages_in_flight.clone(),
197 Column::Encryption => item.encryption.clone(),
198 Column::ContentBasedDeduplication => item.content_based_deduplication.clone(),
199 Column::LastUpdated => format_unix_timestamp(&item.last_modified_timestamp),
200 Column::VisibilityTimeout => {
201 if let Ok(seconds) = item.visibility_timeout.parse::<i32>() {
202 format_duration_seconds(seconds)
203 } else {
204 item.visibility_timeout.clone()
205 }
206 }
207 Column::MessageRetentionPeriod => {
208 if let Ok(seconds) = item.message_retention_period.parse::<i32>() {
209 format_duration_seconds(seconds)
210 } else {
211 item.message_retention_period.clone()
212 }
213 }
214 Column::MaximumMessageSize => {
215 if let Some(bytes_str) = item.maximum_message_size.split_whitespace().next() {
216 if let Ok(bytes) = bytes_str.parse::<i64>() {
217 format_bytes(bytes)
218 } else {
219 item.maximum_message_size.clone()
220 }
221 } else {
222 item.maximum_message_size.clone()
223 }
224 }
225 Column::DeliveryDelay => {
226 if let Ok(seconds) = item.delivery_delay.parse::<i32>() {
227 format_duration_seconds(seconds)
228 } else {
229 item.delivery_delay.clone()
230 }
231 }
232 Column::ReceiveMessageWaitTime => {
233 if let Ok(seconds) = item.receive_message_wait_time.parse::<i32>() {
234 format_duration_seconds(seconds)
235 } else {
236 item.receive_message_wait_time.clone()
237 }
238 }
239 Column::HighThroughputFifo => item.high_throughput_fifo.clone(),
240 Column::DeduplicationScope => item.deduplication_scope.clone(),
241 Column::FifoThroughputLimit => item.fifo_throughput_limit.clone(),
242 };
243 (text, Style::default())
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 #[test]
252 fn test_column_all() {
253 assert_eq!(Column::all().len(), 16);
254 }
255
256 #[test]
257 fn test_maximum_message_size_formatting() {
258 let queue = Queue {
259 name: "test".to_string(),
260 url: String::new(),
261 queue_type: "Standard".to_string(),
262 created_timestamp: String::new(),
263 messages_available: "0".to_string(),
264 messages_in_flight: "0".to_string(),
265 encryption: "Disabled".to_string(),
266 content_based_deduplication: "Disabled".to_string(),
267 last_modified_timestamp: String::new(),
268 visibility_timeout: String::new(),
269 message_retention_period: String::new(),
270 maximum_message_size: "262144 bytes".to_string(),
271 delivery_delay: String::new(),
272 receive_message_wait_time: String::new(),
273 high_throughput_fifo: "-".to_string(),
274 deduplication_scope: "-".to_string(),
275 fifo_throughput_limit: "-".to_string(),
276 dead_letter_queue: "-".to_string(),
277 messages_delayed: "0".to_string(),
278 redrive_allow_policy: "-".to_string(),
279 redrive_policy: "".to_string(),
280 redrive_task_id: "-".to_string(),
281 redrive_task_start_time: "-".to_string(),
282 redrive_task_status: "-".to_string(),
283 redrive_task_percent: "-".to_string(),
284 redrive_task_destination: "-".to_string(),
285 };
286
287 let (text, _) = Column::MaximumMessageSize.render(&queue);
288 assert_eq!(text, "262.14 KB");
289 }
290
291 #[test]
292 fn test_duration_formatting() {
293 let queue = Queue {
294 name: "test".to_string(),
295 url: String::new(),
296 queue_type: "Standard".to_string(),
297 created_timestamp: String::new(),
298 messages_available: "0".to_string(),
299 messages_in_flight: "0".to_string(),
300 encryption: "Disabled".to_string(),
301 content_based_deduplication: "Disabled".to_string(),
302 last_modified_timestamp: String::new(),
303 visibility_timeout: "30".to_string(),
304 message_retention_period: "345600".to_string(),
305 maximum_message_size: String::new(),
306 delivery_delay: "0".to_string(),
307 receive_message_wait_time: "20".to_string(),
308 high_throughput_fifo: "-".to_string(),
309 deduplication_scope: "-".to_string(),
310 fifo_throughput_limit: "-".to_string(),
311 dead_letter_queue: "-".to_string(),
312 messages_delayed: "0".to_string(),
313 redrive_allow_policy: "-".to_string(),
314 redrive_policy: "".to_string(),
315 redrive_task_id: "-".to_string(),
316 redrive_task_start_time: "-".to_string(),
317 redrive_task_status: "-".to_string(),
318 redrive_task_percent: "-".to_string(),
319 redrive_task_destination: "-".to_string(),
320 };
321
322 let (text, _) = Column::VisibilityTimeout.render(&queue);
323 assert_eq!(text, "30s");
324
325 let (text, _) = Column::MessageRetentionPeriod.render(&queue);
326 assert_eq!(text, "4d");
327
328 let (text, _) = Column::DeliveryDelay.render(&queue);
329 assert_eq!(text, "0s");
330
331 let (text, _) = Column::ReceiveMessageWaitTime.render(&queue);
332 assert_eq!(text, "20s");
333 }
334
335 #[test]
336 fn test_timestamp_formatting() {
337 let queue = Queue {
338 name: "test".to_string(),
339 url: String::new(),
340 queue_type: "Standard".to_string(),
341 created_timestamp: "1609459200".to_string(),
342 messages_available: "0".to_string(),
343 messages_in_flight: "0".to_string(),
344 encryption: "Disabled".to_string(),
345 content_based_deduplication: "Disabled".to_string(),
346 last_modified_timestamp: "1609459200".to_string(),
347 visibility_timeout: String::new(),
348 message_retention_period: String::new(),
349 maximum_message_size: String::new(),
350 delivery_delay: String::new(),
351 receive_message_wait_time: String::new(),
352 high_throughput_fifo: "-".to_string(),
353 deduplication_scope: "-".to_string(),
354 fifo_throughput_limit: "-".to_string(),
355 dead_letter_queue: "-".to_string(),
356 messages_delayed: "0".to_string(),
357 redrive_allow_policy: "-".to_string(),
358 redrive_policy: "".to_string(),
359 redrive_task_id: "-".to_string(),
360 redrive_task_start_time: "-".to_string(),
361 redrive_task_status: "-".to_string(),
362 redrive_task_percent: "-".to_string(),
363 redrive_task_destination: "-".to_string(),
364 };
365
366 let (text, _) = Column::Created.render(&queue);
367 assert!(text.contains("2021-01-01"));
368 assert!(text.contains("(UTC)"));
369 }
370}
371
372#[test]
373fn test_column_ids_have_correct_prefix() {
374 for col in Column::all() {
375 assert!(
376 col.id().starts_with("column.sqs.queue."),
377 "Column ID '{}' should start with 'column.sqs.queue.'",
378 col.id()
379 );
380 }
381}