1use color_eyre::Result;
2use color_eyre::eyre::eyre;
3use comfy_table::{Attribute, Cell, Color, ContentArrangement, Table};
4use console::{Term, style};
5use envx_core::EnvVarManager;
6
7pub fn handle_list_command(
24 source: Option<&str>,
25 query: Option<&str>,
26 format: &str,
27 sort: &str,
28 names_only: bool,
29 limit: Option<usize>,
30 stats: bool,
31) -> Result<()> {
32 let mut manager = EnvVarManager::new();
33 manager.load_all()?;
34
35 let mut vars = if let Some(q) = &query {
37 manager.search(q)
38 } else if let Some(src) = source {
39 let source_filter = match src {
40 "system" => envx_core::EnvVarSource::System,
41 "user" => envx_core::EnvVarSource::User,
42 "process" => envx_core::EnvVarSource::Process,
43 "shell" => envx_core::EnvVarSource::Shell,
44 _ => return Err(eyre!("Invalid source: {}", src)),
45 };
46 manager.filter_by_source(&source_filter)
47 } else {
48 manager.list()
49 };
50
51 match sort {
53 "name" => vars.sort_by(|a, b| a.name.cmp(&b.name)),
54 "value" => vars.sort_by(|a, b| a.value.cmp(&b.value)),
55 "source" => vars.sort_by(|a, b| format!("{:?}", a.source).cmp(&format!("{:?}", b.source))),
56 _ => {}
57 }
58
59 let total_count = vars.len();
61 if let Some(lim) = limit {
62 vars.truncate(lim);
63 }
64
65 if stats || (format == "table" && !names_only) {
67 print_statistics(&manager, &vars, total_count, query, source);
68 }
69
70 if names_only {
72 for var in vars {
73 println!("{}", var.name);
74 }
75 return Ok(());
76 }
77
78 match format {
80 "json" => {
81 println!("{}", serde_json::to_string_pretty(&vars)?);
82 }
83 "simple" => {
84 for var in vars {
85 println!("{} = {}", style(&var.name).cyan(), var.value);
86 }
87 }
88 "compact" => {
89 for var in vars {
90 let source_str = format_source_compact(&var.source);
91 println!(
92 "{} {} = {}",
93 source_str,
94 style(&var.name).bright(),
95 style(truncate_value(&var.value, 60)).dim()
96 );
97 }
98 }
99 _ => {
100 print_table(vars, limit.is_some());
101 }
102 }
103
104 if let Some(lim) = limit {
106 if total_count > lim {
107 println!(
108 "\n{}",
109 style(format!(
110 "Showing {lim} of {total_count} total variables. Use --limit to see more."
111 ))
112 .yellow()
113 );
114 }
115 }
116
117 Ok(())
118}
119
120fn print_statistics(
121 manager: &EnvVarManager,
122 filtered_vars: &[&envx_core::EnvVar],
123 total_count: usize,
124 query: Option<&str>,
125 source: Option<&str>,
126) {
127 let _term = Term::stdout();
128
129 let system_count = manager.filter_by_source(&envx_core::EnvVarSource::System).len();
131 let user_count = manager.filter_by_source(&envx_core::EnvVarSource::User).len();
132 let process_count = manager.filter_by_source(&envx_core::EnvVarSource::Process).len();
133 let shell_count = manager.filter_by_source(&envx_core::EnvVarSource::Shell).len();
134
135 println!("{}", style("═".repeat(60)).blue().bold());
137 println!("{}", style("Environment Variables Summary").cyan().bold());
138 println!("{}", style("═".repeat(60)).blue().bold());
139
140 if query.is_some() || source.is_some() {
142 print!(" {} ", style("Filter:").yellow());
143 if let Some(q) = query {
144 print!("query='{}' ", style(q).green());
145 }
146 if let Some(s) = source {
147 print!("source={} ", style(s).green());
148 }
149 println!();
150 println!(
151 " {} {}/{} variables",
152 style("Showing:").yellow(),
153 style(filtered_vars.len()).green().bold(),
154 total_count
155 );
156 } else {
157 println!(
158 " {} {} variables",
159 style("Total:").yellow(),
160 style(total_count).green().bold()
161 );
162 }
163
164 println!();
165 println!(" {} By Source:", style("►").cyan());
166
167 let max_count = system_count.max(user_count).max(process_count).max(shell_count);
169 let bar_width = 30;
170
171 print_source_bar("System", system_count, max_count, bar_width, "red");
172 print_source_bar("User", user_count, max_count, bar_width, "yellow");
173 print_source_bar("Process", process_count, max_count, bar_width, "green");
174 print_source_bar("Shell", shell_count, max_count, bar_width, "cyan");
175
176 println!("{}", style("─".repeat(60)).blue());
177 println!();
178}
179
180fn print_source_bar(label: &str, count: usize, max: usize, width: usize, color: &str) {
181 let filled = if max > 0 { (count * width / max).max(1) } else { 0 };
182
183 let bar = "█".repeat(filled);
184 let empty = "░".repeat(width - filled);
185
186 let colored_bar = match color {
187 "red" => style(bar).red(),
188 "yellow" => style(bar).yellow(),
189 "green" => style(bar).green(),
190 "cyan" => style(bar).cyan(),
191 _ => style(bar).white(),
192 };
193
194 println!(
195 " {:10} {} {}{} {}",
196 style(label).bold(),
197 colored_bar,
198 style(empty).dim(),
199 style(format!(" {count:4}")).bold(),
200 style("vars").dim()
201 );
202}
203
204fn print_table(vars: Vec<&envx_core::EnvVar>, _is_limited: bool) {
205 if vars.is_empty() {
206 println!("{}", style("No environment variables found.").yellow());
207 }
208
209 let mut table = Table::new();
210
211 table
213 .set_content_arrangement(ContentArrangement::Dynamic)
214 .set_width(120)
215 .set_header(vec![
216 Cell::new("Source").add_attribute(Attribute::Bold).fg(Color::Cyan),
217 Cell::new("Name").add_attribute(Attribute::Bold).fg(Color::Cyan),
218 Cell::new("Value").add_attribute(Attribute::Bold).fg(Color::Cyan),
219 ]);
220
221 for var in vars {
223 let (source_str, source_color) = format_source(&var.source);
224 let truncated_value = truncate_value(&var.value, 50);
225
226 table.add_row(vec![
227 Cell::new(source_str).fg(source_color),
228 Cell::new(&var.name).fg(Color::White),
229 Cell::new(truncated_value).fg(Color::Grey),
230 ]);
231 }
232
233 println!("{table}");
234}
235
236fn format_source(source: &envx_core::EnvVarSource) -> (String, Color) {
237 match source {
238 envx_core::EnvVarSource::System => ("System".to_string(), Color::Red),
239 envx_core::EnvVarSource::User => ("User".to_string(), Color::Yellow),
240 envx_core::EnvVarSource::Process => ("Process".to_string(), Color::Green),
241 envx_core::EnvVarSource::Shell => ("Shell".to_string(), Color::Cyan),
242 envx_core::EnvVarSource::Application(app) => (format!("App:{app}"), Color::Magenta),
243 }
244}
245
246fn format_source_compact(source: &envx_core::EnvVarSource) -> console::StyledObject<String> {
247 match source {
248 envx_core::EnvVarSource::System => style("[SYS]".to_string()).red().bold(),
249 envx_core::EnvVarSource::User => style("[USR]".to_string()).yellow().bold(),
250 envx_core::EnvVarSource::Process => style("[PRC]".to_string()).green().bold(),
251 envx_core::EnvVarSource::Shell => style("[SHL]".to_string()).cyan().bold(),
252 envx_core::EnvVarSource::Application(app) => style(format!("[{}]", &app[..3.min(app.len())].to_uppercase()))
253 .magenta()
254 .bold(),
255 }
256}
257
258fn truncate_value(value: &str, max_len: usize) -> String {
259 if value.len() <= max_len {
260 value.to_string()
261 } else {
262 format!("{}...", &value[..max_len - 3])
263 }
264}