redbear-power: wire keybinding config into key dispatch

Add KeyBindings::char_for() and replace the 7 hardcoded Key::Char arms in main.rs (quit, cycle_governor, refresh_now, toggle_help, snapshot, benchmark_start, benchmark_stop). Esc remains a quit fallback. All other keys stay hardcoded. Build, 224 tests, clippy, and fmt clean.
This commit is contained in:
Sisyphus Agent
2026-07-07 14:05:55 +03:00
parent c7ed62eb30
commit 5266c3d0ce
2 changed files with 33 additions and 7 deletions
@@ -117,6 +117,27 @@ impl Default for KeyBindings {
}
}
impl KeyBindings {
/// Return the first character configured for a command, if any.
/// Recognized commands: `quit`, `cycle_governor`, `refresh_now`,
/// `toggle_help`, `snapshot`, `benchmark_start`, `benchmark_stop`.
/// Multi-character strings return only the first char; unrecognized
/// commands return `None`.
pub fn char_for(&self, command: &str) -> Option<char> {
let s = match command {
"quit" => &self.quit,
"cycle_governor" => &self.cycle_governor,
"refresh_now" => &self.refresh_now,
"toggle_help" => &self.toggle_help,
"snapshot" => &self.snapshot,
"benchmark_start" => &self.benchmark_start,
"benchmark_stop" => &self.benchmark_stop,
_ => return None,
};
s.chars().next()
}
}
#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
pub struct BenchmarkConfig {
@@ -332,6 +332,7 @@ fn main() -> io::Result<()> {
POLL_MS
});
app.poll_ms = poll.as_millis() as u64;
let kb = &cfg.keybindings;
let mut show_help = false;
// Tab/BackTab cycles keyboard focus between header / table / controls.
let mut focused_panel: usize = 1;
@@ -912,7 +913,9 @@ fn main() -> io::Result<()> {
Key::Esc if app.pid_detail.is_some() => {
app.pid_detail = None;
}
Key::Char('q') | Key::Esc => {
_ if k == Key::Esc
|| matches!(k, Key::Char(c) if kb.char_for("quit") == Some(c)) =>
{
if bench.running {
bench.stop();
}
@@ -960,7 +963,7 @@ fn main() -> io::Result<()> {
Key::Char('8') => app.set_tab(app::TabId::Storage),
Key::Char('9') => app.set_tab(app::TabId::Process),
Key::Char('T') => app.set_tab(app.current_tab.next()),
Key::Char('?') => show_help = !show_help,
Key::Char(c) if kb.char_for("toggle_help") == Some(c) => show_help = !show_help,
Key::Down | Key::Char('j') if show_help => {
app.help_scroll = app.help_scroll.saturating_add(1);
}
@@ -979,7 +982,9 @@ fn main() -> io::Result<()> {
Key::End | Key::Char('G') if show_help => {
app.help_scroll = 200;
}
Key::Char('g') => app.cycle_governor(),
Key::Char(c) if kb.char_for("cycle_governor") == Some(c) => {
app.cycle_governor()
}
Key::Char('p') => app.step_selected_pstate(-1),
Key::Char('P') => app.step_selected_pstate(1),
Key::Char('m') => app.force_min_pstate(),
@@ -1072,12 +1077,12 @@ fn main() -> io::Result<()> {
app.flash_status("no process selected");
}
}
Key::Char('r') => {
Key::Char(c) if kb.char_for("refresh_now") == Some(c) => {
app.refresh();
last_refresh = Instant::now();
app.flash_status("refreshed");
}
Key::Char('c') => {
Key::Char(c) if kb.char_for("snapshot") == Some(c) => {
let path = "/tmp/redbear-power-snapshot.txt";
match fs::write(path, snapshot(&app, 140, 50)) {
Ok(_) => app.flash_status(format!("snapshot → {path}")),
@@ -1143,7 +1148,7 @@ fn main() -> io::Result<()> {
interval_input = None;
app.flash_status("refresh interval cancelled");
}
Key::Char('b') => {
Key::Char(c) if kb.char_for("benchmark_start") == Some(c) => {
bench.start(app.cpus.len(), 30);
let cores = if bench.single_core {
1
@@ -1156,7 +1161,7 @@ fn main() -> io::Result<()> {
cores
));
}
Key::Char('B') => {
Key::Char(c) if kb.char_for("benchmark_stop") == Some(c) => {
if bench.running {
bench.stop();
app.flash_status(format!(