Skip to main content

rusticity_term/vpc/
columns.rs

1use crate::common::{translate_column, ColumnId};
2use crate::ui::table::Column as TableColumn;
3use ratatui::prelude::*;
4use rusticity_core::vpc::Vpc;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum Column {
8    VpcId,
9    State,
10    EncryptionControlId,
11    EncryptionControlMode,
12    BlockPublicAccess,
13    Ipv4Cidrs,
14    Ipv6Cidrs,
15    DhcpOptions,
16    MainRouteTable,
17    MainNetworkAcl,
18    Tenancy,
19    DefaultVpc,
20    Owner,
21}
22
23impl Column {
24    /// The console's 13 selectable properties, in console order. Name, Region,
25    /// and ARN remain metadata for filtering/copying, not additional properties.
26    pub const ALL: [Self; 13] = [
27        Self::VpcId,
28        Self::State,
29        Self::EncryptionControlId,
30        Self::EncryptionControlMode,
31        Self::BlockPublicAccess,
32        Self::Ipv4Cidrs,
33        Self::Ipv6Cidrs,
34        Self::DhcpOptions,
35        Self::MainRouteTable,
36        Self::MainNetworkAcl,
37        Self::Tenancy,
38        Self::DefaultVpc,
39        Self::Owner,
40    ];
41
42    fn definition(self) -> (ColumnId, &'static str, usize) {
43        match self {
44            Self::VpcId => ("column.vpc.vpc.id", "VPC ID", 23),
45            Self::State => ("column.vpc.vpc.state", "State", 16),
46            Self::EncryptionControlId => (
47                "column.vpc.vpc.encryption_control_id",
48                "Encryption control ID",
49                25,
50            ),
51            Self::EncryptionControlMode => (
52                "column.vpc.vpc.encryption_control_mode",
53                "Encryption control mode",
54                25,
55            ),
56            Self::BlockPublicAccess => (
57                "column.vpc.vpc.block_public_access",
58                "Block Public Access",
59                23,
60            ),
61            Self::Ipv4Cidrs => ("column.vpc.vpc.ipv4", "IPv4 CIDR", 22),
62            Self::Ipv6Cidrs => ("column.vpc.vpc.ipv6", "IPv6 CIDR", 45),
63            Self::DhcpOptions => ("column.vpc.vpc.dhcp_options", "DHCP option set", 24),
64            Self::MainRouteTable => ("column.vpc.vpc.main_route_table", "Main route table", 24),
65            Self::MainNetworkAcl => ("column.vpc.vpc.main_network_acl", "Main network ACL", 24),
66            Self::Tenancy => ("column.vpc.vpc.tenancy", "Tenancy", 12),
67            Self::DefaultVpc => ("column.vpc.vpc.default", "Default VPC", 12),
68            Self::Owner => ("column.vpc.vpc.owner", "Owner ID", 14),
69        }
70    }
71    pub fn id(self) -> ColumnId {
72        self.definition().0
73    }
74    pub fn default_name(self) -> &'static str {
75        self.definition().1
76    }
77    pub fn name(self) -> String {
78        translate_column(self.id(), self.default_name())
79    }
80    pub fn ids() -> Vec<ColumnId> {
81        Self::ALL.into_iter().map(Self::id).collect()
82    }
83    pub fn default_visible_ids() -> Vec<ColumnId> {
84        [
85            Self::VpcId,
86            Self::State,
87            Self::Ipv4Cidrs,
88            Self::Ipv6Cidrs,
89            Self::Tenancy,
90            Self::DefaultVpc,
91            Self::Owner,
92        ]
93        .into_iter()
94        .map(Self::id)
95        .collect()
96    }
97    pub fn from_id(id: &str) -> Option<Self> {
98        Self::ALL.into_iter().find(|c| c.id() == id)
99    }
100    pub fn render(self, vpc: &Vpc) -> (String, Style) {
101        let (value, color) = match self {
102            Self::State => match vpc.state.to_ascii_lowercase().as_str() {
103                "available" => ("✅ Available".into(), Color::Green),
104                "pending" => ("⏳ Pending".into(), Color::Yellow),
105                _ => (vpc.state.clone(), Color::White),
106            },
107            Self::DefaultVpc => match vpc.is_default {
108                Some(true) => ("Yes".into(), Color::Green),
109                Some(false) => ("No".into(), Color::White),
110                None => (String::new(), Color::White),
111            },
112            Self::VpcId => (vpc.vpc_id.clone(), Color::White),
113            Self::EncryptionControlId => (vpc.encryption_control_id.clone(), Color::White),
114            Self::EncryptionControlMode => (vpc.encryption_control_mode.clone(), Color::White),
115            Self::BlockPublicAccess => {
116                match vpc.block_public_access.to_ascii_lowercase().as_str() {
117                    "off" => ("❌ Off".into(), Color::White),
118                    "block-ingress" => ("✅ Block ingress".into(), Color::Green),
119                    "block-bidirectional" => ("✅ Block bidirectional".into(), Color::Green),
120                    _ => (vpc.block_public_access.clone(), Color::White),
121                }
122            }
123            Self::Ipv4Cidrs => (vpc.ipv4_cidrs.clone(), Color::White),
124            Self::Ipv6Cidrs => (vpc.ipv6_cidrs.clone(), Color::White),
125            Self::DhcpOptions => (vpc.dhcp_options_id.clone(), Color::White),
126            Self::MainRouteTable => (vpc.main_route_table.clone(), Color::White),
127            Self::MainNetworkAcl => (vpc.main_network_acl.clone(), Color::White),
128            Self::Tenancy => (vpc.tenancy.clone(), Color::White),
129            Self::Owner => (vpc.owner_id.clone(), Color::White),
130        };
131        if value.is_empty() {
132            ("-".into(), Style::default().fg(Color::White))
133        } else if value == "Unavailable" {
134            (value, Style::default().fg(Color::Yellow))
135        } else {
136            (value, Style::default().fg(color))
137        }
138    }
139    pub fn into_table_column(self) -> Box<dyn TableColumn<Vpc>> {
140        Box::new(VpcColumn {
141            column: self,
142            label: self.name(),
143        })
144    }
145}
146
147struct VpcColumn {
148    column: Column,
149    label: String,
150}
151impl TableColumn<Vpc> for VpcColumn {
152    fn name(&self) -> &str {
153        &self.label
154    }
155    fn width(&self) -> u16 {
156        self.label
157            .chars()
158            .count()
159            .max(self.column.definition().2)
160            .min(u16::MAX as usize) as u16
161    }
162    fn fit_content(&self) -> bool {
163        true
164    }
165
166    fn render(&self, item: &Vpc) -> (String, Style) {
167        self.column.render(item)
168    }
169}