1use std::collections::HashMap;
8
9use serde::{Deserialize, Serialize};
10
11pub const CURRENT_TUI_CONFIG_VERSION: u32 = 3;
22
23#[derive(Debug, Clone, Default, Serialize, Deserialize)]
29pub struct TuiConfig {
30 #[serde(default)]
32 pub version: Option<u32>,
33
34 #[serde(default, skip_serializing_if = "Vec::is_empty")]
36 pub column_visibility: Vec<bool>,
37
38 #[serde(default, skip_serializing_if = "Vec::is_empty")]
40 pub column_order: Vec<String>,
41
42 #[serde(default, skip_serializing_if = "Option::is_none")]
44 pub refresh_interval_ms: Option<u64>,
45
46 #[serde(default, skip_serializing_if = "Option::is_none")]
48 pub transcript_poll_interval_ms: Option<u64>,
49
50 #[serde(default, skip_serializing_if = "Option::is_none")]
52 pub theme: Option<String>,
53
54 #[serde(default, skip_serializing_if = "Option::is_none")]
56 pub show_borders: Option<bool>,
57
58 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
61 pub profiles: HashMap<String, TuiColumnProfile>,
62
63 #[serde(default, skip_serializing_if = "Option::is_none")]
65 pub group_panel_enabled: Option<bool>,
66
67 #[serde(default, skip_serializing_if = "Option::is_none")]
69 pub animation_enabled: Option<bool>,
70
71 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub animation_duration_ms: Option<u64>,
74
75 #[serde(default, skip_serializing_if = "Option::is_none")]
77 pub sort_spec: Option<Vec<TuiSortSpec>>,
78
79 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub auto_enable_frameworks: Option<bool>,
82
83 #[serde(default, skip_serializing_if = "Option::is_none")]
85 pub terminal_spawn_method: Option<String>,
86}
87
88#[derive(Debug, Clone, Default, Serialize, Deserialize)]
90pub struct TuiColumnProfile {
91 #[serde(default)]
93 pub visibility: Vec<bool>,
94
95 #[serde(default)]
97 pub order: Vec<String>,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
102pub struct TuiSortSpec {
103 pub field: String,
105
106 pub order: String,
108}
109
110impl TuiConfig {
111 pub fn is_empty(&self) -> bool {
113 self.version.is_none()
114 && self.column_visibility.is_empty()
115 && self.column_order.is_empty()
116 && self.refresh_interval_ms.is_none()
117 && self.transcript_poll_interval_ms.is_none()
118 && self.theme.is_none()
119 && self.show_borders.is_none()
120 && self.profiles.is_empty()
121 && self.group_panel_enabled.is_none()
122 && self.animation_enabled.is_none()
123 && self.animation_duration_ms.is_none()
124 && self.sort_spec.is_none()
125 && self.auto_enable_frameworks.is_none()
126 && self.terminal_spawn_method.is_none()
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_tui_config_default_is_empty() {
136 let config = TuiConfig::default();
137 assert!(config.is_empty());
138 }
139
140 #[test]
141 fn test_tui_config_serde_roundtrip() {
142 let config = TuiConfig {
143 version: Some(CURRENT_TUI_CONFIG_VERSION),
144 column_visibility: vec![true, false, true],
145 column_order: vec!["pid".to_string(), "status".to_string(), "age".to_string()],
146 refresh_interval_ms: Some(1000),
147 transcript_poll_interval_ms: Some(5000),
148 theme: Some("dracula".to_string()),
149 show_borders: Some(true),
150 profiles: HashMap::new(),
151 group_panel_enabled: Some(true),
152 animation_enabled: Some(true),
153 animation_duration_ms: Some(220),
154 sort_spec: Some(vec![TuiSortSpec {
155 field: "cpu".to_string(),
156 order: "descending".to_string(),
157 }]),
158 auto_enable_frameworks: Some(false),
159 terminal_spawn_method: Some("new_mux_pane".to_string()),
160 };
161
162 let toml_str = toml::to_string_pretty(&config).unwrap();
163 let parsed: TuiConfig = toml::from_str(&toml_str).unwrap();
164
165 assert_eq!(parsed.version, config.version);
166 assert_eq!(parsed.column_visibility, config.column_visibility);
167 assert_eq!(parsed.column_order, config.column_order);
168 assert_eq!(parsed.theme, config.theme);
169 assert_eq!(parsed.refresh_interval_ms, config.refresh_interval_ms);
170 }
171
172 #[test]
173 fn test_tui_config_empty_fields_not_serialized() {
174 let config = TuiConfig {
175 theme: Some("default".to_string()),
176 ..Default::default()
177 };
178
179 let toml_str = toml::to_string_pretty(&config).unwrap();
180 assert!(!toml_str.contains("column_visibility"));
182 assert!(!toml_str.contains("profiles"));
183 assert!(toml_str.contains("theme"));
185 }
186}