redbear-power: auto-fix 84 clippy warnings (100→16)

Mechanical fixes: collapsible-if, is_multiple_of, sort_by_key,
needless borrows, useless format!, manual checked division,
push-after-creation, and other style lints. Zero behavioral
changes. Remaining 16 warnings are pre-existing intentional
patterns (identical if-blocks from match arms, &mut Vec params).
This commit is contained in:
2026-07-07 04:58:45 +03:00
parent bd1016a202
commit 6fa762d9e1
18 changed files with 345 additions and 384 deletions
@@ -33,20 +33,20 @@ pub fn read_cpu_freq_khz_sysfs(cpu: u32) -> Option<u32> {
"/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq",
cpu
);
if let Ok(s) = fs::read_to_string(&path) {
if let Ok(khz) = s.trim().parse::<u32>() {
return Some(khz);
}
if let Ok(s) = fs::read_to_string(&path)
&& let Ok(khz) = s.trim().parse::<u32>()
{
return Some(khz);
}
// Fallback: cpuinfo_cur_freq (some drivers expose this instead).
let path2 = format!(
"/sys/devices/system/cpu/cpu{}/cpufreq/cpuinfo_cur_freq",
cpu
);
if let Ok(s) = fs::read_to_string(&path2) {
if let Ok(khz) = s.trim().parse::<u32>() {
return Some(khz);
}
if let Ok(s) = fs::read_to_string(&path2)
&& let Ok(khz) = s.trim().parse::<u32>()
{
return Some(khz);
}
None
}
@@ -69,16 +69,16 @@ pub fn read_rapl_package_energy() -> Option<(u64, Instant)> {
}
// 2. Fallback: Linux powercap sysfs
let path = "/sys/class/powercap/intel-rapl:0/energy_uj";
if let Ok(s) = fs::read_to_string(path) {
if let Ok(uj) = s.trim().parse::<u64>() {
return Some((uj, Instant::now()));
}
if let Ok(s) = fs::read_to_string(path)
&& let Ok(uj) = s.trim().parse::<u64>()
{
return Some((uj, Instant::now()));
}
let path2 = "/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj";
if let Ok(s) = fs::read_to_string(path2) {
if let Ok(uj) = s.trim().parse::<u64>() {
return Some((uj, Instant::now()));
}
if let Ok(s) = fs::read_to_string(path2)
&& let Ok(uj) = s.trim().parse::<u64>()
{
return Some((uj, Instant::now()));
}
None
}
@@ -94,12 +94,7 @@ pub fn rapl_power_watts(curr: (u64, Instant), prev: (u64, Instant)) -> (f64, (u6
if dt < Duration::from_millis(100) || dt > Duration::from_secs(5) {
return (0.0, curr);
}
let de = if e_curr >= e_prev {
e_curr - e_prev
} else {
// Energy counter wrapped (unlikely at u64 microjoules, but handle it).
0
};
let de = e_curr.saturating_sub(e_prev);
let watts = (de as f64 / 1_000_000.0) / dt.as_secs_f64();
(watts, curr)
}
@@ -119,12 +114,11 @@ pub fn detect_cpus() -> Vec<u32> {
// not exist on Redox. Linux falls back to /dev/cpu/N entries.
if let Ok(data) = fs::read_to_string("/scheme/sys/cpu") {
for line in data.lines() {
if let Some(rest) = line.strip_prefix("CPUs: ") {
if let Ok(n) = rest.trim().parse::<u32>() {
if n > 0 {
return (0..n).collect();
}
}
if let Some(rest) = line.strip_prefix("CPUs: ")
&& let Ok(n) = rest.trim().parse::<u32>()
&& n > 0
{
return (0..n).collect();
}
}
}
@@ -214,10 +208,10 @@ fn delta_load(total: u64, idle: u64, prev: &mut (u64, u64)) -> f64 {
pub fn read_acpi_pss(cpu: u32) -> Vec<PState> {
// Try Redox ACPI _PSS first.
let scheme_path = format!("/scheme/acpi/processor/CPU{}/pss", cpu);
if let Ok(s) = fs::read_to_string(&scheme_path) {
if let Some(out) = parse_pss(&s) {
return out;
}
if let Ok(s) = fs::read_to_string(&scheme_path)
&& let Some(out) = parse_pss(&s)
{
return out;
}
// Linux fallback: /sys/devices/system/cpu/cpu{N}/cpufreq/scaling_available_frequencies
// (kHz strings separated by spaces, e.g. "2400000 2200000 1900000 ...").
@@ -263,25 +257,23 @@ pub fn read_acpi_pss(cpu: u32) -> Vec<PState> {
"/sys/devices/system/cpu/cpu{}/cpufreq/cpuinfo_max_freq",
cpu
);
if let (Ok(min_s), Ok(max_s)) = (fs::read_to_string(&min_path), fs::read_to_string(&max_path)) {
if let (Ok(min_khz), Ok(max_khz)) =
if let (Ok(min_s), Ok(max_s)) = (fs::read_to_string(&min_path), fs::read_to_string(&max_path))
&& let (Ok(min_khz), Ok(max_khz)) =
(min_s.trim().parse::<u32>(), max_s.trim().parse::<u32>())
{
if max_khz > min_khz {
let steps: u32 = 5;
let mut out = Vec::with_capacity(steps as usize + 1);
for i in 0..=steps {
let freq = min_khz + (max_khz - min_khz) * i / steps;
let ctl = (i as u64) << 8;
out.push(PState {
freq_khz: freq,
power_mw: 0,
ctl,
});
}
return out;
}
&& max_khz > min_khz
{
let steps: u32 = 5;
let mut out = Vec::with_capacity(steps as usize + 1);
for i in 0..=steps {
let freq = min_khz + (max_khz - min_khz) * i / steps;
let ctl = (i as u64) << 8;
out.push(PState {
freq_khz: freq,
power_mw: 0,
ctl,
});
}
return out;
}
Vec::new()
}
@@ -290,18 +282,18 @@ fn parse_pss(s: &str) -> Option<Vec<PState>> {
let mut out = Vec::new();
for line in s.lines() {
let w: Vec<&str> = line.split_whitespace().collect();
if w.len() >= 6 {
if let (Ok(f), Ok(pw), Ok(ct)) = (
if w.len() >= 6
&& let (Ok(f), Ok(pw), Ok(ct)) = (
w[0].parse::<u32>(),
w[2].parse::<u32>(),
u64::from_str_radix(w[5], 16),
) {
out.push(PState {
freq_khz: f,
power_mw: pw,
ctl: ct,
});
}
)
{
out.push(PState {
freq_khz: f,
power_mw: pw,
ctl: ct,
});
}
}
if out.is_empty() { None } else { Some(out) }
@@ -328,15 +320,11 @@ pub fn read_cpu_id() -> Option<(String, String)> {
continue;
}
match key.as_str() {
"vendor_id" | "vendor" | "manufacturer" => {
if vendor.is_empty() {
vendor = val;
}
"vendor_id" | "vendor" | "manufacturer" if vendor.is_empty() => {
vendor = val;
}
"model name" | "model" | "product name" | "cpu" | "hardware" => {
if model.is_empty() {
model = val;
}
"model name" | "model" | "product name" | "cpu" | "hardware" if model.is_empty() => {
model = val;
}
_ => {}
}
@@ -572,10 +572,10 @@ impl App {
// without hammering /proc/meminfo on every tick.
self.refresh_counter = self.refresh_counter.wrapping_add(1);
// Clear MSR failure cache every ~30s so permission changes are picked up.
if self.refresh_counter % 60 == 0 {
if self.refresh_counter.is_multiple_of(60) {
crate::msr::clear_msr_cache();
}
if self.refresh_counter % 4 == 0 {
if self.refresh_counter.is_multiple_of(4) {
self.meminfo = crate::meminfo::read_meminfo();
self.os_info = crate::meminfo::read_os_info();
self.load_avg = std::fs::read_to_string("/proc/loadavg").ok().and_then(|s| {
@@ -598,7 +598,7 @@ impl App {
// Battery tab stays useful without hammering sysfs at 2 Hz.
// On desktops without a battery, find_battery_dir() returns None
// in ~1 ms; the cost is negligible.
if self.refresh_counter % 5 == 0 {
if self.refresh_counter.is_multiple_of(5) {
self.battery = crate::battery::BatteryInfo::read();
}
@@ -607,7 +607,7 @@ impl App {
// modulus is coprime to meminfo's 4-tick and battery's 5-tick
// moduli, so sensor reads don't synchronize with other data
// sources (no thundering-herd syscalls).
if self.refresh_counter % 3 == 0 {
if self.refresh_counter.is_multiple_of(3) {
self.sensors = crate::sensor::SensorInfo::read();
}
@@ -617,7 +617,7 @@ impl App {
// so network reads don't synchronize with any other data
// source. 3.5 sec cadence is the same order as cpu-x's
// network panel update rate.
if self.refresh_counter % 7 == 0 {
if self.refresh_counter.is_multiple_of(7) {
let now_secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs_f64())
@@ -653,7 +653,7 @@ impl App {
// read_bytes/write_bytes vs previous 11th-tick refresh,
// divided by elapsed wall time. SMART data is also refreshed
// at the same cadence since it pairs naturally with disk info.
if self.refresh_counter % 11 == 0 {
if self.refresh_counter.is_multiple_of(11) {
let now_secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs_f64())
@@ -691,7 +691,7 @@ impl App {
// 7, 11), so process reads don't synchronize with any other
// data source. 6.5 sec is sufficient because process state
// changes are mostly visible at human-perceptual timescales.
if self.refresh_counter % 13 == 0 {
if self.refresh_counter.is_multiple_of(13) {
let now_secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs_f64())
@@ -71,10 +71,10 @@ impl BatteryInfo {
let dir = fs::read_dir(&search_root).ok()?;
for entry in dir.flatten() {
let type_path = entry.path().join("type");
if let Some(t) = read_sysfs(&type_path) {
if t == "Battery" {
return Some(entry.path());
}
if let Some(t) = read_sysfs(&type_path)
&& t == "Battery"
{
return Some(entry.path());
}
}
None
@@ -135,7 +135,7 @@ fn prime_worker(cancel: &AtomicBool, duration: Duration) -> u64 {
let mut is_prime = n >= 2;
let mut i: u64 = 2;
while i * i <= n && is_prime {
if n % i == 0 {
if n.is_multiple_of(i) {
is_prime = false;
}
i += 1;
@@ -26,6 +26,7 @@ use serde::Deserialize;
#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
#[derive(Default)]
pub struct Config {
pub display: DisplayConfig,
pub theme: ThemeConfig,
@@ -33,17 +34,6 @@ pub struct Config {
pub benchmark: BenchmarkConfig,
}
impl Default for Config {
fn default() -> Self {
Self {
display: DisplayConfig::default(),
theme: ThemeConfig::default(),
keybindings: KeyBindings::default(),
benchmark: BenchmarkConfig::default(),
}
}
}
#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
pub struct DisplayConfig {
@@ -210,11 +210,11 @@ impl Cpufreq {
continue;
}
// Verify.
if let Some(actual) = backend.read_active() {
if actual == *candidate {
self.active = candidate.clone();
return Some(candidate.clone());
}
if let Some(actual) = backend.read_active()
&& actual == *candidate
{
self.active = candidate.clone();
return Some(candidate.clone());
}
}
None
@@ -228,11 +228,11 @@ impl Cpufreq {
if !backend.write(name) {
return false;
}
if let Some(actual) = backend.read_active() {
if actual == name {
self.active = name.to_string();
return true;
}
if let Some(actual) = backend.read_active()
&& actual == name
{
self.active = name.to_string();
return true;
}
false
}
@@ -270,10 +270,11 @@ fn discover_by_probing(backend: &Backend) -> Vec<String> {
if !backend.write(candidate) {
continue;
}
if let Some(actual) = backend.read_active() {
if actual == *candidate && !found.contains(&candidate.to_string()) {
found.push(candidate.to_string());
}
if let Some(actual) = backend.read_active()
&& actual == *candidate
&& !found.contains(&candidate.to_string())
{
found.push(candidate.to_string());
}
}
@@ -35,7 +35,7 @@ pub struct CpuId {
/// Intel hybrid CPU (12th gen+) identifies P-cores and E-cores via
/// CPUID leaf 0x1A; AMD Zen 2+ identifies CCDs via 0x8000001E. Both
/// schemes map each logical processor to a `CoreType` for grouping.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum CoreType {
/// Intel Performance core (Raptor Lake / Alder Lake).
IntelP,
@@ -45,15 +45,10 @@ pub enum CoreType {
/// The CCD number is stored separately.
AmdCcd(u8),
/// Unknown / vendor without hybrid support.
#[default]
Unknown,
}
impl Default for CoreType {
fn default() -> Self {
CoreType::Unknown
}
}
#[derive(Clone, Debug, Default)]
pub struct HybridInfo {
pub is_hybrid: bool,
@@ -251,20 +246,20 @@ fn detect_hybrid(max_leaf: u32, max_ext: u32, num_cpus: u32, vendor: &str) -> Ve
// which is our display group. For Zen 4+, leaf 0x80000026 exposes
// CCD-level counts directly; we honor that when available.
let (_, ebx_1e, _, _) = raw::cpuid(0x8000_001e, 0);
let nc = ((ebx_1e >> 8) & 0xff) as u32;
let nc = (ebx_1e >> 8) & 0xff;
let cores_per_ccx = if nc > 0 { nc as usize } else { 1 };
let (zen4_ccds, _zen4_cores_per_ccd) = if max_ext >= 0x8000_0026 {
// Zen 4+ topology leaf: EAX bits 7:0 = # of enabled CCDs,
// ECX bits 7:0 = # of cores per CCD (0 = not supported).
let (eax_26, _ebx_26, ecx_26, _edx_26) = raw::cpuid(0x8000_0026, 0);
((eax_26 & 0xff) as u32, (ecx_26 & 0xff) as u32)
((eax_26 & 0xff), (ecx_26 & 0xff))
} else {
(0, 0)
};
let mut out = Vec::with_capacity(num_cpus as usize);
for cpu in 0..num_cpus {
if zen4_ccds > 0 {
let ccd_id = cpu / _zen4_cores_per_ccd.max(1) as u32;
let ccd_id = cpu / _zen4_cores_per_ccd.max(1);
out.push((cpu, CoreType::AmdCcd(ccd_id as u8)));
} else {
let ccd_id = cpu / cores_per_ccx.max(1) as u32;
@@ -108,17 +108,18 @@ impl Widget for BrailleGraph<'_> {
.render(canvas_area, buf);
// Render y-axis labels in the reserved left margin.
if let Some((ref top_label, ref bot_label)) = self.y_labels {
if inner.height >= 2 && inner.width >= 4 {
let label_style = Style::new().dark_gray();
// Top label: right-aligned in 4-char space.
let top = format!("{:>4}", top_label.chars().take(4).collect::<String>());
buf.set_string(inner.x, inner.y, &top, label_style);
// Bottom label.
let bot_y = inner.y + inner.height.saturating_sub(1);
let bot = format!("{:>4}", bot_label.chars().take(4).collect::<String>());
buf.set_string(inner.x, bot_y, &bot, label_style);
}
if let Some((ref top_label, ref bot_label)) = self.y_labels
&& inner.height >= 2
&& inner.width >= 4
{
let label_style = Style::new().dark_gray();
// Top label: right-aligned in 4-char space.
let top = format!("{:>4}", top_label.chars().take(4).collect::<String>());
buf.set_string(inner.x, inner.y, &top, label_style);
// Bottom label.
let bot_y = inner.y + inner.height.saturating_sub(1);
let bot = format!("{:>4}", bot_label.chars().take(4).collect::<String>());
buf.set_string(inner.x, bot_y, &bot, label_style);
}
// Render optional x-axis labels.
@@ -77,10 +77,10 @@ impl KillDialog {
}
pub fn auto_close_if_done(&mut self) {
if let Some(at) = self.result_at {
if at.elapsed().as_millis() > 2000 {
self.close();
}
if let Some(at) = self.result_at
&& at.elapsed().as_millis() > 2000
{
self.close();
}
}
}
@@ -184,19 +184,19 @@ fn handle_mouse(me: MouseEvent, header: &Rect, table: &Rect, keybar: &Rect, app:
// Header line is y == table.y; rows start at y == table.y + 1.
let row = y.saturating_sub(table.y + 1) as usize;
let id = app.cpus.get(row).map(|c| c.id);
if let Some(id) = id {
if let Some(idx) = app.cpus.iter().position(|c| c.id == id) {
app.table_state.select(Some(idx));
// Re-click on the same row toggles expand
// (mirrors htop). A click on a different
// row just selects that row.
if app.last_clicked_cpu == Some(id) {
app.toggle_expand();
} else {
app.expanded_cpu = None;
}
app.last_clicked_cpu = Some(id);
if let Some(id) = id
&& let Some(idx) = app.cpus.iter().position(|c| c.id == id)
{
app.table_state.select(Some(idx));
// Re-click on the same row toggles expand
// (mirrors htop). A click on a different
// row just selects that row.
if app.last_clicked_cpu == Some(id) {
app.toggle_expand();
} else {
app.expanded_cpu = None;
}
app.last_clicked_cpu = Some(id);
}
} else if hit_test(*header, x, y) {
// Header click cycles the governor (the most common
@@ -271,7 +271,7 @@ fn main() -> io::Result<()> {
let theme_mode = args
.theme_mode
.as_deref()
.unwrap_or_else(|| cfg.theme.mode.as_str());
.unwrap_or(cfg.theme.mode.as_str());
app.theme_idx = match theme_mode {
"light" => 1,
"high-contrast" => 2,
@@ -395,18 +395,18 @@ fn main() -> io::Result<()> {
app.refresh();
last_refresh = Instant::now();
app.bench_line = bench.status_line(app.cpus.len());
if bench.running {
if let Some((elapsed, _)) = bench.progress() {
let dur = bench.duration.as_secs() as u32;
if dur > 0 && elapsed >= dur {
bench.stop();
app.flash_toast(format!(
"Benchmark done: {} {} in {}s",
bench.last_score,
bench.unit_name(),
bench.last_duration_s
));
}
if bench.running
&& let Some((elapsed, _)) = bench.progress()
{
let dur = bench.duration.as_secs() as u32;
if dur > 0 && elapsed >= dur {
bench.stop();
app.flash_toast(format!(
"Benchmark done: {} {} in {}s",
bench.last_score,
bench.unit_name(),
bench.last_duration_s
));
}
}
if let Some(server) = dbus_server.as_ref() {
@@ -605,29 +605,28 @@ fn main() -> io::Result<()> {
if app.affinity_editor.open {
f.render_widget(&mut app.affinity_editor, full);
}
if app.show_open_files {
if let Some(files) = &app.open_files_result {
let pid = app.open_files_for_pid.unwrap_or(0);
let area =
full.centered(Constraint::Percentage(70), Constraint::Percentage(60));
f.render_widget(Clear, area);
let lines: Vec<ratatui::text::Line> =
crate::open_files::format_files(files)
.into_iter()
.map(ratatui::text::Line::from)
.collect();
let block = ratatui::widgets::Block::default()
.borders(ratatui::widgets::Borders::ALL)
.title(format!(
" Open FDs: PID {} ({}) \u{2014} Esc to close ",
pid,
files.len()
));
let para = ratatui::widgets::Paragraph::new(lines)
.block(block)
.scroll((0, 0));
f.render_widget(para, area);
}
if app.show_open_files
&& let Some(files) = &app.open_files_result
{
let pid = app.open_files_for_pid.unwrap_or(0);
let area =
full.centered(Constraint::Percentage(70), Constraint::Percentage(60));
f.render_widget(Clear, area);
let lines: Vec<ratatui::text::Line> = crate::open_files::format_files(files)
.into_iter()
.map(ratatui::text::Line::from)
.collect();
let block = ratatui::widgets::Block::default()
.borders(ratatui::widgets::Borders::ALL)
.title(format!(
" Open FDs: PID {} ({}) \u{2014} Esc to close ",
pid,
files.len()
));
let para = ratatui::widgets::Paragraph::new(lines)
.block(block)
.scroll((0, 0));
f.render_widget(para, area);
}
// Hint bar: remind user how to exit expand mode.
let hint = ratatui::widgets::Paragraph::new(ratatui::text::Line::styled(
@@ -863,10 +862,8 @@ fn main() -> io::Result<()> {
app.kill_dialog.close();
app.flash_status("kill cancelled");
}
Key::Char('\n') if app.kill_dialog.open => {
if app.kill_dialog.result.is_none() {
app.kill_dialog.send_signal();
}
Key::Char('\n') if app.kill_dialog.open && app.kill_dialog.result.is_none() => {
app.kill_dialog.send_signal();
}
Key::Up if app.kill_dialog.open => {
app.kill_dialog.move_selection(-1);
@@ -1000,17 +997,17 @@ fn main() -> io::Result<()> {
});
}
Key::Char('+') => {
let themes: [(&str, fn() -> crate::theme::Theme); 6] = [
("dark", crate::theme::Theme::dark),
("light", crate::theme::Theme::light),
("high-contrast", crate::theme::Theme::high_contrast),
("nord", crate::theme::Theme::nord),
("gruvbox", crate::theme::Theme::gruvbox),
("solarized-dark", crate::theme::Theme::solarized_dark),
let themes: [(&str, crate::theme::Theme); 6] = [
("dark", crate::theme::Theme::dark()),
("light", crate::theme::Theme::light()),
("high-contrast", crate::theme::Theme::high_contrast()),
("nord", crate::theme::Theme::nord()),
("gruvbox", crate::theme::Theme::gruvbox()),
("solarized-dark", crate::theme::Theme::solarized_dark()),
];
app.theme_idx = (app.theme_idx + 1) % themes.len();
let (name, ctor) = themes[app.theme_idx];
app.theme = ctor();
let (name, theme) = &themes[app.theme_idx];
app.theme = theme.clone();
app.flash_toast(format!("theme: {name}"));
}
Key::Char('k') if app.current_tab == TabId::Process => {
@@ -1107,10 +1104,11 @@ fn main() -> io::Result<()> {
app.flash_status("refresh interval (ms): type digits + Enter (50..60000)");
}
Key::Char(c) if interval_input.is_some() => {
if let Some(buf) = interval_input.as_mut() {
if c.is_ascii_digit() && buf.len() < 5 {
buf.push(c);
}
if let Some(buf) = interval_input.as_mut()
&& c.is_ascii_digit()
&& buf.len() < 5
{
buf.push(c);
}
}
Key::Backspace if interval_input.is_some() => {
@@ -1256,20 +1254,20 @@ fn main() -> io::Result<()> {
"process filter: type chars + Enter to apply, Esc to clear",
);
}
Key::Char(c) if process_filter_input.is_some() && !interval_input.is_some() => {
Key::Char(c) if process_filter_input.is_some() && interval_input.is_none() => {
if let Some(buf) = process_filter_input.as_mut() {
buf.push(c);
}
}
Key::Backspace
if process_filter_input.is_some() && !interval_input.is_some() =>
if process_filter_input.is_some() && interval_input.is_none() =>
{
if let Some(buf) = process_filter_input.as_mut() {
buf.pop();
}
}
Key::Char('\n')
if process_filter_input.is_some() && !interval_input.is_some() =>
if process_filter_input.is_some() && interval_input.is_none() =>
{
let new_filter = process_filter_input.take().unwrap_or_default();
app.process_filter = new_filter.clone();
@@ -68,18 +68,18 @@ impl MemInfo {
pub fn read_meminfo() -> MemInfo {
let mut info = MemInfo::default();
// Redox scheme: a single file with key:value lines (analogous to /proc/meminfo).
if let Ok(s) = fs::read_to_string("/scheme/sys/mem") {
if parse_meminfo_kv(&s, &mut info) {
info.available = true;
return info;
}
if let Ok(s) = fs::read_to_string("/scheme/sys/mem")
&& parse_meminfo_kv(&s, &mut info)
{
info.available = true;
return info;
}
// Linux /proc/meminfo.
if let Ok(s) = fs::read_to_string("/proc/meminfo") {
if parse_meminfo_kv(&s, &mut info) {
info.available = true;
return info;
}
if let Ok(s) = fs::read_to_string("/proc/meminfo")
&& parse_meminfo_kv(&s, &mut info)
{
info.available = true;
return info;
}
info
}
@@ -171,15 +171,15 @@ pub fn read_os_info() -> OsInfo {
}
}
// Redox fallback: use uname release string as the name (no /etc/os-release).
if info.name.is_empty() {
if let Ok(s) = fs::read_to_string("/scheme/sys/uname") {
for line in s.lines() {
if let Some(rest) = line.strip_prefix("release=") {
let v = rest.trim().to_string();
if !v.is_empty() {
info.name = v;
break;
}
if info.name.is_empty()
&& let Ok(s) = fs::read_to_string("/scheme/sys/uname")
{
for line in s.lines() {
if let Some(rest) = line.strip_prefix("release=") {
let v = rest.trim().to_string();
if !v.is_empty() {
info.name = v;
break;
}
}
}
@@ -214,12 +214,11 @@ pub fn read_os_info() -> OsInfo {
}
// Uptime: /proc/uptime first field is seconds (float).
if let Ok(s) = fs::read_to_string("/proc/uptime") {
if let Some(first) = s.split_whitespace().next() {
if let Ok(v) = first.parse::<f64>() {
info.uptime_secs = v as u64;
}
}
if let Ok(s) = fs::read_to_string("/proc/uptime")
&& let Some(first) = s.split_whitespace().next()
&& let Ok(v) = first.parse::<f64>()
{
info.uptime_secs = v as u64;
}
info.available = !info.kernel.is_empty() || !info.name.is_empty() || info.uptime_secs > 0;
@@ -171,10 +171,10 @@ pub fn read_msr(cpu: u32, msr: u32) -> Option<u64> {
let scheme_path = format!("/scheme/sys/msr/{}/0x{:x}", cpu, msr);
if Path::new(&scheme_path).exists() {
let mut data = [0u8; 8];
if let Ok(mut f) = fs::File::open(&scheme_path) {
if f.read_exact(&mut data).is_ok() {
return Some(u64::from_le_bytes(data));
}
if let Ok(mut f) = fs::File::open(&scheme_path)
&& f.read_exact(&mut data).is_ok()
{
return Some(u64::from_le_bytes(data));
}
// Path exists but open/read failed — permission denied or similar.
mark_msr_failed(cpu);
@@ -183,12 +183,12 @@ pub fn read_msr(cpu: u32, msr: u32) -> Option<u64> {
// Linux fallback: /dev/cpu/{cpu}/msr
let dev_path = format!("/dev/cpu/{}/msr", cpu);
if Path::new(&dev_path).exists() {
if let Ok(mut f) = fs::File::open(&dev_path) {
if f.seek(SeekFrom::Start(msr as u64)).is_ok() {
let mut data = [0u8; 8];
if f.read_exact(&mut data).is_ok() {
return Some(u64::from_le_bytes(data));
}
if let Ok(mut f) = fs::File::open(&dev_path)
&& f.seek(SeekFrom::Start(msr as u64)).is_ok()
{
let mut data = [0u8; 8];
if f.read_exact(&mut data).is_ok() {
return Some(u64::from_le_bytes(data));
}
}
// Dev path exists but open/read failed.
@@ -201,14 +201,14 @@ pub fn read_msr(cpu: u32, msr: u32) -> Option<u64> {
pub fn write_msr(cpu: u32, msr: u32, val: u64) -> bool {
let scheme_path = format!("/scheme/sys/msr/{}/0x{:x}", cpu, msr);
if Path::new(&scheme_path).exists() {
return fs::write(&scheme_path, &val.to_le_bytes()).is_ok();
return fs::write(&scheme_path, val.to_le_bytes()).is_ok();
}
let dev_path = format!("/dev/cpu/{}/msr", cpu);
if let Ok(mut f) = fs::OpenOptions::new().write(true).open(&dev_path) {
if f.seek(SeekFrom::Start(msr as u64)).is_ok() {
use std::io::Write;
return f.write_all(&val.to_le_bytes()).is_ok();
}
if let Ok(mut f) = fs::OpenOptions::new().write(true).open(&dev_path)
&& f.seek(SeekFrom::Start(msr as u64)).is_ok()
{
use std::io::Write;
return f.write_all(&val.to_le_bytes()).is_ok();
}
false
}
@@ -10,6 +10,7 @@ use crate::theme;
const MIN_NICE: i32 = -20;
const MAX_NICE: i32 = 19;
#[derive(Default)]
pub struct NiceEdit {
pub open: bool,
pub pid: u32,
@@ -19,24 +20,11 @@ pub struct NiceEdit {
pub result_timer: Option<std::time::Instant>,
}
impl Default for NiceEdit {
fn default() -> Self {
Self {
open: false,
pid: 0,
comm: String::new(),
value: 0,
result: None,
result_timer: None,
}
}
}
impl NiceEdit {
pub fn open_for(&mut self, pid: u32, comm: &str, current_nice: i32) {
self.pid = pid;
self.comm = comm.to_string();
self.value = current_nice.clamp(MIN_NICE, MAX_NICE) as i32;
self.value = current_nice.clamp(MIN_NICE, MAX_NICE);
self.open = true;
self.result = None;
self.result_timer = None;
@@ -59,10 +47,10 @@ impl NiceEdit {
}
pub fn auto_close_if_done(&mut self) {
if let Some(t) = self.result_timer {
if t.elapsed().as_secs() >= 2 {
self.close();
}
if let Some(t) = self.result_timer
&& t.elapsed().as_secs() >= 2
{
self.close();
}
}
}
@@ -131,7 +119,7 @@ impl Widget for &NiceEdit {
Span::raw("]"),
]);
Widget::render(
&Paragraph::new(slider_line).alignment(Alignment::Center),
Paragraph::new(slider_line).alignment(Alignment::Center),
slider_area,
buf,
);
@@ -150,6 +138,6 @@ impl Widget for &NiceEdit {
theme::VALUE_OFF,
))
};
Widget::render(&Paragraph::new(msg), msg_area, buf);
Widget::render(Paragraph::new(msg), msg_area, buf);
}
}
@@ -107,7 +107,7 @@ impl SortMode {
pub fn sort_ascending(self, processes: &mut Vec<ProcessInfo>, ascending: bool) {
if ascending {
match self {
SortMode::Rss => processes.sort_by(|a, b| a.rss_kb.cmp(&b.rss_kb)),
SortMode::Rss => processes.sort_by_key(|a| a.rss_kb),
SortMode::Cpu => processes.sort_by(|a, b| {
a.cpu_pct
.partial_cmp(&b.cpu_pct)
@@ -132,9 +132,9 @@ impl SortMode {
SortMode::IoWriteRate => {
sort_by_io_rate_field_asc(processes, |p| p.io_write_rate_kbs)
}
SortMode::RChar => processes.sort_by(|a, b| a.io_rchar_kb.cmp(&b.io_rchar_kb)),
SortMode::WChar => processes.sort_by(|a, b| a.io_wchar_kb.cmp(&b.io_wchar_kb)),
SortMode::VSize => processes.sort_by(|a, b| a.vsize_kb.cmp(&b.vsize_kb)),
SortMode::RChar => processes.sort_by_key(|a| a.io_rchar_kb),
SortMode::WChar => processes.sort_by_key(|a| a.io_wchar_kb),
SortMode::VSize => processes.sort_by_key(|a| a.vsize_kb),
SortMode::Pid => processes.sort_by_key(|p| p.pid),
SortMode::Name => processes.sort_by(|a, b| a.comm.cmp(&b.comm)),
SortMode::ThreadIo => sort_by_io_field_asc(processes, |p| {
@@ -308,7 +308,7 @@ pub fn sort_tree(processes: &mut Vec<ProcessInfo>, sort_mode: SortMode) {
let mut out: Vec<ProcessInfo> = Vec::with_capacity(processes.len());
let mut visited: BTreeSet<u32> = BTreeSet::new();
for &root in &roots {
dfs_emit(&processes, &children, root, &mut out, &mut visited);
dfs_emit(processes, &children, root, &mut out, &mut visited);
}
// 5. Append any leftover procs (defensive — should not happen
@@ -637,11 +637,11 @@ fn read_thread_io(pid: u32) -> (Option<u64>, Option<u64>) {
total_read = total_read.saturating_add(v);
any_counted = true;
}
} else if let Some(rest) = line.strip_prefix("write_bytes:") {
if let Ok(v) = rest.trim().parse::<u64>() {
total_write = total_write.saturating_add(v);
any_counted = true;
}
} else if let Some(rest) = line.strip_prefix("write_bytes:")
&& let Ok(v) = rest.trim().parse::<u64>()
{
total_write = total_write.saturating_add(v);
any_counted = true;
}
}
}
@@ -721,10 +721,10 @@ pub fn parse_cpu_list(s: &str) -> Vec<u32> {
out.push(c);
}
}
} else if let Ok(n) = chunk.parse::<u32>() {
if !out.contains(&n) {
out.push(n);
}
} else if let Ok(n) = chunk.parse::<u32>()
&& !out.contains(&n)
{
out.push(n);
}
}
out.sort_unstable();
@@ -863,20 +863,20 @@ fn read_process(pid: u32) -> Option<ProcessInfo> {
}
fn derive_sched_policy(pid: u32, priority: i64) -> String {
if let Ok(data) = fs::read(format!("/proc/{}/sched-policy", pid)) {
if let Some(&policy) = data.first() {
return match policy {
0 => "FIFO",
1 => "RR",
2 => "OTHER",
_ => "?",
}
.into();
if let Ok(data) = fs::read(format!("/proc/{}/sched-policy", pid))
&& let Some(&policy) = data.first()
{
return match policy {
0 => "FIFO",
1 => "RR",
2 => "OTHER",
_ => "?",
}
.into();
}
if priority > 0 && priority < 40 {
"OTHER".into()
} else if priority >= 40 && priority < 100 {
} else if (40..100).contains(&priority) {
"RT".into()
} else {
"OTHER".into()
@@ -2005,7 +2005,7 @@ pub fn set_nice(pid: u32, nice: i32) -> Result<(), String> {
#[cfg(target_os = "linux")]
unsafe {
// PRIO_PROCESS = 0, who = pid
if libc::setpriority(0, pid as u32, n) == 0 {
if libc::setpriority(0, pid, n) == 0 {
Ok(())
} else {
Err(std::io::Error::last_os_error().to_string())
@@ -186,11 +186,9 @@ pub fn render_header<'a>(app: &'a App, focused: bool) -> Paragraph<'a> {
" ".into(),
"Throttle: ".set_style(theme::LABEL),
match app.throttle {
ThrottleMode::Auto => "AUTO".set_style(theme::HEADER_THROTTLE_AUTO).into(),
ThrottleMode::User => "USER".set_style(theme::HEADER_THROTTLE_USER).into(),
ThrottleMode::ForcedMin => {
"FORCED MIN".set_style(theme::HEADER_THROTTLE_FORCED).into()
}
ThrottleMode::Auto => "AUTO".set_style(theme::HEADER_THROTTLE_AUTO),
ThrottleMode::User => "USER".set_style(theme::HEADER_THROTTLE_USER),
ThrottleMode::ForcedMin => "FORCED MIN".set_style(theme::HEADER_THROTTLE_FORCED),
},
]),
Line::from(vec![
@@ -986,13 +984,14 @@ pub fn render_network_panel<'a>(app: &'a App, focused: bool) -> Paragraph<'a> {
.unwrap_or("?")
.set_style(theme::VALUE),
]));
if let Some(mac) = &iface.mac_address {
if !mac.is_empty() && mac != "00:00:00:00:00:00" {
lines.push(Line::from(vec![
" MAC: ".set_style(theme::LABEL),
mac.clone().set_style(theme::VALUE),
]));
}
if let Some(mac) = &iface.mac_address
&& !mac.is_empty()
&& mac != "00:00:00:00:00:00"
{
lines.push(Line::from(vec![
" MAC: ".set_style(theme::LABEL),
mac.clone().set_style(theme::VALUE),
]));
}
if let Some(mtu) = iface.mtu {
lines.push(Line::from(vec![
@@ -1000,13 +999,13 @@ pub fn render_network_panel<'a>(app: &'a App, focused: bool) -> Paragraph<'a> {
mtu.to_string().set_style(theme::VALUE),
]));
}
if let Some(speed) = iface.speed_mbps {
if speed > 0 {
lines.push(Line::from(vec![
" Speed: ".set_style(theme::LABEL),
format!("{} Mbps", speed).set_style(theme::VALUE),
]));
}
if let Some(speed) = iface.speed_mbps
&& speed > 0
{
lines.push(Line::from(vec![
" Speed: ".set_style(theme::LABEL),
format!("{} Mbps", speed).set_style(theme::VALUE),
]));
}
lines.push(Line::from(vec![
" RX bytes: ".set_style(theme::LABEL),
@@ -1481,7 +1480,7 @@ fn tree_prefix(
None => return String::new(),
};
let next_ppid = all.get(my_index + 1).map(|n| n.ppid);
let is_last = next_ppid.map_or(true, |np| np != ppid);
let is_last = next_ppid != Some(ppid);
// For each ancestor level, decide if the bar continues. The
// bar at depth d (counting from 0 for the row's immediate
@@ -1494,7 +1493,7 @@ fn tree_prefix(
let mut bar = String::new();
for &ancestor in ancestors.iter() {
let next_row = all.get(my_index + 1);
let ancestor_active_in_next = next_row.map_or(false, |n| {
let ancestor_active_in_next = next_row.is_some_and(|n| {
if n.ppid == ancestor {
return true;
}
@@ -1910,7 +1909,7 @@ pub fn render_cpu_table<'a>(
let mut padded: Vec<u8> = cpu.load_history.iter().copied().collect();
if padded.len() < SPARK_WIDTH {
let pad = SPARK_WIDTH - padded.len();
padded = std::iter::repeat(0u8).take(pad).chain(padded).collect();
padded = std::iter::repeat_n(0u8, pad).chain(padded).collect();
}
padded_to_sparkline(&padded)
};
@@ -1960,76 +1959,76 @@ pub fn render_cpu_table<'a>(
})
.collect();
let mut rows = rows;
if let Some(expanded_id) = expanded_cpu {
if let Some(cpu) = cpus.iter().find(|c| c.id == expanded_id) {
let sub_style = theme::LABEL;
let active_style = Style::new().yellow().bold();
for (idx, pstate) in cpu.pstates.iter().enumerate() {
let is_current = cpu.current_idx == Some(idx);
let s = if is_current { active_style } else { sub_style };
let marker = if is_current { "" } else { "" };
let label = if is_current {
format!("{marker} P{idx} (current)")
} else {
format!("{marker} P{idx}")
};
let freq_mhz = pstate.freq_khz / 1000;
let power_w = pstate.power_mw as f64 / 1000.0;
rows.push(Row::new(vec![
label.set_style(s),
format!("{freq_mhz} MHz").set_style(s),
format!("{power_w:.1} W").set_style(s),
format!("0x{:02x}", (pstate.ctl >> 8) & 0x7f).set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
]));
}
if let Some(ref hwp) = cpu.hwp {
let s = sub_style;
rows.push(Row::new(vec![
" HWP caps".set_style(s),
format!(
"eff={} guar={} max={}",
hwp.efficient_perf, hwp.guaranteed_perf, hwp.max_perf
)
.set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
format!("{}", if hwp.enabled { "ON" } else { "OFF" }).set_style(
if hwp.enabled {
Style::new().green()
} else {
theme::VALUE_OFF
},
),
"".set_style(s),
"".set_style(s),
"".set_style(s),
]));
rows.push(Row::new(vec![
" HWP req".set_style(s),
format!(
"min={} max={} des={} EPP={} ({})",
hwp.min_request,
hwp.max_request,
hwp.desired_perf,
hwp.epp,
hwp.epp_label()
)
.set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
]));
}
if let Some(expanded_id) = expanded_cpu
&& let Some(cpu) = cpus.iter().find(|c| c.id == expanded_id)
{
let sub_style = theme::LABEL;
let active_style = Style::new().yellow().bold();
for (idx, pstate) in cpu.pstates.iter().enumerate() {
let is_current = cpu.current_idx == Some(idx);
let s = if is_current { active_style } else { sub_style };
let marker = if is_current { "" } else { "" };
let label = if is_current {
format!("{marker} P{idx} (current)")
} else {
format!("{marker} P{idx}")
};
let freq_mhz = pstate.freq_khz / 1000;
let power_w = pstate.power_mw as f64 / 1000.0;
rows.push(Row::new(vec![
label.set_style(s),
format!("{freq_mhz} MHz").set_style(s),
format!("{power_w:.1} W").set_style(s),
format!("0x{:02x}", (pstate.ctl >> 8) & 0x7f).set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
]));
}
if let Some(ref hwp) = cpu.hwp {
let s = sub_style;
rows.push(Row::new(vec![
" HWP caps".set_style(s),
format!(
"eff={} guar={} max={}",
hwp.efficient_perf, hwp.guaranteed_perf, hwp.max_perf
)
.set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
(if hwp.enabled { "ON" } else { "OFF" })
.to_string()
.set_style(if hwp.enabled {
Style::new().green()
} else {
theme::VALUE_OFF
}),
"".set_style(s),
"".set_style(s),
"".set_style(s),
]));
rows.push(Row::new(vec![
" HWP req".set_style(s),
format!(
"min={} max={} des={} EPP={} ({})",
hwp.min_request,
hwp.max_request,
hwp.desired_perf,
hwp.epp,
hwp.epp_label()
)
.set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
"".set_style(s),
]));
}
}
Table::new(
@@ -113,10 +113,10 @@ impl SchedStats {
stats.context_switches = rest.trim().parse().ok();
}
// "intr <total> <irq0> <irq1> ..." — first field is total.
if let Some(rest) = trimmed.strip_prefix("intr ") {
if let Some(total_str) = rest.split_whitespace().next() {
stats.total_irqs = total_str.parse().ok();
}
if let Some(rest) = trimmed.strip_prefix("intr ")
&& let Some(total_str) = rest.split_whitespace().next()
{
stats.total_irqs = total_str.parse().ok();
}
}
stats
@@ -88,7 +88,7 @@ impl SmartInfo {
fn read_smart_for_disk(disk: &str) -> SmartHealth {
let output = Command::new("smartctl")
.args(["-A", "-H", "/dev/", &format!("{disk}").to_string()])
.args(["-A", "-H", "/dev/", &disk.to_string().to_string()])
.output();
let output = match output {
Ok(o) => o,
@@ -42,7 +42,7 @@ impl DiskStats {
Self {
read_bytes: fields.get(2).copied().unwrap_or(0),
write_bytes: fields.get(6).copied().unwrap_or(0),
reads_completed: fields.get(0).copied().unwrap_or(0),
reads_completed: fields.first().copied().unwrap_or(0),
writes_completed: fields.get(4).copied().unwrap_or(0),
read_kbps: 0.0,
write_kbps: 0.0,
@@ -97,10 +97,12 @@ fn read_disk(name: &str, path: &Path) -> Option<DiskInfo> {
if let Ok(entries) = fs::read_dir(path) {
for entry in entries.flatten() {
let p = entry.path();
if let Some(n) = p.file_name().and_then(|s| s.to_str()) {
if p.is_dir() && n.starts_with(name) && n != name {
partitions.push(n.to_string());
}
if let Some(n) = p.file_name().and_then(|s| s.to_str())
&& p.is_dir()
&& n.starts_with(name)
&& n != name
{
partitions.push(n.to_string());
}
}
}