Skip to content

Commit 15afe22

Browse files
Update main.rs
1 parent 5aa8b94 commit 15afe22

File tree

1 file changed

+155
-74
lines changed

1 file changed

+155
-74
lines changed

src/main.rs

Lines changed: 155 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
use std::io::{self, Write};
1+
use std::io::{self, BufRead, BufReader, Write};
22
use std::process::{Command, Stdio};
3-
use std::thread;
3+
use std::sync::{Arc, Mutex};
4+
use std::thread::{self, JoinHandle};
45
use std::time::Duration;
56
use colored::*;
67
use indicatif::{ProgressBar, ProgressStyle, MultiProgress};
@@ -11,15 +12,16 @@ use crossterm::{
1112
};
1213

1314
// Constants for styling
14-
const HEADER_WIDTH: usize = 60;
15-
const SPINNER_TICK_CHARS: &str = "⣾⣷⣯⣟⡿⢿⣻⣽";
16-
const PROGRESS_BAR_CHARS: &str = "█▉▊▋▌▍▎▏ ";
15+
const HEADER_WIDTH: usize = 80;
16+
const SPINNER_TICK_CHARS: &str = "⠁⠂⠄⠈⠐⠠⢀⡀";
17+
const PROGRESS_BAR_CHARS: &str = "█▓▒░ ";
1718

1819
// Structure to hold command details
1920
struct CommandInfo {
2021
name: &'static str,
2122
cmd: &'static str,
2223
color: Color,
24+
list_cmd: Option<&'static str>,
2325
}
2426

2527
// Structure to manage update sections
@@ -43,23 +45,27 @@ fn main() -> io::Result<()> {
4345
commands: vec![
4446
CommandInfo {
4547
name: "APT Update",
46-
cmd: "sudo /usr/lib/HackerOS/apt update",
48+
cmd: "sudo apt update",
4749
color: Color::BrightMagenta,
50+
list_cmd: None,
4851
},
4952
CommandInfo {
5053
name: "APT Upgrade",
51-
cmd: "sudo /usr/lib/HackerOS/apt upgrade -y",
54+
cmd: "sudo apt upgrade -y",
5255
color: Color::BrightMagenta,
56+
list_cmd: Some("apt list --upgradable"),
5357
},
5458
CommandInfo {
5559
name: "APT Autoremove",
56-
cmd: "sudo /usr/lib/HackerOS/apt autoremove -y",
60+
cmd: "sudo apt autoremove -y",
5761
color: Color::BrightMagenta,
62+
list_cmd: None,
5863
},
5964
CommandInfo {
6065
name: "APT Autoclean",
61-
cmd: "sudo /usr/lib/HackerOS/apt autoclean",
66+
cmd: "sudo apt autoclean",
6267
color: Color::BrightMagenta,
68+
list_cmd: None,
6369
},
6470
],
6571
},
@@ -70,6 +76,7 @@ fn main() -> io::Result<()> {
7076
name: "Flatpak Update",
7177
cmd: "flatpak update -y",
7278
color: Color::BrightYellow,
79+
list_cmd: Some("flatpak remote-ls --updates flathub"),
7380
},
7481
],
7582
},
@@ -80,6 +87,7 @@ fn main() -> io::Result<()> {
8087
name: "Snap Refresh",
8188
cmd: "sudo snap refresh",
8289
color: Color::BrightBlue,
90+
list_cmd: Some("snap refresh --list"),
8391
},
8492
],
8593
},
@@ -90,11 +98,13 @@ fn main() -> io::Result<()> {
9098
name: "Firmware Refresh",
9199
cmd: "sudo fwupdmgr refresh",
92100
color: Color::BrightGreen,
101+
list_cmd: None,
93102
},
94103
CommandInfo {
95104
name: "Firmware Update",
96105
cmd: "sudo fwupdmgr update",
97106
color: Color::BrightGreen,
107+
list_cmd: Some("fwupdmgr get-updates"),
98108
},
99109
],
100110
},
@@ -105,6 +115,7 @@ fn main() -> io::Result<()> {
105115
name: "HackerOS Script",
106116
cmd: "/usr/share/HackerOS/Scripts/Bin/Update-usrshare.sh",
107117
color: Color::Magenta,
118+
list_cmd: None,
108119
},
109120
],
110121
},
@@ -116,59 +127,137 @@ fn main() -> io::Result<()> {
116127
// Process each update section
117128
for section in update_sections {
118129
print_section_header(&section.name);
119-
let total_steps = section.commands.len() as u64 * 100;
130+
let total_steps = section.commands.len() as u64;
120131
let pb = multi_pb.add(ProgressBar::new(total_steps));
121132
pb.set_style(
122133
ProgressStyle::with_template(
123-
"{prefix:.bold.dim} {spinner:.cyan/blue} [{bar:40.cyan/blue}] {percent}% | {msg} | ETA: {eta_precise}"
134+
"{prefix:.bold.dim} {spinner:.cyan/blue} [{wide_bar:.cyan/blue}] {pos}/{len} | {msg:.white.bold} | ETA: {eta}"
124135
)
125136
.unwrap()
126137
.progress_chars(PROGRESS_BAR_CHARS)
127138
.tick_chars(SPINNER_TICK_CHARS),
128139
);
129-
pb.set_prefix(format!("{} ", section.name));
140+
pb.set_prefix(format!("{:<30}", section.name.bright_cyan()));
130141
pb.enable_steady_tick(Duration::from_millis(50));
131142

132143
for cmd_info in section.commands {
133-
pb.set_message(format!("{}", cmd_info.name.bright_white().bold()));
134-
// Simulate progress
135-
for _ in 0..100 {
136-
pb.inc(1);
137-
thread::sleep(Duration::from_millis(20));
144+
pb.set_message(format!("{}", cmd_info.name));
145+
if let Some(list_cmd) = cmd_info.list_cmd {
146+
let list_spinner = multi_pb.add(ProgressBar::new_spinner());
147+
list_spinner.set_style(
148+
ProgressStyle::with_template("{spinner:.green} {msg:.white}")
149+
.unwrap()
150+
.tick_chars(SPINNER_TICK_CHARS),
151+
);
152+
list_spinner.set_message(format!("Listing updates for {}...", cmd_info.name));
153+
list_spinner.enable_steady_tick(Duration::from_millis(40));
154+
155+
let list_output = Command::new("sh")
156+
.arg("-c")
157+
.arg(list_cmd)
158+
.stdout(Stdio::piped())
159+
.stderr(Stdio::piped())
160+
.output();
161+
162+
list_spinner.finish_and_clear();
163+
match list_output {
164+
Ok(o) => {
165+
let list_stdout = String::from_utf8_lossy(&o.stdout).to_string();
166+
let list_stderr = String::from_utf8_lossy(&o.stderr).to_string();
167+
if !list_stdout.trim().is_empty() && o.status.success() {
168+
println!(
169+
"{}",
170+
format!("Updates available for {}:", cmd_info.name).bold().color(cmd_info.color)
171+
);
172+
let mut count = 0;
173+
for line in list_stdout.lines().filter(|l| !l.trim().is_empty() && !l.contains("Listing...") && !l.contains("All snaps up to date.")) {
174+
println!(" {:2}. {}", count + 1, line.bright_white());
175+
count += 1;
176+
}
177+
if count == 0 {
178+
println!("{}", " None".bright_white());
179+
}
180+
} else if !list_stderr.is_empty() {
181+
println!("{}", format!("Error listing updates: {}", list_stderr).bright_red());
182+
} else {
183+
println!(
184+
"{}",
185+
format!("No updates available for {}.", cmd_info.name).bright_green()
186+
);
187+
}
188+
}
189+
Err(e) => {
190+
println!("{}", format!("Failed to list updates: {}", e).bright_red());
191+
}
192+
}
193+
println!();
138194
}
195+
139196
let spinner = multi_pb.add(ProgressBar::new_spinner());
140197
spinner.set_style(
141-
ProgressStyle::with_template("{spinner:.green} {msg}")
198+
ProgressStyle::with_template("{spinner:.green} {msg:.white}")
142199
.unwrap()
143200
.tick_chars(SPINNER_TICK_CHARS),
144201
);
145202
spinner.set_message(format!("Executing: {}", cmd_info.name));
146203
spinner.enable_steady_tick(Duration::from_millis(40));
147204

148-
let output = Command::new("sh")
149-
.arg("-c")
150-
.arg(cmd_info.cmd)
151-
.stdout(Stdio::piped())
152-
.stderr(Stdio::piped())
153-
.output();
205+
// Spawn the command
206+
let mut child = Command::new("sh")
207+
.arg("-c")
208+
.arg(cmd_info.cmd)
209+
.stdout(Stdio::piped())
210+
.stderr(Stdio::piped())
211+
.spawn()?;
154212

155-
spinner.finish_and_clear();
156-
match output {
157-
Ok(output) => {
158-
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
159-
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
160-
let success = output.status.success();
161-
logs.push((cmd_info.name.to_string(), stdout.clone(), true));
162-
if !stderr.is_empty() {
163-
logs.push((cmd_info.name.to_string(), stderr.clone(), false));
164-
}
165-
print_command_result(&cmd_info.name, success, cmd_info.color);
213+
let stdout_reader = BufReader::new(child.stdout.take().unwrap());
214+
let stderr_reader = BufReader::new(child.stderr.take().unwrap());
215+
216+
let stdout_lines = Arc::new(Mutex::new(Vec::<String>::new()));
217+
let stderr_lines = Arc::new(Mutex::new(Vec::<String>::new()));
218+
219+
let stdout_lines_clone = Arc::clone(&stdout_lines);
220+
let stderr_lines_clone = Arc::clone(&stderr_lines);
221+
let cmd_color = cmd_info.color;
222+
223+
// Threads to read stdout and stderr
224+
let stdout_handle: JoinHandle<()> = thread::spawn(move || {
225+
for line in stdout_reader.lines().flatten() {
226+
println!("{}", line.color(cmd_color));
227+
stdout_lines_clone.lock().unwrap().push(line);
166228
}
167-
Err(e) => {
168-
logs.push((cmd_info.name.to_string(), format!("Failed: {}", e), false));
169-
print_command_result(&cmd_info.name, false, cmd_info.color);
229+
});
230+
231+
let stderr_handle: JoinHandle<()> = thread::spawn(move || {
232+
for line in stderr_reader.lines().flatten() {
233+
println!("{}", line.bright_red());
234+
stderr_lines_clone.lock().unwrap().push(line);
170235
}
236+
});
237+
238+
// Wait for threads to finish
239+
stdout_handle.join().unwrap();
240+
stderr_handle.join().unwrap();
241+
242+
// Wait for child and get status
243+
let status = child.wait()?;
244+
let success = status.success();
245+
246+
let stdout = stdout_lines.lock().unwrap().join("\n");
247+
let stderr = stderr_lines.lock().unwrap().join("\n");
248+
249+
spinner.finish_with_message(format!(
250+
"{}: {}",
251+
cmd_info.name,
252+
if success { "Completed".bright_green().bold() } else { "Failed".bright_red().bold() }
253+
));
254+
println!();
255+
256+
logs.push((cmd_info.name.to_string(), stdout.clone(), true));
257+
if !stderr.is_empty() {
258+
logs.push((cmd_info.name.to_string(), stderr.clone(), false));
171259
}
260+
pb.inc(1);
172261
}
173262
pb.finish_with_message(format!("{} completed", section.name.bright_green().bold()));
174263
println!();
@@ -225,66 +314,58 @@ fn main() -> io::Result<()> {
225314

226315
// Helper functions
227316
fn print_header() {
228-
let title = "HackerOS Update Utility";
229-
let _padding = (HEADER_WIDTH - title.len()) / 2; // Unused but kept for potential future use
230-
println!("{}", "═".repeat(HEADER_WIDTH).bright_green().bold());
231-
println!("{}", format!("║{:^width$}║", title.bright_cyan().bold(), width = HEADER_WIDTH - 2).on_bright_black());
232-
println!("{}", "═".repeat(HEADER_WIDTH).bright_green().bold());
233-
println!("{}", "Initializing updates...".bright_blue().italic().bold());
317+
let title = "HackerOS Update Utility v0.0.1";
318+
println!("{}", "┏".to_string() + &"━".repeat(HEADER_WIDTH - 2) + &"┓".bright_green().bold());
319+
println!("{}", format!("┃{:^width$}┃", title.bright_cyan().bold(), width = HEADER_WIDTH - 2).on_bright_black());
320+
println!("{}", "┗".to_string() + &"━".repeat(HEADER_WIDTH - 2) + &"┛".bright_green().bold());
321+
println!("{}", format!("{:^width$}", "Initializing updates...".bright_blue().italic().bold(), width = HEADER_WIDTH));
234322
println!();
235323
}
236324

237325
fn print_section_header(name: &str) {
238326
let padding = (HEADER_WIDTH - name.len() - 4) / 2;
239327
println!(
240328
"{}",
241-
format!("{} {} {}{}", "".repeat(padding), name, "".repeat(padding), if (name.len() + 4) % 2 == 1 { "" } else { "" })
242-
.white().bold().on_color(get_section_color(name))
329+
format!("{} {} {}{}", "".repeat(padding), name.bright_white().bold(), "".repeat(padding), if (name.len() + 4) % 2 == 1 { "" } else { "" })
330+
.on_color(get_section_color(name))
243331
);
244332
println!();
245333
}
246334

247-
fn print_command_result(name: &str, success: bool, color: Color) {
248-
let status = if success { "Completed" } else { "Failed" };
249-
let _status_color = if success { Color::BrightGreen } else { Color::BrightRed }; // Unused but kept for potential future use
250-
println!(
251-
"{}",
252-
format!("╠═ {} {} ═╝", status, name).white().bold().on_color(color)
253-
);
254-
}
255-
256335
fn print_menu() {
257-
println!("{}", format!("{}", "╒".to_string() + &"═".repeat(HEADER_WIDTH - 2) + &"╕".bright_cyan().bold()));
258-
println!("{}", format!("│{:^width$}│", "Update Process Completed", width = HEADER_WIDTH - 2).white().bold().on_bright_black());
259-
println!("{}", format!("{}", "├".to_string() + &"─".repeat(HEADER_WIDTH - 2) + &"┤".bright_cyan().bold()));
260-
println!("{}", format!("│{:^width$}│", "(E)xit (S)hutdown (R)eboot", width = HEADER_WIDTH - 2).bright_yellow().bold());
261-
println!("{}", format!("│{:^width$}│", "(L)og Out (T)ry Again (H) Show Logs", width = HEADER_WIDTH - 2).bright_yellow().bold());
262-
println!("{}", format!("{}", "╘".to_string() + &"═".repeat(HEADER_WIDTH - 2) + &"╛".bright_cyan().bold()));
263-
println!("{}", "Select an option:".white().italic().bold());
336+
println!("{}", format!("{}", "┏".to_string() + &"━".repeat(HEADER_WIDTH - 2) + &"┓".bright_cyan().bold()));
337+
println!("{}", format!("┃{:^width$}┃", "Update Process Completed".bright_green().bold(), width = HEADER_WIDTH - 2).on_bright_black());
338+
println!("{}", format!("{}", "┣".to_string() + &"━".repeat(HEADER_WIDTH - 2) + &"┫".bright_cyan().bold()));
339+
println!("{}", format!("┃{:^width$}┃", "Choose an action:", width = HEADER_WIDTH - 2).bright_white().bold());
340+
println!("{}", format!("┃{:^width$}┃", "(E)xit (S)hutdown (R)eboot", width = HEADER_WIDTH - 2).bright_yellow());
341+
println!("{}", format!("┃{:^width$}┃", "(L)og Out (T)ry Again (H) Show Logs", width = HEADER_WIDTH - 2).bright_yellow());
342+
println!("{}", format!("{}", "┗".to_string() + &"━".repeat(HEADER_WIDTH - 2) + &"┛".bright_cyan().bold()));
343+
println!("{}", format!("{:^width$}", "Select an option:".white().italic().bold(), width = HEADER_WIDTH));
264344
}
265345

266346
fn print_logs(logs: &[(String, String, bool)]) {
267-
println!("{}", format!("{}", "".to_string() + &"".repeat(HEADER_WIDTH - 2) + &"".bright_cyan().bold()));
268-
println!("{}", format!("{:^width$}", "Update Logs", width = HEADER_WIDTH - 2).white().bold().on_bright_cyan());
269-
println!("{}", format!("{}", "".to_string() + &"".repeat(HEADER_WIDTH - 2) + &"".bright_cyan().bold()));
347+
println!("{}", format!("{}", "".to_string() + &"".repeat(HEADER_WIDTH - 2) + &"".bright_cyan().bold()));
348+
println!("{}", format!("{:^width$}", "Update Logs", width = HEADER_WIDTH - 2).white().bold().on_bright_cyan());
349+
println!("{}", format!("{}", "".to_string() + &"".repeat(HEADER_WIDTH - 2) + &"".bright_cyan().bold()));
270350
for (name, log, is_stdout) in logs {
271351
let log_type = if *is_stdout { "Output" } else { "Error" };
272-
let log_color = if *is_stdout { Color::White } else { Color::BrightRed };
273-
let max_len = HEADER_WIDTH - 10;
274-
let truncated_log = if log.len() > max_len { &log[..max_len] } else { log };
275-
println!(
276-
"{}",
277-
format!("│ {}: {} {}", log_type, name, truncated_log).color(log_color).on_bright_black()
278-
);
352+
let log_color = if *is_stdout { Color::BrightWhite } else { Color::BrightRed };
353+
if !log.trim().is_empty() {
354+
println!("{}", format!("┃ {} for {}:", log_type, name).bold().color(log_color));
355+
for line in log.lines() {
356+
println!("{}", format!("┃ {}", line).color(log_color));
357+
}
358+
println!("{}", format!("{}", "┣".to_string() + &"━".repeat(HEADER_WIDTH - 2) + &"┫".bright_black()));
359+
}
279360
}
280-
println!("{}", format!("{}", "".to_string() + &"".repeat(HEADER_WIDTH - 2) + &"".bright_cyan().bold()));
361+
println!("{}", format!("{}", "".to_string() + &"".repeat(HEADER_WIDTH - 2) + &"".bright_cyan().bold()));
281362
println!();
282363
}
283364

284365
fn print_action(message: &str, color: Color) {
285366
println!(
286367
"{}",
287-
format!("╠═ {} ═╝", message).white().bold().on_color(color)
368+
format!("┣━ {} ━┫", message).white().bold().on_color(color)
288369
);
289370
thread::sleep(Duration::from_millis(200));
290371
}

0 commit comments

Comments
 (0)