1use crate::common::{format_iso_timestamp, translate_column, ColumnId, UTC_TIMESTAMP_WIDTH};
2use crate::ui::table::Column as TableColumn;
3use ratatui::prelude::*;
4use rusticity_core::fsx::FsxFileSystem as FileSystem;
5use std::collections::HashMap;
6
7pub fn init(i18n: &mut HashMap<String, String>) {
8 for col in Column::all() {
9 i18n.entry(col.id().to_string())
10 .or_insert_with(|| col.default_name().to_string());
11 }
12}
13
14#[derive(Debug, Clone, Copy, PartialEq)]
15pub enum Column {
16 Name,
17 FileSystemId,
18 FileSystemType,
19 Status,
20 DeploymentType,
21 StorageClass,
22 StorageCapacity,
23 ThroughputCapacity,
24 ProvisionedIops,
25 CreationTime,
26 LustreVersion,
27 Vpc,
28 KmsKeyId,
29}
30
31impl Column {
32 const ID_NAME: &'static str = "column.fsx.fs.name";
33 const ID_FILE_SYSTEM_ID: &'static str = "column.fsx.fs.file_system_id";
34 const ID_FILE_SYSTEM_TYPE: &'static str = "column.fsx.fs.file_system_type";
35 const ID_STATUS: &'static str = "column.fsx.fs.status";
36 const ID_DEPLOYMENT_TYPE: &'static str = "column.fsx.fs.deployment_type";
37 const ID_STORAGE_CLASS: &'static str = "column.fsx.fs.storage_class";
38 const ID_STORAGE_CAPACITY: &'static str = "column.fsx.fs.storage_capacity";
39 const ID_THROUGHPUT_CAPACITY: &'static str = "column.fsx.fs.throughput_capacity";
40 const ID_PROVISIONED_IOPS: &'static str = "column.fsx.fs.provisioned_iops";
41 const ID_CREATION_TIME: &'static str = "column.fsx.fs.creation_time";
42 const ID_LUSTRE_VERSION: &'static str = "column.fsx.fs.lustre_version";
43 const ID_VPC: &'static str = "column.fsx.fs.vpc";
44 const ID_KMS_KEY_ID: &'static str = "column.fsx.fs.kms_key_id";
45
46 pub const fn id(&self) -> &'static str {
47 match self {
48 Column::Name => Self::ID_NAME,
49 Column::FileSystemId => Self::ID_FILE_SYSTEM_ID,
50 Column::FileSystemType => Self::ID_FILE_SYSTEM_TYPE,
51 Column::Status => Self::ID_STATUS,
52 Column::DeploymentType => Self::ID_DEPLOYMENT_TYPE,
53 Column::StorageClass => Self::ID_STORAGE_CLASS,
54 Column::StorageCapacity => Self::ID_STORAGE_CAPACITY,
55 Column::ThroughputCapacity => Self::ID_THROUGHPUT_CAPACITY,
56 Column::ProvisionedIops => Self::ID_PROVISIONED_IOPS,
57 Column::CreationTime => Self::ID_CREATION_TIME,
58 Column::LustreVersion => Self::ID_LUSTRE_VERSION,
59 Column::Vpc => Self::ID_VPC,
60 Column::KmsKeyId => Self::ID_KMS_KEY_ID,
61 }
62 }
63
64 pub const fn default_name(&self) -> &'static str {
65 match self {
66 Column::Name => "File system name",
67 Column::FileSystemId => "File system ID",
68 Column::FileSystemType => "File system type",
69 Column::Status => "Status",
70 Column::DeploymentType => "Deployment type",
71 Column::StorageClass => "Storage class",
72 Column::StorageCapacity => "Storage capacity",
73 Column::ThroughputCapacity => "Throughput capacity",
74 Column::ProvisionedIops => "Provisioned IOPS",
75 Column::CreationTime => "Creation time",
76 Column::LustreVersion => "File system Lustre version",
77 Column::Vpc => "VPC",
78 Column::KmsKeyId => "KMS key ID",
79 }
80 }
81
82 pub const fn all() -> [Column; 13] {
83 [
84 Column::Name,
85 Column::FileSystemId,
86 Column::FileSystemType,
87 Column::Status,
88 Column::DeploymentType,
89 Column::StorageClass,
90 Column::StorageCapacity,
91 Column::ThroughputCapacity,
92 Column::ProvisionedIops,
93 Column::CreationTime,
94 Column::LustreVersion,
95 Column::Vpc,
96 Column::KmsKeyId,
97 ]
98 }
99
100 pub fn ids() -> Vec<ColumnId> {
101 Self::all().iter().map(|c| c.id()).collect()
102 }
103
104 pub fn default_visible_ids() -> Vec<ColumnId> {
107 Self::all()
108 .iter()
109 .filter(|c| {
110 !matches!(
111 c,
112 Column::ProvisionedIops
113 | Column::LustreVersion
114 | Column::Vpc
115 | Column::KmsKeyId
116 )
117 })
118 .map(|c| c.id())
119 .collect()
120 }
121
122 pub fn from_id(id: &str) -> Option<Self> {
123 match id {
124 Self::ID_NAME => Some(Column::Name),
125 Self::ID_FILE_SYSTEM_ID => Some(Column::FileSystemId),
126 Self::ID_FILE_SYSTEM_TYPE => Some(Column::FileSystemType),
127 Self::ID_STATUS => Some(Column::Status),
128 Self::ID_DEPLOYMENT_TYPE => Some(Column::DeploymentType),
129 Self::ID_STORAGE_CLASS => Some(Column::StorageClass),
130 Self::ID_STORAGE_CAPACITY => Some(Column::StorageCapacity),
131 Self::ID_THROUGHPUT_CAPACITY => Some(Column::ThroughputCapacity),
132 Self::ID_PROVISIONED_IOPS => Some(Column::ProvisionedIops),
133 Self::ID_CREATION_TIME => Some(Column::CreationTime),
134 Self::ID_LUSTRE_VERSION => Some(Column::LustreVersion),
135 Self::ID_VPC => Some(Column::Vpc),
136 Self::ID_KMS_KEY_ID => Some(Column::KmsKeyId),
137 _ => None,
138 }
139 }
140
141 pub fn name(&self) -> String {
142 translate_column(self.id(), self.default_name())
143 }
144}
145
146impl TableColumn<FileSystem> for Column {
147 fn name(&self) -> &str {
148 Box::leak(translate_column(self.id(), self.default_name()).into_boxed_str())
149 }
150
151 fn width(&self) -> u16 {
152 let translated = translate_column(self.id(), self.default_name());
153 translated.len().max(match self {
154 Column::Name => 24,
155 Column::FileSystemId => 21,
156 Column::FileSystemType => 16,
157 Column::Status => 14,
158 Column::DeploymentType => 18,
159 Column::StorageClass => 13,
160 Column::StorageCapacity => 16,
161 Column::ThroughputCapacity => 19,
162 Column::ProvisionedIops => 16,
163 Column::CreationTime => UTC_TIMESTAMP_WIDTH as usize,
164 Column::LustreVersion => 26,
165 Column::Vpc => 21,
166 Column::KmsKeyId => 30,
167 }) as u16
168 }
169
170 fn render(&self, item: &FileSystem) -> (String, Style) {
171 match self {
172 Column::Name => (item.name.clone(), Style::default()),
173 Column::FileSystemId => (item.file_system_id.clone(), Style::default()),
174 Column::FileSystemType => (item.file_system_type.clone(), Style::default()),
175 Column::Status => {
176 let (text, color) = format_status(&item.status);
177 (text.to_string(), Style::default().fg(color))
178 }
179 Column::DeploymentType => (
180 crate::ui::fsx::format_deployment_type(&item.deployment_type),
181 Style::default(),
182 ),
183 Column::StorageClass => (
184 crate::ui::fsx::format_storage_class(&item.storage_class),
185 Style::default(),
186 ),
187 Column::StorageCapacity => (item.storage_capacity.clone(), Style::default()),
188 Column::ThroughputCapacity => (item.throughput_capacity.clone(), Style::default()),
189 Column::ProvisionedIops => (item.provisioned_iops.clone(), Style::default()),
190 Column::CreationTime => (format_iso_timestamp(&item.creation_time), Style::default()),
191 Column::LustreVersion => (item.lustre_version.clone(), Style::default()),
192 Column::Vpc => (item.vpc_id.clone(), Style::default()),
193 Column::KmsKeyId => (item.kms_key_id.clone(), Style::default()),
194 }
195 }
196}
197
198fn format_status(status: &str) -> (&'static str, Color) {
199 match status {
200 "AVAILABLE" | "Available" | "available" => ("✅ Available", Color::Green),
201 "CREATING" | "Creating" | "creating" => ("⏳ Creating", Color::Yellow),
202 "UPDATING" | "Updating" | "updating" => ("🔄 Updating", Color::Yellow),
203 "DELETING" | "Deleting" | "deleting" => ("🗑 Deleting", Color::Red),
204 "FAILED" | "Failed" | "failed" => ("⚠ Failed", Color::Red),
205 "MISCONFIGURED" | "Misconfigured" | "MISCONFIGURED_UNAVAILABLE" => {
206 ("⚠ Misconfigured", Color::Red)
207 }
208 _ => ("Unknown", Color::White),
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn test_column_ids_have_prefix() {
218 for col in Column::all() {
219 assert!(col.id().starts_with("column.fsx.fs."));
220 }
221 }
222
223 #[test]
224 fn test_from_id_roundtrip() {
225 for col in Column::all() {
226 assert_eq!(Column::from_id(col.id()), Some(col));
227 }
228 }
229
230 #[test]
231 fn test_all_thirteen_columns() {
232 assert_eq!(Column::all().len(), 13);
233 }
234
235 #[test]
236 fn test_default_visible_hides_iops_lustre_vpc_kms() {
237 let visible = Column::default_visible_ids();
238 assert!(!visible.contains(&Column::ProvisionedIops.id()));
239 assert!(!visible.contains(&Column::LustreVersion.id()));
240 assert!(!visible.contains(&Column::Vpc.id()));
241 assert!(!visible.contains(&Column::KmsKeyId.id()));
242 assert_eq!(visible.len(), 9);
244 assert!(visible.contains(&Column::Name.id()));
245 assert!(visible.contains(&Column::Status.id()));
246 assert!(visible.contains(&Column::StorageCapacity.id()));
247 assert!(visible.contains(&Column::CreationTime.id()));
248 }
249
250 #[test]
251 fn test_columns_reuse_summary_formatters() {
252 let fs = FileSystem {
254 storage_class: "Ssd".to_string(),
255 deployment_type: "Persistent2".to_string(),
256 ..Default::default()
257 };
258
259 let (sc, _) = TableColumn::render(&Column::StorageClass, &fs);
260 assert_eq!(sc, "SSD");
261 let (dt, _) = TableColumn::render(&Column::DeploymentType, &fs);
262 assert_eq!(dt, "Persistent 2");
263 }
264}