rusticity_term/sqs/
queue.rs

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}