1use crate::common::{format_bytes, ColumnTrait, UTC_TIMESTAMP_WIDTH};
2use crate::ui::lambda::{ApplicationDetailTab, DetailTab};
3use crate::ui::table::Column as TableColumn;
4use ratatui::prelude::*;
5
6pub fn format_runtime(runtime: &str) -> String {
7 let lower = runtime.to_lowercase();
8
9 if let Some(rest) = lower.strip_prefix("python") {
10 let version = if rest.contains('.') {
11 rest.to_string()
12 } else if rest.len() >= 2 {
13 format!("{}.{}", &rest[0..1], &rest[1..])
14 } else {
15 rest.to_string()
16 };
17 format!("Python {}", version)
18 } else if let Some(rest) = lower.strip_prefix("nodejs") {
19 let formatted = rest.replace("x", ".x");
20 format!("Node.js {}", formatted)
21 } else if let Some(rest) = lower.strip_prefix("java") {
22 format!("Java {}", rest)
23 } else if let Some(rest) = lower.strip_prefix("dotnet") {
24 format!(".NET {}", rest)
25 } else if let Some(rest) = lower.strip_prefix("go") {
26 format!("Go {}", rest)
27 } else if let Some(rest) = lower.strip_prefix("ruby") {
28 format!("Ruby {}", rest)
29 } else {
30 runtime.to_string()
31 }
32}
33
34pub fn format_architecture(arch: &str) -> String {
35 match arch {
36 "X86_64" => "x86-64".to_string(),
37 "X8664" => "x86-64".to_string(),
38 "Arm64" => "arm64".to_string(),
39 _ => arch.replace("X86", "x86").replace("Arm", "arm"),
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46
47 #[test]
48 fn test_format_runtime() {
49 assert_eq!(format_runtime("Python39"), "Python 3.9");
51 assert_eq!(format_runtime("Python312"), "Python 3.12");
52 assert_eq!(format_runtime("Nodejs20x"), "Node.js 20.x");
53
54 assert_eq!(format_runtime("python3.12"), "Python 3.12");
56 assert_eq!(format_runtime("python3.11"), "Python 3.11");
57 assert_eq!(format_runtime("nodejs20x"), "Node.js 20.x");
58 assert_eq!(format_runtime("nodejs18x"), "Node.js 18.x");
59 assert_eq!(format_runtime("java21"), "Java 21");
60 assert_eq!(format_runtime("dotnet8"), ".NET 8");
61 assert_eq!(format_runtime("go1.x"), "Go 1.x");
62 assert_eq!(format_runtime("ruby3.3"), "Ruby 3.3");
63 assert_eq!(format_runtime("unknown"), "unknown");
64 }
65
66 #[test]
67 fn test_format_architecture() {
68 assert_eq!(format_architecture("X8664"), "x86-64");
69 assert_eq!(format_architecture("X86_64"), "x86-64");
70 assert_eq!(format_architecture("Arm64"), "arm64");
71 assert_eq!(format_architecture("arm64"), "arm64");
72 }
73
74 #[test]
75 fn test_runtime_formatter_in_table_column() {
76 let func = Function {
77 name: "test-func".to_string(),
78 arn: "arn".to_string(),
79 application: None,
80 description: "desc".to_string(),
81 package_type: "Zip".to_string(),
82 runtime: "python3.12".to_string(),
83 architecture: "X86_64".to_string(),
84 code_size: 1024,
85 code_sha256: "hash".to_string(),
86 memory_mb: 128,
87 timeout_seconds: 30,
88 last_modified: "2024-01-01".to_string(),
89 layers: vec![],
90 };
91
92 let runtime_col = Column::Runtime.to_column();
93 let (text, _) = runtime_col.render(&func);
94 assert_eq!(text, "Python 3.12");
95 }
96
97 #[test]
98 fn test_architecture_formatter_in_table_column() {
99 let func = Function {
100 name: "test-func".to_string(),
101 arn: "arn".to_string(),
102 application: None,
103 description: "desc".to_string(),
104 package_type: "Zip".to_string(),
105 runtime: "python3.12".to_string(),
106 architecture: "X86_64".to_string(),
107 code_size: 1024,
108 code_sha256: "hash".to_string(),
109 memory_mb: 128,
110 timeout_seconds: 30,
111 last_modified: "2024-01-01".to_string(),
112 layers: vec![],
113 };
114
115 let arch_col = Column::Architecture.to_column();
116 let (text, _) = arch_col.render(&func);
117 assert_eq!(text, "x86-64");
118 }
119
120 #[test]
121 fn test_nodejs_runtime_formatting() {
122 assert_eq!(format_runtime("nodejs16x"), "Node.js 16.x");
123 assert_eq!(format_runtime("nodejs18x"), "Node.js 18.x");
124 assert_eq!(format_runtime("nodejs20x"), "Node.js 20.x");
125 }
126
127 #[test]
128 fn test_python_runtime_formatting() {
129 assert_eq!(format_runtime("Python38"), "Python 3.8");
131 assert_eq!(format_runtime("Python39"), "Python 3.9");
132 assert_eq!(format_runtime("Python310"), "Python 3.10");
133 assert_eq!(format_runtime("Python311"), "Python 3.11");
134 assert_eq!(format_runtime("Python312"), "Python 3.12");
135
136 assert_eq!(format_runtime("python3.8"), "Python 3.8");
138 assert_eq!(format_runtime("python3.9"), "Python 3.9");
139 assert_eq!(format_runtime("python3.10"), "Python 3.10");
140 assert_eq!(format_runtime("python3.11"), "Python 3.11");
141 assert_eq!(format_runtime("python3.12"), "Python 3.12");
142 }
143
144 #[test]
145 fn test_timeout_formatting() {
146 assert_eq!(300 / 60, 5); assert_eq!(300 % 60, 0); assert_eq!(900 / 60, 15); assert_eq!(900 % 60, 0); assert_eq!(330 / 60, 5); assert_eq!(330 % 60, 30); }
154
155 #[test]
156 fn test_version_column_architecture_formatter() {
157 let version = Version {
158 version: "1".to_string(),
159 aliases: "prod".to_string(),
160 description: "Production version".to_string(),
161 last_modified: "2024-01-01".to_string(),
162 architecture: "X86_64".to_string(),
163 };
164
165 let arch_col = VersionColumn::Architecture.to_column();
166 let (text, _) = arch_col.render(&version);
167 assert_eq!(text, "x86-64");
168 }
169
170 #[test]
171 fn test_version_architecture_formatter_arm() {
172 let version = Version {
173 version: "1".to_string(),
174 aliases: "".to_string(),
175 description: "".to_string(),
176 last_modified: "".to_string(),
177 architecture: "Arm64".to_string(),
178 };
179
180 let arch_col = VersionColumn::Architecture.to_column();
181 let (text, _) = arch_col.render(&version);
182 assert_eq!(text, "arm64");
183 }
184
185 #[test]
186 fn test_version_column_all() {
187 let all = VersionColumn::all();
188 assert_eq!(all.len(), 5);
189 assert!(all.contains(&VersionColumn::Version));
190 assert!(all.contains(&VersionColumn::Aliases));
191 assert!(all.contains(&VersionColumn::Description));
192 assert!(all.contains(&VersionColumn::LastModified));
193 assert!(all.contains(&VersionColumn::Architecture));
194 }
195
196 #[test]
197 fn test_version_column_names() {
198 assert_eq!(VersionColumn::Version.name(), "Version");
199 assert_eq!(VersionColumn::Aliases.name(), "Aliases");
200 assert_eq!(VersionColumn::Description.name(), "Description");
201 assert_eq!(VersionColumn::LastModified.name(), "Last modified");
202 assert_eq!(VersionColumn::Architecture.name(), "Architecture");
203 }
204
205 #[test]
206 fn test_versions_table_sort_config() {
207 let sort_column = "Version";
211 let sort_direction = "DESC";
212 assert_eq!(sort_column, "Version");
213 assert_eq!(sort_direction, "DESC");
214 }
215
216 #[test]
217 fn test_input_focus_cycling() {
218 use crate::common::InputFocus;
219 use crate::ui::lambda::FILTER_CONTROLS;
220
221 let focus = InputFocus::Filter;
222 assert_eq!(focus.next(&FILTER_CONTROLS), InputFocus::Pagination);
223
224 let focus = InputFocus::Pagination;
225 assert_eq!(focus.next(&FILTER_CONTROLS), InputFocus::Filter);
226
227 let focus = InputFocus::Filter;
228 assert_eq!(focus.prev(&FILTER_CONTROLS), InputFocus::Pagination);
229
230 let focus = InputFocus::Pagination;
231 assert_eq!(focus.prev(&FILTER_CONTROLS), InputFocus::Filter);
232 }
233
234 #[test]
235 #[allow(clippy::useless_vec)]
236 fn test_version_sorting_desc() {
237 let mut versions = vec![
238 Version {
239 version: "1".to_string(),
240 aliases: "".to_string(),
241 description: "".to_string(),
242 last_modified: "".to_string(),
243 architecture: "".to_string(),
244 },
245 Version {
246 version: "10".to_string(),
247 aliases: "".to_string(),
248 description: "".to_string(),
249 last_modified: "".to_string(),
250 architecture: "".to_string(),
251 },
252 Version {
253 version: "2".to_string(),
254 aliases: "".to_string(),
255 description: "".to_string(),
256 last_modified: "".to_string(),
257 architecture: "".to_string(),
258 },
259 ];
260
261 versions.sort_by(|a, b| {
263 let a_num = a.version.parse::<i32>().unwrap_or(0);
264 let b_num = b.version.parse::<i32>().unwrap_or(0);
265 b_num.cmp(&a_num)
266 });
267
268 assert_eq!(versions[0].version, "10");
269 assert_eq!(versions[1].version, "2");
270 assert_eq!(versions[2].version, "1");
271 }
272
273 #[test]
274 fn test_version_sorting_with_36_versions() {
275 let mut versions: Vec<Version> = (1..=36)
276 .map(|i| Version {
277 version: i.to_string(),
278 aliases: "".to_string(),
279 description: "".to_string(),
280 last_modified: "".to_string(),
281 architecture: "".to_string(),
282 })
283 .collect();
284
285 versions.sort_by(|a, b| {
287 let a_num = a.version.parse::<i32>().unwrap_or(0);
288 let b_num = b.version.parse::<i32>().unwrap_or(0);
289 b_num.cmp(&a_num)
290 });
291
292 assert_eq!(versions[0].version, "36");
294 assert_eq!(versions[1].version, "35");
295 assert_eq!(versions[35].version, "1");
296 assert_eq!(versions.len(), 36);
297 }
298}
299
300pub fn console_url_functions(region: &str) -> String {
301 format!(
302 "https://{}.console.aws.amazon.com/lambda/home?region={}#/functions",
303 region, region
304 )
305}
306
307pub fn console_url_function_detail(region: &str, function_name: &str) -> String {
308 format!(
309 "https://{}.console.aws.amazon.com/lambda/home?region={}#/functions/{}",
310 region, region, function_name
311 )
312}
313
314pub fn console_url_function_version(
315 region: &str,
316 function_name: &str,
317 version: &str,
318 detail_tab: &DetailTab,
319) -> String {
320 let tab = match detail_tab {
321 DetailTab::Code => "code",
322 DetailTab::Configuration => "configure",
323 _ => "code",
324 };
325 format!(
326 "https://{}.console.aws.amazon.com/lambda/home?region={}#/functions/{}/versions/{}?tab={}",
327 region, region, function_name, version, tab
328 )
329}
330
331pub fn console_url_applications(region: &str) -> String {
332 format!(
333 "https://{}.console.aws.amazon.com/lambda/home?region={}#/applications",
334 region, region
335 )
336}
337
338pub fn console_url_application_detail(
339 region: &str,
340 app_name: &str,
341 tab: &ApplicationDetailTab,
342) -> String {
343 let tab_param = match tab {
344 ApplicationDetailTab::Overview => "overview",
345 ApplicationDetailTab::Deployments => "deployments",
346 };
347 format!(
348 "https://{}.console.aws.amazon.com/lambda/home?region={}#/applications/{}?tab={}",
349 region, region, app_name, tab_param
350 )
351}
352
353#[derive(Debug, Clone)]
354pub struct Function {
355 pub name: String,
356 pub arn: String,
357 pub application: Option<String>,
358 pub description: String,
359 pub package_type: String,
360 pub runtime: String,
361 pub architecture: String,
362 pub code_size: i64,
363 pub code_sha256: String,
364 pub memory_mb: i32,
365 pub timeout_seconds: i32,
366 pub last_modified: String,
367 pub layers: Vec<Layer>,
368}
369
370#[derive(Debug, Clone)]
371pub struct Layer {
372 pub arn: String,
373 pub code_size: i64,
374}
375
376#[derive(Debug, Clone)]
377pub struct Version {
378 pub version: String,
379 pub aliases: String,
380 pub description: String,
381 pub last_modified: String,
382 pub architecture: String,
383}
384
385#[derive(Debug, Clone)]
386pub struct Alias {
387 pub name: String,
388 pub versions: String,
389 pub description: String,
390}
391
392#[derive(Debug, Clone)]
393pub struct Application {
394 pub name: String,
395 pub arn: String,
396 pub description: String,
397 pub status: String,
398 pub last_modified: String,
399}
400
401#[derive(Debug, Clone, Copy, PartialEq)]
402pub enum Column {
403 Name,
404 Description,
405 PackageType,
406 Runtime,
407 Architecture,
408 CodeSize,
409 MemoryMb,
410 TimeoutSeconds,
411 LastModified,
412}
413
414impl Column {
415 pub fn name(&self) -> &'static str {
416 match self {
417 Column::Name => "Function name",
418 Column::Description => "Description",
419 Column::PackageType => "Package type",
420 Column::Runtime => "Runtime",
421 Column::Architecture => "Architecture",
422 Column::CodeSize => "Code size",
423 Column::MemoryMb => "Memory (MB)",
424 Column::TimeoutSeconds => "Timeout (s)",
425 Column::LastModified => "Last modified",
426 }
427 }
428
429 pub fn all() -> Vec<Column> {
430 vec![
431 Column::Name,
432 Column::Description,
433 Column::PackageType,
434 Column::Runtime,
435 Column::Architecture,
436 Column::CodeSize,
437 Column::MemoryMb,
438 Column::TimeoutSeconds,
439 Column::LastModified,
440 ]
441 }
442
443 pub fn to_column(&self) -> Box<dyn TableColumn<Function>> {
444 struct FunctionColumn {
445 variant: Column,
446 }
447
448 impl TableColumn<Function> for FunctionColumn {
449 fn name(&self) -> &str {
450 self.variant.name()
451 }
452
453 fn width(&self) -> u16 {
454 self.variant.name().len().max(match self.variant {
455 Column::Name => 30,
456 Column::Description => 40,
457 Column::Runtime => 20,
458 Column::LastModified => UTC_TIMESTAMP_WIDTH as usize,
459 _ => 0,
460 }) as u16
461 }
462
463 fn render(&self, item: &Function) -> (String, Style) {
464 let text = match self.variant {
465 Column::Name => item.name.clone(),
466 Column::Description => item.description.clone(),
467 Column::PackageType => item.package_type.clone(),
468 Column::Runtime => format_runtime(&item.runtime),
469 Column::Architecture => format_architecture(&item.architecture),
470 Column::CodeSize => format_bytes(item.code_size),
471 Column::MemoryMb => item.memory_mb.to_string(),
472 Column::TimeoutSeconds => item.timeout_seconds.to_string(),
473 Column::LastModified => item.last_modified.clone(),
474 };
475 (text, Style::default())
476 }
477 }
478
479 Box::new(FunctionColumn { variant: *self })
480 }
481}
482
483#[derive(Debug, Clone, Copy, PartialEq)]
484pub enum ApplicationColumn {
485 Name,
486 Description,
487 Status,
488 LastModified,
489}
490
491impl ApplicationColumn {
492 pub fn name(&self) -> &'static str {
493 match self {
494 ApplicationColumn::Name => "Name",
495 ApplicationColumn::Description => "Description",
496 ApplicationColumn::Status => "Status",
497 ApplicationColumn::LastModified => "Last modified",
498 }
499 }
500
501 pub fn all() -> Vec<ApplicationColumn> {
502 vec![
503 ApplicationColumn::Name,
504 ApplicationColumn::Description,
505 ApplicationColumn::Status,
506 ApplicationColumn::LastModified,
507 ]
508 }
509
510 pub fn to_column(&self) -> Box<dyn crate::ui::table::Column<Application>> {
511 struct ApplicationColumnImpl {
512 variant: ApplicationColumn,
513 }
514
515 impl crate::ui::table::Column<Application> for ApplicationColumnImpl {
516 fn name(&self) -> &str {
517 self.variant.name()
518 }
519
520 fn width(&self) -> u16 {
521 match self.variant {
522 ApplicationColumn::Name => 40,
523 ApplicationColumn::Description => 50,
524 ApplicationColumn::Status => 20,
525 ApplicationColumn::LastModified => UTC_TIMESTAMP_WIDTH,
526 }
527 }
528
529 fn render(&self, item: &Application) -> (String, Style) {
530 use ratatui::prelude::{Color, Style};
531 match self.variant {
532 ApplicationColumn::Name => (item.name.clone(), Style::default()),
533 ApplicationColumn::Description => (item.description.clone(), Style::default()),
534 ApplicationColumn::Status => {
535 let status_upper = item.status.to_uppercase();
536 let (text, color) = if status_upper.contains("UPDATE_COMPLETE") {
537 ("✅ Update complete", Color::Green)
538 } else if status_upper.contains("CREATE_COMPLETE") {
539 ("✅ Create complete", Color::Green)
540 } else {
541 (item.status.as_str(), Color::White)
542 };
543 (text.to_string(), Style::default().fg(color))
544 }
545 ApplicationColumn::LastModified => {
546 (item.last_modified.clone(), Style::default())
547 }
548 }
549 }
550 }
551
552 Box::new(ApplicationColumnImpl { variant: *self })
553 }
554}
555
556impl ColumnTrait for Column {
557 fn name(&self) -> &'static str {
558 self.name()
559 }
560}
561
562impl ColumnTrait for ApplicationColumn {
563 fn name(&self) -> &'static str {
564 self.name()
565 }
566}
567
568#[derive(Debug, Clone, Copy, PartialEq)]
569pub enum VersionColumn {
570 Version,
571 Aliases,
572 Description,
573 LastModified,
574 Architecture,
575}
576
577impl VersionColumn {
578 pub fn name(&self) -> &'static str {
579 match self {
580 VersionColumn::Version => "Version",
581 VersionColumn::Aliases => "Aliases",
582 VersionColumn::Description => "Description",
583 VersionColumn::LastModified => "Last modified",
584 VersionColumn::Architecture => "Architecture",
585 }
586 }
587
588 pub fn all() -> Vec<VersionColumn> {
589 vec![
590 VersionColumn::Version,
591 VersionColumn::Aliases,
592 VersionColumn::Description,
593 VersionColumn::LastModified,
594 VersionColumn::Architecture,
595 ]
596 }
597
598 pub fn to_column(&self) -> Box<dyn crate::ui::table::Column<Version>> {
599 struct VersionCol {
600 variant: VersionColumn,
601 }
602
603 impl crate::ui::table::Column<Version> for VersionCol {
604 fn name(&self) -> &str {
605 self.variant.name()
606 }
607
608 fn width(&self) -> u16 {
609 match self.variant {
610 VersionColumn::Version => 10,
611 VersionColumn::Aliases => 20,
612 VersionColumn::Description => 40,
613 VersionColumn::LastModified => UTC_TIMESTAMP_WIDTH,
614 VersionColumn::Architecture => 15,
615 }
616 }
617
618 fn render(&self, item: &Version) -> (String, Style) {
619 let text = match self.variant {
620 VersionColumn::Version => item.version.clone(),
621 VersionColumn::Aliases => item.aliases.clone(),
622 VersionColumn::Description => item.description.clone(),
623 VersionColumn::LastModified => item.last_modified.clone(),
624 VersionColumn::Architecture => format_architecture(&item.architecture),
625 };
626 (text, ratatui::style::Style::default())
627 }
628 }
629
630 Box::new(VersionCol { variant: *self })
631 }
632}
633
634impl ColumnTrait for VersionColumn {
635 fn name(&self) -> &'static str {
636 self.name()
637 }
638}
639
640#[derive(Debug, Clone, Copy, PartialEq)]
641pub enum AliasColumn {
642 Name,
643 Versions,
644 Description,
645}
646
647impl AliasColumn {
648 pub fn name(&self) -> &'static str {
649 match self {
650 AliasColumn::Name => "Name",
651 AliasColumn::Versions => "Versions",
652 AliasColumn::Description => "Description",
653 }
654 }
655
656 pub fn all() -> Vec<AliasColumn> {
657 vec![
658 AliasColumn::Name,
659 AliasColumn::Versions,
660 AliasColumn::Description,
661 ]
662 }
663
664 pub fn to_column(&self) -> Box<dyn crate::ui::table::Column<Alias>> {
665 struct AliasCol {
666 variant: AliasColumn,
667 }
668
669 impl crate::ui::table::Column<Alias> for AliasCol {
670 fn name(&self) -> &str {
671 self.variant.name()
672 }
673
674 fn width(&self) -> u16 {
675 match self.variant {
676 AliasColumn::Name => 20,
677 AliasColumn::Versions => 15,
678 AliasColumn::Description => 50,
679 }
680 }
681
682 fn render(&self, item: &Alias) -> (String, Style) {
683 let text = match self.variant {
684 AliasColumn::Name => item.name.clone(),
685 AliasColumn::Versions => item.versions.clone(),
686 AliasColumn::Description => item.description.clone(),
687 };
688 (text, ratatui::style::Style::default())
689 }
690 }
691
692 Box::new(AliasCol { variant: *self })
693 }
694}
695
696impl ColumnTrait for AliasColumn {
697 fn name(&self) -> &'static str {
698 self.name()
699 }
700}
701
702#[derive(Debug, Clone, Copy, PartialEq)]
703pub enum LayerColumn {
704 MergeOrder,
705 Name,
706 LayerVersion,
707 CompatibleRuntimes,
708 CompatibleArchitectures,
709 VersionArn,
710}
711
712impl LayerColumn {
713 pub fn name(&self) -> &'static str {
714 match self {
715 LayerColumn::MergeOrder => "Merge order",
716 LayerColumn::Name => "Name",
717 LayerColumn::LayerVersion => "Layer version",
718 LayerColumn::CompatibleRuntimes => "Compatible runtimes",
719 LayerColumn::CompatibleArchitectures => "Compatible architectures",
720 LayerColumn::VersionArn => "Version ARN",
721 }
722 }
723
724 pub fn all() -> Vec<LayerColumn> {
725 vec![
726 LayerColumn::MergeOrder,
727 LayerColumn::Name,
728 LayerColumn::LayerVersion,
729 LayerColumn::CompatibleRuntimes,
730 LayerColumn::CompatibleArchitectures,
731 LayerColumn::VersionArn,
732 ]
733 }
734}
735
736impl ColumnTrait for LayerColumn {
737 fn name(&self) -> &'static str {
738 self.name()
739 }
740}
741
742#[derive(Debug, Clone, Copy, PartialEq)]
743pub enum DeploymentColumn {
744 Deployment,
745 ResourceType,
746 LastUpdated,
747 Status,
748}
749
750impl DeploymentColumn {
751 pub fn all() -> Vec<Self> {
752 vec![
753 Self::Deployment,
754 Self::ResourceType,
755 Self::LastUpdated,
756 Self::Status,
757 ]
758 }
759
760 pub fn name(&self) -> &'static str {
761 match self {
762 Self::Deployment => "Deployment",
763 Self::ResourceType => "Resource type",
764 Self::LastUpdated => "Last updated time",
765 Self::Status => "Status",
766 }
767 }
768
769 pub fn as_table_column(self) -> Box<dyn TableColumn<Deployment>> {
770 struct DeploymentColumnImpl {
771 variant: DeploymentColumn,
772 }
773
774 impl TableColumn<Deployment> for DeploymentColumnImpl {
775 fn name(&self) -> &str {
776 self.variant.name()
777 }
778
779 fn width(&self) -> u16 {
780 match self.variant {
781 DeploymentColumn::Deployment => 30,
782 DeploymentColumn::ResourceType => 20,
783 DeploymentColumn::LastUpdated => UTC_TIMESTAMP_WIDTH,
784 DeploymentColumn::Status => 20,
785 }
786 }
787
788 fn render(&self, item: &Deployment) -> (String, Style) {
789 match self.variant {
790 DeploymentColumn::Deployment => (item.deployment_id.clone(), Style::default()),
791 DeploymentColumn::ResourceType => {
792 (item.resource_type.clone(), Style::default())
793 }
794 DeploymentColumn::LastUpdated => (item.last_updated.clone(), Style::default()),
795 DeploymentColumn::Status => {
796 if item.status == "Succeeded" {
797 (
798 format!("✅ {}", item.status),
799 Style::default().fg(Color::Green),
800 )
801 } else {
802 (item.status.clone(), Style::default())
803 }
804 }
805 }
806 }
807 }
808
809 Box::new(DeploymentColumnImpl { variant: self })
810 }
811}
812
813impl ColumnTrait for DeploymentColumn {
814 fn name(&self) -> &'static str {
815 self.name()
816 }
817}
818
819#[derive(Clone, Debug)]
820pub struct Resource {
821 pub logical_id: String,
822 pub physical_id: String,
823 pub resource_type: String,
824 pub last_modified: String,
825}
826
827#[derive(Clone, Debug)]
828pub struct Deployment {
829 pub deployment_id: String,
830 pub resource_type: String,
831 pub last_updated: String,
832 pub status: String,
833}
834
835#[derive(Debug, Clone, Copy, PartialEq)]
836pub enum ResourceColumn {
837 LogicalId,
838 PhysicalId,
839 Type,
840 LastModified,
841}
842
843impl ColumnTrait for ResourceColumn {
844 fn name(&self) -> &'static str {
845 self.name()
846 }
847}
848
849impl ResourceColumn {
850 pub fn all() -> Vec<Self> {
851 vec![
852 Self::LogicalId,
853 Self::PhysicalId,
854 Self::Type,
855 Self::LastModified,
856 ]
857 }
858
859 pub fn name(&self) -> &'static str {
860 match self {
861 Self::LogicalId => "Logical ID",
862 Self::PhysicalId => "Physical ID",
863 Self::Type => "Type",
864 Self::LastModified => "Last modified",
865 }
866 }
867}