1use anyhow::{Context, Result};
2use colored::*;
3use std::io::{self, Write};
4use std::process::Command;
5#[cfg(unix)]
6use users::get_effective_uid;
7
8#[cfg(unix)]
10pub fn check_root() -> bool {
11 get_effective_uid() == 0
12}
13
14#[cfg(not(unix))]
15pub fn check_root() -> bool {
16 false
17}
18
19#[cfg(unix)]
22pub fn elevate_if_needed() -> Result<bool> {
23 if check_root() {
24 return Ok(true);
25 }
26
27 print_warning("System cleaners require root privileges.");
28 println!("You can either:");
29 println!(" 1. Run this command again with sudo");
30 println!(" 2. Enter your password to elevate now");
31 print!("\nWould you like to elevate now? [Y/n]: ");
32 io::stdout().flush()?;
33
34 let mut response = String::new();
35 io::stdin().read_line(&mut response)?;
36
37 match response.trim().to_lowercase().as_str() {
38 "n" | "no" => {
39 print_warning("Skipping system cleaners. Only user cleaners will run.");
40 Ok(false)
41 }
42 _ => {
43 print!("Authenticating... ");
45 io::stdout().flush()?;
46
47 let status = Command::new("sudo")
48 .args(["-v"])
49 .status()
50 .context("Failed to execute sudo")?;
51
52 if status.success() {
53 println!("{}", "✓ Authentication successful".green());
54 Ok(true)
55 } else {
56 print_error("Authentication failed. Skipping system cleaners.");
57 Ok(false)
58 }
59 }
60 }
61}
62
63#[cfg(not(unix))]
64pub fn elevate_if_needed() -> Result<bool> {
65 print_warning("System cleaners are only available on Unix-like systems.");
66 Ok(false)
67}
68
69#[cfg(unix)]
73pub fn execute_with_sudo(command: &str, args: &[&str]) -> Result<std::process::Output> {
74 use std::process::Stdio;
75
76 if check_root() {
77 Command::new(command)
79 .args(args)
80 .output()
81 .context(format!("Failed to execute command: {}", command))
82 } else {
83 let mut sudo_args = vec!["-n", command];
86 sudo_args.extend_from_slice(args);
87
88 Command::new("sudo")
89 .args(sudo_args)
90 .stdin(Stdio::null())
91 .output()
92 .context(format!("Failed to execute command with sudo: {}", command))
93 }
94}
95
96#[cfg(not(unix))]
97pub fn execute_with_sudo(command: &str, args: &[&str]) -> Result<std::process::Output> {
98 Command::new(command)
99 .args(args)
100 .output()
101 .context(format!("Failed to execute command: {}", command))
102}
103
104pub fn print_header(text: &str) {
106 let width = 60;
107 let padding = (width - text.len()) / 2;
108 let line = "=".repeat(width);
109
110 println!("\n{}", line.bright_blue());
111 println!(
112 "{}{}{}",
113 " ".repeat(padding),
114 text.bright_white().bold(),
115 " ".repeat(padding)
116 );
117 println!("{}\n", line.bright_blue());
118}
119
120pub fn print_success(message: &str) {
122 println!("{} {}", "✓".green().bold(), message);
123}
124
125pub fn print_warning(message: &str) {
127 println!("{} {}", "!".yellow().bold(), message);
128}
129
130pub fn print_error(message: &str) {
132 eprintln!("{} {}", "✗".red().bold(), message);
133}
134
135pub fn confirm(prompt: &str, default: bool) -> Result<bool> {
137 let yes_no = if default { "[Y/n]" } else { "[y/N]" };
138 print!("{} {} ", prompt, yes_no);
139 io::stdout().flush()?;
140
141 let mut response = String::new();
142 io::stdin().read_line(&mut response)?;
143
144 match response.trim().to_lowercase().as_str() {
145 "y" | "yes" => Ok(true),
146 "n" | "no" => Ok(false),
147 "" => Ok(default),
148 _ => {
149 print_warning("Invalid response. Please enter 'y' or 'n'.");
150 confirm(prompt, default)
151 }
152 }
153}
154
155pub fn format_size(bytes: u64) -> String {
157 const KB: u64 = 1024;
158 const MB: u64 = KB * 1024;
159 const GB: u64 = MB * 1024;
160
161 if bytes >= GB {
162 format!("{:.2} GB", bytes as f64 / GB as f64)
163 } else if bytes >= MB {
164 format!("{:.2} MB", bytes as f64 / MB as f64)
165 } else if bytes >= KB {
166 format!("{:.2} KB", bytes as f64 / KB as f64)
167 } else {
168 format!("{} bytes", bytes)
169 }
170}
171
172pub fn get_size(path: &str) -> Result<u64> {
174 let output = std::process::Command::new("du")
175 .args(["-sb", path])
176 .output()?;
177
178 if !output.status.success() {
179 return Ok(0);
180 }
181
182 let stdout = String::from_utf8_lossy(&output.stdout);
183 let parts: Vec<&str> = stdout.split_whitespace().collect();
184 if parts.is_empty() {
185 return Ok(0);
186 }
187
188 match parts[0].parse::<u64>() {
189 Ok(size) => Ok(size),
190 Err(_) => Ok(0),
191 }
192}