From 2c364382a5449f5f1357d7b6904ee1e54c50403d Mon Sep 17 00:00:00 2001 From: Christopher Sardegna Date: Wed, 7 Oct 2026 20:25:58 -0700 Subject: [PATCH] Clippy pedantic fixes --- src/app.rs | 24 +-- src/app_readme_tests.rs | 2 +- src/app_tests.rs | 284 ++++++++++++++++---------------- src/daemon.rs | 19 +-- src/format.rs | 2 +- src/frame.rs | 2 +- src/harness/omp.rs | 4 +- src/main.rs | 2 +- src/protocol.rs | 30 ++-- src/protocol_tests.rs | 4 +- src/selection.rs | 13 +- src/session.rs | 10 +- src/supervisor.rs | 45 +++-- src/supervisor_capture_tests.rs | 74 ++++----- src/supervisor_tests.rs | 4 +- src/task.rs | 15 +- src/task_tests.rs | 2 +- src/terminal/ansi.rs | 6 +- src/terminal/emulator.rs | 11 +- src/terminal/input.rs | 4 +- src/testutil.rs | 2 +- src/ui.rs | 4 +- tests/common/mod.rs | 6 +- tests/daemon_handshake.rs | 17 +- tests/daemon_kill.rs | 4 +- tests/daemon_lifetime.rs | 2 +- tests/daemon_resume.rs | 6 +- 27 files changed, 290 insertions(+), 308 deletions(-) diff --git a/src/app.rs b/src/app.rs index a9692c7..2379d2b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -958,7 +958,8 @@ impl App { CtEvent::Mouse(m) => self.on_mouse(m), CtEvent::FocusGained => self.terminal_focused = true, CtEvent::FocusLost => self.terminal_focused = false, - _ => {} + // Key releases carry nothing to forward. + CtEvent::Key(_) => {} } } } @@ -1105,8 +1106,8 @@ impl App { /// Navigate into `dir`: retype the input as its path (trailing slash) so /// completion continues inside it, with the dir itself selected as row 0. - fn enter_dir(&mut self, dir: PathBuf) { - self.dir_input = EditBuffer::seeded(format!("{}/", path::abbreviate(&dir))); + fn enter_dir(&mut self, dir: &Path) { + self.dir_input = EditBuffer::seeded(format!("{}/", path::abbreviate(dir))); self.refresh_dir_candidates(); } @@ -1288,7 +1289,7 @@ impl App { // Reconnect only makes sense against a daemon; a dead in-process core // has nothing to reconnect to, so `--foreground` just quits. KeyCode::Char('r') if self.daemon_backed => self.reconnect(), - KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => { + KeyCode::Char('q' | 'Q') | KeyCode::Esc => { self.exit_intent = Some(ExitIntent::Disconnect); } _ => {} @@ -1457,7 +1458,7 @@ impl App { && c.kind != DirKind::Use { let path = c.path.clone(); - self.enter_dir(path); + self.enter_dir(&path); } return; } @@ -1476,7 +1477,7 @@ impl App { // Resolved path or current-task directory: run there. DirKind::Use | DirKind::Jump => self.open_spawn_prompt(path), // Subdirectory: descend and select its resolved-path row. - DirKind::Into => self.enter_dir(path), + DirKind::Into => self.enter_dir(&path), } } } @@ -1494,7 +1495,7 @@ impl App { KeyCode::Esc => self.close_group_picker(), KeyCode::Up => self.group_sel = self.group_sel.saturating_sub(1), KeyCode::Down => { - self.group_sel = step_down(self.group_sel, self.group_candidates.len()) + self.group_sel = step_down(self.group_sel, self.group_candidates.len()); } KeyCode::Enter => { // Enter assigns the highlighted group, or creates the typed @@ -1574,7 +1575,7 @@ impl App { KeyCode::Esc => self.close_prompt(), KeyCode::Up => self.agent_sel = self.agent_sel.saturating_sub(1), KeyCode::Down => { - self.agent_sel = step_down(self.agent_sel, self.agent_candidates.len()) + self.agent_sel = step_down(self.agent_sel, self.agent_candidates.len()); } KeyCode::Enter => { if let Some(agent) = self.agent_candidates.get(self.agent_sel).cloned() { @@ -1597,7 +1598,7 @@ impl App { fn on_key_peek(&mut self, k: KeyEvent) { match k.code { - KeyCode::Char(' ') | KeyCode::Esc | KeyCode::Char('q') => self.mode = Mode::Dashboard, + KeyCode::Char(' ' | 'q') | KeyCode::Esc => self.mode = Mode::Dashboard, KeyCode::Up | KeyCode::Char('k') => self.select_up(), KeyCode::Down | KeyCode::Char('j') => self.select_down(), KeyCode::Enter => self.attach(), @@ -1617,7 +1618,7 @@ impl App { fn on_key_attached(&mut self, out: &mut impl Write, k: KeyEvent) { // Background on Ctrl-\; the chord may be reported by crossterm as Ctrl-4. let detach = k.modifiers.contains(KeyModifiers::CONTROL) - && matches!(k.code, KeyCode::Char('\\') | KeyCode::Char('4')); + && matches!(k.code, KeyCode::Char('\\' | '4')); if detach { self.detach(out); return; @@ -2042,8 +2043,7 @@ fn key_event_to_key(ev: KeyEvent) -> Option<(Key, Mods)> { /// Recognize `Ctrl-]` in kitty and legacy encodings. Accept Ctrl-5 for legacy /// 0x1D: in crossterm, 0x1C..=0x1F are mapped to Ctrl-'4'..=Ctrl-'7'. fn is_chord_key(k: KeyEvent) -> bool { - k.modifiers.contains(KeyModifiers::CONTROL) - && matches!(k.code, KeyCode::Char(']') | KeyCode::Char('5')) + k.modifiers.contains(KeyModifiers::CONTROL) && matches!(k.code, KeyCode::Char(']' | '5')) } /// Recognize either Shift-`/` event: `?`, or `/` with the Shift modifier. diff --git a/src/app_readme_tests.rs b/src/app_readme_tests.rs index 8a23b2c..7fb0580 100644 --- a/src/app_readme_tests.rs +++ b/src/app_readme_tests.rs @@ -587,7 +587,7 @@ fn cargo_test_screen(id: u64) -> ScreenView { ]; ScreenView { id, - lines: lines.iter().map(|s| s.to_string()).collect(), + lines: lines.iter().map(ToString::to_string).collect(), // Peek reads plain lines; formatted bytes are unused. formatted: Vec::new(), cursor: (0, 0), diff --git a/src/app_tests.rs b/src/app_tests.rs index bfd2714..1a31046 100644 --- a/src/app_tests.rs +++ b/src/app_tests.rs @@ -35,17 +35,17 @@ impl App { self.sync(); } - fn spawn_in(&mut self, cmd: &str, cwd: PathBuf) { + fn spawn_in(&mut self, cmd: &str, cwd: &Path) { self.spawn_checked(cmd, cwd, None); } - fn spawn_grouped(&mut self, cmd: &str, cwd: PathBuf, group: &str) { + fn spawn_grouped(&mut self, cmd: &str, cwd: &Path, group: &str) { self.spawn_checked(cmd, cwd, Some(group)); } /// Send a spawn and retry transient failures. Failed spawns leave /// `next_id` unchanged, so retries preserve task IDs. - fn spawn_checked(&mut self, cmd: &str, cwd: PathBuf, group: Option<&str>) { + fn spawn_checked(&mut self, cmd: &str, cwd: &Path, group: Option<&str>) { self.pump(); let want = self.views.len() + 1; for attempt in 0u64..5 { @@ -54,7 +54,7 @@ impl App { } self.transport.send(Command::Spawn { command: cmd.to_string(), - cwd: cwd.clone(), + cwd: cwd.to_path_buf(), group: group.map(str::to_string), }); self.pump(); @@ -92,7 +92,7 @@ impl App { fn attached(rows: u16, cols: u16, cmd: &str) -> (Self, u64) { let mut app = Self::new_local(rows, cols); let dir = app.invocation_dir.clone(); - app.spawn_in(cmd, dir); + app.spawn_in(cmd, &dir); app.attach(); let id = app.focused_id.expect("attached"); (app, id) @@ -275,10 +275,10 @@ fn selection_follows_task_across_reorder() { fn spawn_moves_selection_to_the_new_task() { let mut app = App::new_local(30, 100); let dir = app.invocation_dir.clone(); - app.spawn_in("sleep 30", dir.clone()); + app.spawn_in("sleep 30", &dir); let first = app.selected_id.expect("first spawn selected"); - app.spawn_in("sleep 31", dir); + app.spawn_in("sleep 31", &dir); let second = app.selected_id.expect("second spawn selected"); assert_ne!(first, second, "selection must move off the prior task"); assert_eq!( @@ -324,7 +324,7 @@ fn two_spawns_in_one_sync_select_the_last() { fn refused_spawn_leaves_selection_alone() { let mut app = App::new_local(30, 100); let dir = app.invocation_dir.clone(); - app.spawn_in("sleep 30", dir.clone()); + app.spawn_in("sleep 30", &dir); let kept = app.selected_id; assert!(kept.is_some()); @@ -350,7 +350,7 @@ fn refused_spawn_leaves_selection_alone() { fn pending_select_ignores_snapshots_without_the_id() { let mut app = App::new_local(30, 100); let dir = app.invocation_dir.clone(); - app.spawn_in("sleep 30", dir); + app.spawn_in("sleep 30", &dir); let kept = app.selected_id; app.pending_select = Some(9999); @@ -419,10 +419,10 @@ fn dir_mode_groups_by_cwd() { fn custom_sections_collate_case_insensitively() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv.clone(), "zebra"); // id 1 - app.spawn_grouped("sleep 5", inv.clone(), "API"); // id 2 - app.spawn_grouped("sleep 5", inv.clone(), "Review"); // id 3 - app.spawn_grouped("sleep 5", inv, "api"); // id 4 + app.spawn_grouped("sleep 5", &inv, "zebra"); // id 1 + app.spawn_grouped("sleep 5", &inv, "API"); // id 2 + app.spawn_grouped("sleep 5", &inv, "Review"); // id 3 + app.spawn_grouped("sleep 5", &inv, "api"); // id 4 app.group_mode = GroupMode::Custom; assert_eq!( @@ -445,8 +445,8 @@ fn dir_sections_collate_case_insensitively() { let (upper, lower) = (base.join("Zed"), base.join("apple")); std::fs::create_dir_all(&upper).unwrap(); std::fs::create_dir_all(&lower).unwrap(); - app.spawn_in("sleep 5", upper.clone()); // id 1 - app.spawn_in("sleep 5", lower.clone()); // id 2 + app.spawn_in("sleep 5", &upper); // id 1 + app.spawn_in("sleep 5", &lower); // id 2 app.group_mode = GroupMode::Dir; let (z, a) = (path::abbreviate(&upper), path::abbreviate(&lower)); @@ -478,9 +478,9 @@ fn group_mode_cycles_state_dir_custom() { fn custom_mode_groups_by_name_with_unassigned_last() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv.clone(), "beta"); // id 1 - app.spawn_grouped("sleep 5", inv.clone(), "alpha"); // id 2 - app.spawn_in("sleep 5", inv); // id 3, no group + app.spawn_grouped("sleep 5", &inv, "beta"); // id 1 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 2 + app.spawn_in("sleep 5", &inv); // id 3, no group app.group_mode = GroupMode::Custom; assert_eq!( @@ -498,13 +498,13 @@ fn custom_mode_groups_by_name_with_unassigned_last() { fn custom_mode_unassigned_tracks_membership() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 app.group_mode = GroupMode::Custom; assert_eq!(app.section_ids(), vec![("alpha".to_string(), vec![1])]); let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); // id 1, no group + app.spawn_in("sleep 5", &inv); // id 1, no group app.group_mode = GroupMode::Custom; assert_eq!(app.section_ids(), vec![("Unassigned".to_string(), vec![1])]); } @@ -514,8 +514,8 @@ fn custom_mode_unassigned_tracks_membership() { fn custom_mode_selection_survives_group_move() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv.clone(), "alpha"); // id 1 - app.spawn_grouped("sleep 5", inv, "beta"); // id 2 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "beta"); // id 2 app.group_mode = GroupMode::Custom; // Exercise moving id 1 rather than the selected second spawn. app.selected_id = Some(1); @@ -545,9 +545,9 @@ fn custom_mode_clusters_by_dir_within_group() { std::fs::create_dir_all(&dir_a).unwrap(); std::fs::create_dir_all(&dir_b).unwrap(); - app.spawn_grouped("sleep 5", dir_b, "alpha"); // id 1, dir b - app.spawn_grouped("sleep 5", dir_a.clone(), "alpha"); // id 2, dir a - app.spawn_grouped("sleep 5", dir_a, "alpha"); // id 3, dir a + app.spawn_grouped("sleep 5", &dir_b, "alpha"); // id 1, dir b + app.spawn_grouped("sleep 5", &dir_a, "alpha"); // id 2, dir a + app.spawn_grouped("sleep 5", &dir_a, "alpha"); // id 3, dir a app.group_mode = GroupMode::Custom; assert_eq!( @@ -563,7 +563,7 @@ fn custom_mode_clusters_by_dir_within_group() { fn tagging_a_finished_task_moves_it_to_in_use() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("true", inv); // exits ~immediately + app.spawn_in("true", &inv); // exits ~immediately app.wait_finished(1); assert!(matches!( app.views[0].lifecycle, @@ -585,10 +585,10 @@ fn tagging_a_finished_task_moves_it_to_in_use() { fn idle_task_lands_in_idle_between_running_and_completed() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv.clone()); // id 1: running - app.spawn_in("sleep 5", inv.clone()); // id 2: idle below - app.spawn_in("sleep 5", inv.clone()); // id 3: tagged below - app.spawn_in("true", inv); // id 4: exits ~immediately + app.spawn_in("sleep 5", &inv); // id 1: running + app.spawn_in("sleep 5", &inv); // id 2: idle below + app.spawn_in("sleep 5", &inv); // id 3: tagged below + app.spawn_in("true", &inv); // id 4: exits ~immediately app.wait_finished(4); app.transport.send(Command::Tag { id: 3, on: true }); app.pump(); @@ -612,8 +612,8 @@ fn idle_task_lands_in_idle_between_running_and_completed() { fn tagged_task_stays_in_use_across_lifecycles() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv.clone()); // id 1: tagged + idle - app.spawn_in("sleep 5", inv); // id 2: running + app.spawn_in("sleep 5", &inv); // id 1: tagged + idle + app.spawn_in("sleep 5", &inv); // id 2: running app.transport.send(Command::Tag { id: 1, on: true }); app.pump(); @@ -642,8 +642,8 @@ fn tagged_task_stays_in_use_across_lifecycles() { fn selection_follows_task_across_idle_rebucket() { let mut app = App::new_local(30, 100); let dir = app.invocation_dir.clone(); - app.spawn_in("sleep 5", dir.clone()); // id 1 - app.spawn_in("sleep 5", dir); // id 2 + app.spawn_in("sleep 5", &dir); // id 1 + app.spawn_in("sleep 5", &dir); // id 2 app.selected_id = Some(1); // Idle id 1 -> it sinks into "Idle", below id 2's "Running". @@ -682,9 +682,9 @@ fn idle_round_trip_leaves_row_order_identical() { for mode in [GroupMode::Custom, GroupMode::Dir] { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv.clone(), "alpha"); // id 1 - app.spawn_grouped("sleep 5", inv.clone(), "alpha"); // id 2 - app.spawn_grouped("sleep 5", inv.clone(), "alpha"); // id 3 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 2 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 3 app.group_mode = mode; let label = match mode { GroupMode::Custom => "alpha".to_string(), @@ -717,8 +717,8 @@ fn finished_task_sinks_within_its_section() { for mode in [GroupMode::Custom, GroupMode::Dir] { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("true", inv.clone(), "alpha"); // id 1: exits ~immediately - app.spawn_grouped("sleep 30", inv.clone(), "alpha"); // id 2: stays live + app.spawn_grouped("true", &inv, "alpha"); // id 1: exits ~immediately + app.spawn_grouped("sleep 30", &inv, "alpha"); // id 2: stays live app.wait_finished(1); app.group_mode = mode; let label = match mode { @@ -739,8 +739,8 @@ fn finished_task_sinks_within_its_section() { fn custom_mode_tagged_task_floats_and_holds_while_idle() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv.clone(), "alpha"); // id 1 - app.spawn_grouped("sleep 5", inv, "alpha"); // id 2: tagged below + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 2: tagged below app.group_mode = GroupMode::Custom; assert_eq!(app.section_ids(), vec![("alpha".to_string(), vec![1, 2])]); @@ -772,14 +772,14 @@ fn state_mode_ordering_survives_the_row_key_split() { std::fs::create_dir_all(&dir_a).unwrap(); std::fs::create_dir_all(&dir_b).unwrap(); - app.spawn_in("sleep 30", dir_b.clone()); // id 1: running, dir b - app.spawn_in("sleep 30", dir_a.clone()); // id 2: running, dir a - app.spawn_in("sleep 30", dir_b.clone()); // id 3: idle, dir b - app.spawn_in("sleep 30", dir_a.clone()); // id 4: idle, dir a - app.spawn_in("sleep 30", dir_b.clone()); // id 5: tagged, dir b - app.spawn_in("sleep 30", dir_a.clone()); // id 6: tagged + idle, dir a - app.spawn_in("true", dir_b.clone()); // id 7: finished, dir b - app.spawn_in("true", dir_a.clone()); // id 8: finished, dir a + app.spawn_in("sleep 30", &dir_b); // id 1: running, dir b + app.spawn_in("sleep 30", &dir_a); // id 2: running, dir a + app.spawn_in("sleep 30", &dir_b); // id 3: idle, dir b + app.spawn_in("sleep 30", &dir_a); // id 4: idle, dir a + app.spawn_in("sleep 30", &dir_b); // id 5: tagged, dir b + app.spawn_in("sleep 30", &dir_a); // id 6: tagged + idle, dir a + app.spawn_in("true", &dir_b); // id 7: finished, dir b + app.spawn_in("true", &dir_a); // id 8: finished, dir a app.wait_finished(7); app.wait_finished(8); for id in [5u64, 6] { @@ -826,11 +826,8 @@ fn rerun_key_is_gated_to_finished_tasks() { let mut app = App::new_local(30, 100); let dir = temp("app_rerun"); let marker = dir.join("marker"); - app.spawn_in("sleep 30", dir.to_path_buf()); // id 1: stays running - app.spawn_in( - &format!("echo run >> {}", marker.display()), - dir.to_path_buf(), - ); // id 2 + app.spawn_in("sleep 30", &dir); // id 1: stays running + app.spawn_in(&format!("echo run >> {}", marker.display()), &dir); // id 2 wait_until(Duration::from_secs(5), || { app.pump(); app.views @@ -855,9 +852,7 @@ fn rerun_key_is_gated_to_finished_tasks() { app.on_key_dashboard(key(KeyCode::Char('r'))); wait_until(Duration::from_secs(5), || { app.pump(); - std::fs::read_to_string(&marker) - .map(|s| s.lines().count() == 2) - .unwrap_or(false) + std::fs::read_to_string(&marker).is_ok_and(|s| s.lines().count() == 2) }); assert_eq!( std::fs::read_to_string(&marker).unwrap().lines().count(), @@ -1122,9 +1117,9 @@ fn esc_closes_the_picker_from_the_recovery_page() { fn recent_dirs_are_distinct_and_newest_first() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", PathBuf::from("/tmp")); // id 1 /tmp - app.spawn_in("sleep 5", inv.clone()); // id 2 invocation - app.spawn_in("sleep 5", PathBuf::from("/tmp")); // id 3 /tmp (dup) + app.spawn_in("sleep 5", Path::new("/tmp")); // id 1 /tmp + app.spawn_in("sleep 5", &inv); // id 2 invocation + app.spawn_in("sleep 5", Path::new("/tmp")); // id 3 /tmp (dup) let dirs = app.in_use_dirs(); assert_eq!(dirs.len(), 2, "duplicate dirs collapse"); @@ -1174,7 +1169,7 @@ fn recents_fixture(tag: &str) -> (App, Scratch) { app.invocation_dir = rust.join("fleetcom"); // Spawn oldest first so `in_use_dirs` returns Logria, crabapple, crabstep. for name in ["crabstep", "crabapple", "Logria"] { - app.spawn_in("sleep 5", rust.join(name)); + app.spawn_in("sleep 5", &rust.join(name)); } (app, root) } @@ -1331,7 +1326,7 @@ fn pickdir_empty_input_lists_every_recent() { fn pickdir_dedupes_a_recent_that_is_also_a_subdirectory() { let (mut app, root) = recents_fixture("pickdir_recent_dedupe"); let docs = root.join("Documents/Code/Rust/fleetcom/docs"); - app.spawn_in("sleep 5", docs.clone()); + app.spawn_in("sleep 5", &docs); type_pickdir(&mut app, "doc"); @@ -1352,8 +1347,8 @@ fn pickdir_dedupes_a_recent_that_is_also_a_subdirectory() { fn focus_by_id_survives_index_shift() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv.clone()); // id 1 - app.spawn_in("sleep 5", inv); // id 2 + app.spawn_in("sleep 5", &inv); // id 1 + app.spawn_in("sleep 5", &inv); // id 2 app.focused_id = Some(2); assert_eq!(app.views[app.focused_task().unwrap()].id, 2); @@ -1372,8 +1367,8 @@ fn focus_by_id_survives_index_shift() { fn rows_interleave_headers_and_tasks() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("a", inv); // id 1, invocation dir - app.spawn_in("b", PathBuf::from("/tmp")); // id 2, /tmp + app.spawn_in("a", &inv); // id 1, invocation dir + app.spawn_in("b", Path::new("/tmp")); // id 2, /tmp app.group_mode = GroupMode::Dir; let rows = app.list_rows(); @@ -1392,7 +1387,7 @@ fn dashboard_selection_stays_in_scroll_window() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); for _ in 0..8 { - app.spawn_in("sleep 5", inv.clone()); + app.spawn_in("sleep 5", &inv); } // A 4-row window over 9 rows (1 header + 8 tasks): walking the whole @@ -1427,9 +1422,9 @@ fn dashboard_selection_stays_in_scroll_window() { fn selection_wraps_at_list_edges() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv.clone()); // id 1 - app.spawn_in("sleep 5", inv.clone()); // id 2 - app.spawn_in("sleep 5", inv); // id 3 + app.spawn_in("sleep 5", &inv); // id 1 + app.spawn_in("sleep 5", &inv); // id 2 + app.spawn_in("sleep 5", &inv); // id 3 // Tag id 2 -> it sorts into a leading "In use" section, so the wrap // below crosses a section boundary. @@ -1463,7 +1458,7 @@ fn selection_wraps_at_list_edges() { fn navigation_is_noop_with_one_task() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); + app.spawn_in("sleep 5", &inv); assert_eq!(app.selected_id, Some(1)); let steps = [ @@ -1483,7 +1478,7 @@ fn app_with_two_sections() -> App { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); for _ in 0..4 { - app.spawn_in("sleep 5", inv.clone()); + app.spawn_in("sleep 5", &inv); } app.transport.send(Command::Tag { id: 3, on: true }); app.transport.send(Command::Tag { id: 4, on: true }); @@ -1578,7 +1573,7 @@ fn app_with_tagged_pair() -> App { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); for _ in 0..4 { - app.spawn_in("sleep 5", inv.clone()); + app.spawn_in("sleep 5", &inv); } app.transport.send(Command::Tag { id: 2, on: true }); app.transport.send(Command::Tag { id: 4, on: true }); @@ -1598,10 +1593,10 @@ fn app_with_tagged_pair() -> App { fn app_with_tags_split_across_groups() -> App { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv.clone(), "alpha"); // id 1 - app.spawn_grouped("sleep 5", inv.clone(), "alpha"); // id 2 - app.spawn_grouped("sleep 5", inv.clone(), "beta"); // id 3 - app.spawn_grouped("sleep 5", inv, "beta"); // id 4 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 2 + app.spawn_grouped("sleep 5", &inv, "beta"); // id 3 + app.spawn_grouped("sleep 5", &inv, "beta"); // id 4 app.group_mode = GroupMode::Custom; app.transport.send(Command::Tag { id: 1, on: true }); app.transport.send(Command::Tag { id: 3, on: true }); @@ -1654,8 +1649,8 @@ fn cycle_tagged_skips_untagged_tasks() { fn cycle_tagged_is_noop_without_tags() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv.clone()); // id 1 - app.spawn_in("sleep 5", inv); // id 2 + app.spawn_in("sleep 5", &inv); // id 1 + app.spawn_in("sleep 5", &inv); // id 2 app.selected_id = Some(1); app.on_key_dashboard(key(KeyCode::Char('M'))); @@ -1684,9 +1679,9 @@ fn cycle_tagged_from_untagged_selection_jumps_forward() { fn cycle_tagged_with_one_tag_holds_the_selection() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv.clone()); // id 1 - app.spawn_in("sleep 5", inv.clone()); // id 2 - app.spawn_in("sleep 5", inv); // id 3 + app.spawn_in("sleep 5", &inv); // id 1 + app.spawn_in("sleep 5", &inv); // id 2 + app.spawn_in("sleep 5", &inv); // id 3 app.transport.send(Command::Tag { id: 2, on: true }); app.pump(); app.selected_id = Some(2); // Start on the only tagged row. @@ -1975,7 +1970,7 @@ fn attached_wheel_honors_the_childs_1007_veto() { ); } -/// Open scrollback on modified PageUp; close on Esc or typing. +/// Open scrollback on modified `PageUp`; close on Esc or typing. #[test] fn scroll_view_entry_and_exit() { let (mut app, _) = App::attached(30, 100, "sleep 5"); @@ -2002,8 +1997,8 @@ fn scroll_view_entry_and_exit() { fn wheel_moves_dashboard_selection() { let mut app = App::new_local(30, 100); let dir = app.invocation_dir.clone(); - app.spawn_in("sleep 5", dir.clone()); // id 1 - app.spawn_in("sleep 5", dir); // id 2 + app.spawn_in("sleep 5", &dir); // id 1 + app.spawn_in("sleep 5", &dir); // id 2 app.selected_id = Some(1); let wheel = |kind| MouseEvent { @@ -2029,7 +2024,7 @@ fn group_picker_opens_on_g_only_with_a_selection() { assert_eq!(app.mode, Mode::Dashboard, "no selection: g must no-op"); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); + app.spawn_in("sleep 5", &inv); app.on_key_dashboard(key(KeyCode::Char('g'))); assert_eq!(app.mode, Mode::PickGroup(1)); } @@ -2040,10 +2035,10 @@ fn group_picker_opens_on_g_only_with_a_selection() { fn group_candidates_are_distinct_sorted_and_marked() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv.clone(), "beta"); // id 1 - app.spawn_grouped("sleep 5", inv.clone(), "alpha"); // id 2 - app.spawn_grouped("sleep 5", inv.clone(), "alpha"); // id 3, dup group - app.spawn_in("sleep 5", inv); // id 4, no group + app.spawn_grouped("sleep 5", &inv, "beta"); // id 1 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 2 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 3, dup group + app.spawn_in("sleep 5", &inv); // id 4, no group app.selected_id = Some(1); // group "beta" app.on_key_dashboard(key(KeyCode::Char('g'))); @@ -2074,11 +2069,11 @@ fn group_candidates_are_distinct_sorted_and_marked() { fn group_candidates_collate_case_insensitively() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv.clone(), "zebra"); // id 1 - app.spawn_grouped("sleep 5", inv.clone(), "API"); // id 2 - app.spawn_grouped("sleep 5", inv.clone(), "Review"); // id 3 - app.spawn_grouped("sleep 5", inv.clone(), "api"); // id 4 - app.spawn_grouped("sleep 5", inv, "api"); // id 5, exact duplicate + app.spawn_grouped("sleep 5", &inv, "zebra"); // id 1 + app.spawn_grouped("sleep 5", &inv, "API"); // id 2 + app.spawn_grouped("sleep 5", &inv, "Review"); // id 3 + app.spawn_grouped("sleep 5", &inv, "api"); // id 4 + app.spawn_grouped("sleep 5", &inv, "api"); // id 5, exact duplicate app.selected_id = Some(1); app.on_key_dashboard(key(KeyCode::Char('g'))); @@ -2106,8 +2101,8 @@ fn group_candidates_collate_case_insensitively() { fn group_filter_narrows_and_preselects_the_first_match() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv.clone(), "alpha"); // id 1 - app.spawn_grouped("sleep 5", inv, "beta"); // id 2 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "beta"); // id 2 // Keep alpha selected so filtered beta is not marked "(current)". app.selected_id = Some(1); app.on_key_dashboard(key(KeyCode::Char('g'))); @@ -2139,8 +2134,8 @@ fn group_filter_narrows_and_preselects_the_first_match() { fn group_enter_on_a_candidate_assigns_it() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv.clone(), "alpha"); // id 1 - app.spawn_in("sleep 5", inv); // id 2, no group + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 + app.spawn_in("sleep 5", &inv); // id 2, no group app.selected_id = Some(2); app.on_key_dashboard(key(KeyCode::Char('g'))); @@ -2157,7 +2152,7 @@ fn group_enter_on_a_candidate_assigns_it() { fn group_enter_on_novel_text_creates_the_group() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 app.on_key_dashboard(key(KeyCode::Char('g'))); type_into(&mut app, App::on_key_pickgroup, "gamma"); assert_eq!(app.group_candidates.len(), 1, "nothing matches"); @@ -2172,7 +2167,7 @@ fn group_enter_on_novel_text_creates_the_group() { fn group_enter_on_empty_input_clears_to_unassigned() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 app.on_key_dashboard(key(KeyCode::Char('g'))); assert_eq!(app.group_sel, 0, "empty input highlights the clear row"); app.on_key_pickgroup(key(KeyCode::Enter)); @@ -2186,7 +2181,7 @@ fn group_enter_on_empty_input_clears_to_unassigned() { fn group_esc_cancels_without_sending() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 app.on_key_dashboard(key(KeyCode::Char('g'))); type_into(&mut app, App::on_key_pickgroup, "gamma"); app.on_key_pickgroup(key(KeyCode::Esc)); @@ -2213,7 +2208,7 @@ fn find_palette_opens_on_slash_only_with_tasks() { assert!(app.find_candidates.is_empty()); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); + app.spawn_in("sleep 5", &inv); // Exercise opening find without a current selection. app.selected_id = None; app.on_key_dashboard(key(KeyCode::Char('/'))); @@ -2270,8 +2265,8 @@ fn find_empty_input_lists_every_task() { fn find_matches_case_insensitive_substrings() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv.clone()); // id 1 - app.spawn_in("true", inv); // id 2 + app.spawn_in("sleep 5", &inv); // id 1 + app.spawn_in("true", &inv); // id 2 app.on_key_dashboard(key(KeyCode::Char('/'))); find_type(&mut app, "SLEEP"); @@ -2301,8 +2296,8 @@ fn find_matches_case_insensitive_substrings() { fn find_matches_a_named_task_on_both_fields() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv.clone()); // id 1 - app.spawn_in("true", inv); // id 2 + app.spawn_in("sleep 5", &inv); // id 1 + app.spawn_in("true", &inv); // id 2 app.transport.send(Command::SetName { id: 1, name: Some("api tests".to_string()), @@ -2324,8 +2319,8 @@ fn find_matches_a_named_task_on_both_fields() { fn find_matches_group_names() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv.clone(), "backend"); // id 1 - app.spawn_in("sleep 5", inv); // id 2, no group + app.spawn_grouped("sleep 5", &inv, "backend"); // id 1 + app.spawn_in("sleep 5", &inv); // id 2, no group app.on_key_dashboard(key(KeyCode::Char('/'))); find_type(&mut app, "backend"); @@ -2338,7 +2333,7 @@ fn find_does_not_match_the_directory() { let mut app = App::new_local(30, 100); let dir = temp("findpalettedir"); let name = dir.file_name().unwrap().to_string_lossy().into_owned(); - app.spawn_in("sleep 5", dir.to_path_buf()); // id 1 + app.spawn_in("sleep 5", &dir); // id 1 assert!(name.contains("findpalettedir"), "scratch dir name: {name}"); app.on_key_dashboard(key(KeyCode::Char('/'))); @@ -2361,9 +2356,9 @@ fn find_does_not_match_the_directory() { fn find_enter_jumps_the_selection() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv.clone()); // id 1 - app.spawn_in("sleep 5", inv.clone()); // id 2 - app.spawn_in("sleep 5", inv); // id 3 + app.spawn_in("sleep 5", &inv); // id 1 + app.spawn_in("sleep 5", &inv); // id 2 + app.spawn_in("sleep 5", &inv); // id 3 app.selected_id = Some(1); app.on_key_dashboard(key(KeyCode::Char('/'))); @@ -2385,8 +2380,8 @@ fn find_enter_jumps_the_selection() { fn find_esc_leaves_the_selection_alone() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv.clone()); // id 1 - app.spawn_in("true", inv); // id 2 + app.spawn_in("sleep 5", &inv); // id 1 + app.spawn_in("true", &inv); // id 2 app.selected_id = Some(1); app.on_key_dashboard(key(KeyCode::Char('/'))); @@ -2404,7 +2399,7 @@ fn find_esc_leaves_the_selection_alone() { fn find_enter_without_candidates_keeps_the_panel_open() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); + app.spawn_in("sleep 5", &inv); app.on_key_dashboard(key(KeyCode::Char('/'))); find_type(&mut app, "zzz"); @@ -2426,7 +2421,7 @@ fn find_enter_without_candidates_keeps_the_panel_open() { fn find_panel_rows_name_the_task_and_its_section() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); + app.spawn_in("sleep 5", &inv); app.transport.send(Command::SetName { id: 1, name: Some("api tests".to_string()), @@ -2448,8 +2443,8 @@ fn find_panel_rows_name_the_task_and_its_section() { fn find_paste_filters_the_candidates() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv.clone()); // id 1 - app.spawn_in("true", inv); // id 2 + app.spawn_in("sleep 5", &inv); // id 1 + app.spawn_in("true", &inv); // id 2 app.on_key_dashboard(key(KeyCode::Char('/'))); app.on_paste("true"); @@ -2475,7 +2470,7 @@ fn unfocused_terminal_mutes_the_highlight_rows() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); + app.spawn_in("sleep 5", &inv); app.selected_id = Some(1); // The dashboard's only reverse-video line is the selected row, so its @@ -2520,7 +2515,7 @@ fn controls_overlay_opens_on_question_and_closes_on_question_esc_q() { fn plain_slash_still_opens_the_find_palette() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); + app.spawn_in("sleep 5", &inv); app.on_key_dashboard(key(KeyCode::Char('/'))); assert_eq!(app.mode, Mode::Find); } @@ -2530,7 +2525,7 @@ fn plain_slash_still_opens_the_find_palette() { fn controls_overlay_ignores_dashboard_keys() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); + app.spawn_in("sleep 5", &inv); app.on_key_dashboard(key(KeyCode::Char('?'))); app.on_key_controls(key(KeyCode::Char('m'))); @@ -2622,7 +2617,7 @@ fn rename_prompt_opens_on_shift_r_only_with_a_selection() { assert_eq!(app.mode, Mode::Dashboard, "no selection: R must no-op"); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); + app.spawn_in("sleep 5", &inv); app.on_key_dashboard(key(KeyCode::Char('R'))); assert_eq!(app.mode, Mode::Rename(1)); assert_eq!(app.input.as_str(), "", "an unnamed task prefills empty"); @@ -2643,7 +2638,7 @@ fn rename_prompt_opens_on_shift_r_only_with_a_selection() { fn rename_enter_sends_the_typed_name() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); + app.spawn_in("sleep 5", &inv); app.on_key_dashboard(key(KeyCode::Char('R'))); type_into(&mut app, App::on_key_rename, "api server"); app.on_key_rename(key(KeyCode::Enter)); @@ -2659,7 +2654,7 @@ fn rename_enter_sends_the_typed_name() { fn rename_enter_on_empty_input_clears_the_name() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); + app.spawn_in("sleep 5", &inv); app.transport.send(Command::SetName { id: 1, name: Some("api".to_string()), @@ -2681,7 +2676,7 @@ fn rename_enter_on_empty_input_clears_the_name() { fn rename_esc_cancels_without_sending() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); + app.spawn_in("sleep 5", &inv); app.transport.send(Command::SetName { id: 1, name: Some("api".to_string()), @@ -2705,7 +2700,7 @@ fn rename_esc_cancels_without_sending() { fn rename_caret_keys_edit_mid_name() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_in("sleep 5", inv); + app.spawn_in("sleep 5", &inv); app.transport.send(Command::SetName { id: 1, name: Some("api".to_string()), @@ -2748,7 +2743,7 @@ fn rename_caret_keys_edit_mid_name() { fn ctrl_chords_never_insert_their_letter() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 app.on_key_dashboard(key(KeyCode::Char('w'))); app.on_key_savesession(ctrl(KeyCode::Char('k'))); @@ -2831,8 +2826,8 @@ fn pickdir_refreshes_on_edits_not_caret_motion() { fn pickgroup_caret_edits_refresh_the_filter() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv.clone(), "alpha"); // id 1 - app.spawn_grouped("sleep 5", inv, "beta"); // id 2 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "beta"); // id 2 app.on_key_dashboard(key(KeyCode::Char('g'))); app.on_key_pickgroup(key(KeyCode::Char('b'))); @@ -2891,7 +2886,7 @@ fn multibyte_chars_edit_cleanly_in_a_prompt() { fn custom_mode_spawn_inherits_the_selected_group() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 app.group_mode = GroupMode::Custom; app.on_key_dashboard(key(KeyCode::Char('n'))); @@ -2914,7 +2909,7 @@ fn custom_mode_spawn_inherits_the_selected_group() { fn dir_picker_handoff_inherits_the_selected_group_in_custom_mode() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 app.group_mode = GroupMode::Custom; app.on_key_dashboard(key(KeyCode::Char('@'))); @@ -2930,7 +2925,7 @@ fn dir_picker_handoff_inherits_the_selected_group_in_custom_mode() { fn state_and_dir_mode_spawns_stay_unassigned() { let mut app = App::new_local(30, 100); let inv = app.invocation_dir.clone(); - app.spawn_grouped("sleep 5", inv, "alpha"); // id 1 + app.spawn_grouped("sleep 5", &inv, "alpha"); // id 1 for mode in [GroupMode::State, GroupMode::Dir] { app.group_mode = mode; @@ -2955,7 +2950,7 @@ fn dir_mode_spawn_inherits_the_selected_directory() { let mut app = App::new_local(30, 100); let scratch = temp("app_dir_inherit"); let (dir, inv) = (scratch.to_path_buf(), app.invocation_dir.clone()); - app.spawn_in("sleep 5", dir.clone()); // id 1 + app.spawn_in("sleep 5", &dir); // id 1 for (mode, want) in [ (GroupMode::Dir, &dir), @@ -2974,7 +2969,10 @@ fn dir_mode_spawn_inherits_the_selected_directory() { app.on_key_spawn(key(KeyCode::Enter)); app.pump(); let v = app.views.iter().find(|v| v.id == 2).unwrap(); - assert_eq!(v.cwd, dir, "the spawn must launch in the inherited directory"); + assert_eq!( + v.cwd, dir, + "the spawn must launch in the inherited directory" + ); } // --- `n` spawn prompt: Agent page -------------------------------------- @@ -2985,7 +2983,7 @@ impl App { /// Do not forward Enter to a core. fn with_agents(agents: &[&str]) -> (Self, Arc>>) { let mut app = Self::new_local(30, 100); - app.agents = agents.iter().map(|a| a.to_string()).collect(); + app.agents = agents.iter().map(ToString::to_string).collect(); app.transport = Scripted::unplugged(); let sent = app.record_sends(); (app, sent) @@ -3005,7 +3003,7 @@ impl App { } /// Switch between Command and Agent with Tab only when agents are installed. With no -/// agents, ignore Tab and BackTab and keep the current page. +/// agents, ignore Tab and `BackTab` and keep the current page. #[test] fn spawn_tab_toggles_pages_only_with_agents() { let (mut app, _) = App::with_agents(&[]); @@ -3441,7 +3439,7 @@ fn set_watch_resends_on_kind_change_with_the_same_id() { "until [ -e {f} ]; do sleep 0.05; done; printf '\\033]52;c;cG9zdA==\\007'; sleep 30", f = flag.display() ); - app.spawn_in(&cmd, cwd); + app.spawn_in(&cmd, &cwd); let id = app.views[0].id; // Change only the attachment mode. @@ -4091,7 +4089,7 @@ fn wants_mouse_child_keeps_the_left_button() { fn frame_is_wrapped_in_one_synchronized_update() { let mut app = App::new_local(30, 100); let dir = app.invocation_dir.clone(); - app.spawn_in("sleep 5", dir); + app.spawn_in("sleep 5", &dir); for (label, mode) in [ ("dashboard", Mode::Dashboard), ("peek", Mode::Peek), @@ -4120,7 +4118,7 @@ fn frame_is_wrapped_in_one_synchronized_update() { fn unchanged_frame_emits_nothing() { let mut app = App::new_local(30, 100); let dir = app.invocation_dir.clone(); - app.spawn_in("sleep 5", dir); + app.spawn_in("sleep 5", &dir); app.mode = Mode::Peek; let mut first = Vec::new(); assert!( @@ -4215,8 +4213,8 @@ fn chord() -> KeyEvent { fn flagship_pair() -> (App, u64, u64) { let mut app = App::new_local(30, 100); let dir = app.invocation_dir.clone(); - app.spawn_in("sleep 30", dir.clone()); - app.spawn_in("sleep 30", dir); + app.spawn_in("sleep 30", &dir); + app.spawn_in("sleep 30", &dir); let (a, q) = (app.views[0].id, app.views[1].id); app.selected_id = Some(q); app.hit(key(KeyCode::Char(']'))); @@ -4416,7 +4414,7 @@ fn enter_onto_flagship_sets_dashboard_return() { fn mark_then_enter_before_snapshot_returns_to_dashboard() { let mut app = App::new_local(30, 100); let dir = app.invocation_dir.clone(); - app.spawn_in("sleep 30", dir); + app.spawn_in("sleep 30", &dir); let q = app.views[0].id; app.selected_id = Some(q); diff --git a/src/daemon.rs b/src/daemon.rs index fb99e96..d2df5c2 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -69,7 +69,7 @@ fn runtime_dir() -> PathBuf { resolve_runtime_dir( std::env::var(FLEETCOM_RUNTIME_DIR).ok(), std::env::var("XDG_RUNTIME_DIR").ok(), - std::env::temp_dir(), + &std::env::temp_dir(), nix::unistd::getuid().as_raw(), ) } @@ -79,7 +79,7 @@ fn runtime_dir() -> PathBuf { fn resolve_runtime_dir( override_dir: Option, xdg: Option, - tmp: PathBuf, + tmp: &Path, uid: u32, ) -> PathBuf { if let Some(d) = override_dir { @@ -345,7 +345,7 @@ fn spawn_daemon(dir: &Path) -> io::Result<()> { cmd.arg("--daemon") .stdin(Stdio::null()) .stdout(Stdio::null()) - .stderr(log.map(Stdio::from).unwrap_or_else(Stdio::null)) + .stderr(log.map_or_else(Stdio::null, Stdio::from)) .process_group(0); // Pass the client's scrollback flag to the daemon through its environment. if let Some(lines) = supervisor::scrollback_flag() { @@ -775,27 +775,22 @@ mod tests { let tmp = PathBuf::from("/tmpdir"); // Prefer the explicit override over all other sources. assert_eq!( - resolve_runtime_dir( - Some("/override".into()), - Some("/xdg".into()), - tmp.clone(), - 501 - ), + resolve_runtime_dir(Some("/override".into()), Some("/xdg".into()), &tmp, 501), PathBuf::from("/override") ); // XDG next, namespaced. assert_eq!( - resolve_runtime_dir(None, Some("/run/user/501".into()), tmp.clone(), 501), + resolve_runtime_dir(None, Some("/run/user/501".into()), &tmp, 501), PathBuf::from("/run/user/501/fleetcom") ); // An *empty* XDG value is unset in spirit: fall through. assert_eq!( - resolve_runtime_dir(None, Some(String::new()), tmp.clone(), 501), + resolve_runtime_dir(None, Some(String::new()), &tmp, 501), PathBuf::from("/tmpdir/fleetcom-501") ); // The uid-suffixed tmp fallback (the macOS steady state). assert_eq!( - resolve_runtime_dir(None, None, tmp, 42), + resolve_runtime_dir(None, None, &tmp, 42), PathBuf::from("/tmpdir/fleetcom-42") ); } diff --git a/src/format.rs b/src/format.rs index 6045417..7abe97f 100644 --- a/src/format.rs +++ b/src/format.rs @@ -200,7 +200,7 @@ mod tests { // Byte ordering produces a different order for mixed-case names. let mut bytewise = vec!["Zebra", "api", "API", "banana", "Apple"]; - bytewise.sort(); + bytewise.sort_unstable(); assert_eq!(bytewise, vec!["API", "Apple", "Zebra", "api", "banana"]); } diff --git a/src/frame.rs b/src/frame.rs index c50df6c..a9122d1 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -30,7 +30,7 @@ pub const fn fits_base64(n: usize) -> bool { } /// Maximum time one frame write to a socket peer may block. A peer that -/// stops draining its socket (crashed, SIGSTOPped, hostile) must not wedge +/// stops draining its socket (crashed, `SIGSTOPped`, hostile) must not wedge /// the writer: the daemon's event writes and the client's command writes /// both cap here and treat expiry as a dead connection. pub const SEND_TIMEOUT: Duration = Duration::from_secs(5); diff --git a/src/harness/omp.rs b/src/harness/omp.rs index 2a6cd82..9e46d9a 100644 --- a/src/harness/omp.rs +++ b/src/harness/omp.rs @@ -10,7 +10,7 @@ //! Capture is unavailable before omp 18.3.2: without the agent kind in the //! extension context, top-level sessions cannot be distinguished from sub-agents. //! -//! omp's IDs are UUIDv7. [`is_uuid`](super::is_uuid) validates the 8-4-4-4-12 +//! omp's IDs are `UUIDv7`. [`is_uuid`](super::is_uuid) validates the 8-4-4-4-12 //! lowercase-hex shape and not the version field, so they pass unchanged. use std::path::Path; @@ -51,7 +51,7 @@ mod tests { plan, }; - /// Valid UUIDv7 used in capture payloads. + /// Valid `UUIDv7` used in capture payloads. const CAPTURED: &str = "01a0077c-e18e-7000-ae0b-016f4834b6e9"; /// The extension path from [`paths`]. diff --git a/src/main.rs b/src/main.rs index e41b5b5..27d2a01 100644 --- a/src/main.rs +++ b/src/main.rs @@ -377,7 +377,7 @@ mod tests { use super::*; fn parse(args: &[&str]) -> Result { - let owned: Vec = args.iter().map(|s| s.to_string()).collect(); + let owned: Vec = args.iter().map(ToString::to_string).collect(); parse_args(&owned) } diff --git a/src/protocol.rs b/src/protocol.rs index 8a0784a..d265a8d 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -469,9 +469,9 @@ pub fn opt_str(v: &jzon::JsonValue) -> Option> { /// Insert `key` only when the optional field is set; absence encodes `None` /// on the wire (see [`opt_str`]). -pub fn insert_opt_str(o: &mut jzon::JsonValue, key: &str, val: &Option) { +pub fn insert_opt_str(o: &mut jzon::JsonValue, key: &str, val: Option<&str>) { if let Some(s) = val { - let _ = o.insert(key, s.as_str()); + let _ = o.insert(key, s); } } @@ -586,7 +586,7 @@ pub fn encode_command(cmd: &Command) -> (u8, Vec) { "command": command.as_str(), "cwd": path_b64(cwd), }; - insert_opt_str(&mut o, "group", group); + insert_opt_str(&mut o, "group", group.as_deref()); o } Command::SpawnAgent { agent, cwd, group } => { @@ -595,7 +595,7 @@ pub fn encode_command(cmd: &Command) -> (u8, Vec) { "agent": agent.as_str(), "cwd": path_b64(cwd), }; - insert_opt_str(&mut o, "group", group); + insert_opt_str(&mut o, "group", group.as_deref()); o } Command::Kill { id } => jzon::object! { "t": "kill", "id": *id }, @@ -607,19 +607,19 @@ pub fn encode_command(cmd: &Command) -> (u8, Vec) { Command::SetGroup { id, group } => { let mut o = jzon::object! { "t": "group", "id": *id }; // Absence of `g` encodes an unassigned task. - insert_opt_str(&mut o, "g", group); + insert_opt_str(&mut o, "g", group.as_deref()); o } Command::SetName { id, name } => { let mut o = jzon::object! { "t": "name", "id": *id }; // Absence of `n` encodes an unnamed task. - insert_opt_str(&mut o, "n", name); + insert_opt_str(&mut o, "n", name.as_deref()); o } Command::Resize { rows, cols } => jzon::object! { "t": "resize", - "rows": *rows as u64, - "cols": *cols as u64, + "rows": u64::from(*rows), + "cols": u64::from(*cols), }, Command::Watch { id, attached } => { // Preserve an absent watch ID as JSON null. @@ -644,8 +644,8 @@ pub fn encode_command(cmd: &Command) -> (u8, Vec) { "t": "mouse", "id": *id, "k": k, - "col": *col as u64, - "row": *row as u64, + "col": u64::from(*col), + "row": u64::from(*row), }; if let Some(b) = btn { let _ = o.insert("b", b as u64); @@ -662,7 +662,7 @@ pub fn encode_command(cmd: &Command) -> (u8, Vec) { "ch" } Key::F(n) => { - let _ = o.insert("n", *n as u64); + let _ = o.insert("n", u64::from(*n)); "f" } Key::Up => "up", @@ -703,7 +703,7 @@ pub fn encode_command(cmd: &Command) -> (u8, Vec) { }; let mut o = jzon::object! { "t": "sb", "id": *id, "a": a }; if let Some(n) = n { - let _ = o.insert("n", n as u64); + let _ = o.insert("n", u64::from(n)); } o } @@ -883,8 +883,8 @@ pub fn encode_event(ev: &Event) -> (u8, Vec) { let _ = o.insert("flagship", tv.flagship); let _ = o.insert("managed", tv.managed); // Group and name fields are present only when set. - insert_opt_str(&mut o, "group", &tv.group); - insert_opt_str(&mut o, "name", &tv.name); + insert_opt_str(&mut o, "group", tv.group.as_deref()); + insert_opt_str(&mut o, "name", tv.name.as_deref()); let _ = o.insert("life", tv.lifecycle.label()); let _ = o.insert("preview", tv.preview.text.as_str()); // Matcher rules are process-local and omitted from the wire. @@ -927,7 +927,7 @@ pub fn encode_event(ev: &Event) -> (u8, Vec) { Event::Screen(sv) => { let header = jzon::object! { "id": sv.id, - "cursor": [sv.cursor.0 as u64, sv.cursor.1 as u64], + "cursor": [u64::from(sv.cursor.0), u64::from(sv.cursor.1)], "hide": sv.hide_cursor, "mouse": sv.wants_mouse, "alt": sv.alt_screen, diff --git a/src/protocol_tests.rs b/src/protocol_tests.rs index 1ac7925..2394530 100644 --- a/src/protocol_tests.rs +++ b/src/protocol_tests.rs @@ -295,7 +295,7 @@ fn hello_with_malformed_env_is_rejected() { r#"[["P@TH","L2Jpbg=="]]"#, // invalid base64 character r#"[["QUFBQUE","L2Jpbg=="]]"#, // truncated: missing padding r#"[["UEFUSA==","AAAA="]]"#, // bad padding length - r#"[[[80],[65]]]"#, // env pairs must contain base64 strings + r"[[[80],[65]]]", // env pairs must contain base64 strings r#"["PATH=/bin"]"#, // flat string pair ] { // Keep the cwd valid so each case isolates env validation. @@ -550,7 +550,7 @@ fn lifecycle_and_age_fields_round_trip() { Lifecycle::Ok, Lifecycle::Failed, ] { - for age in [None, Some(Duration::from_millis(12_000))] { + for age in [None, Some(Duration::from_secs(12))] { let live = matches!(lifecycle, Lifecycle::Active | Lifecycle::Idle); let tasks = Event::Tasks(vec![TaskView { lifecycle, diff --git a/src/selection.rs b/src/selection.rs index b47f5d7..df6b314 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -28,7 +28,7 @@ impl Selection { } /// Whether the head and anchor occupy the same cell. - pub fn is_click(&self) -> bool { + pub fn is_click(self) -> bool { self.anchor == self.head } @@ -45,7 +45,7 @@ impl Selection { /// /// Each selected screen row occupies one joined line, even when its /// selected span is empty. - pub fn extract(&self, rows: &[String]) -> String { + pub fn extract(self, rows: &[String]) -> String { let Some(last) = rows.len().checked_sub(1) else { return String::new(); }; @@ -65,12 +65,7 @@ impl Selection { /// Endpoints are ordered and clamped before wide glyphs are expanded and /// trailing whitespace is removed. Returns `None` outside the selection /// or when the selected span is empty after trimming. - pub fn row_segment<'a>( - &self, - row: usize, - text: &'a str, - last_row: usize, - ) -> Option<(u16, &'a str)> { + pub fn row_segment(self, row: usize, text: &str, last_row: usize) -> Option<(u16, &str)> { let (start, end) = self.bounds(last_row); if row < start.0 || row > end.0 { return None; @@ -85,7 +80,7 @@ impl Selection { /// Clamp endpoints to the last row, then order them by row and column. Clamp before /// ordering: both endpoints may be clamped to the same row. - fn bounds(&self, last: usize) -> ((usize, usize), (usize, usize)) { + fn bounds(self, last: usize) -> ((usize, usize), (usize, usize)) { let clamp = |(row, col): (u16, u16)| ((row as usize).min(last), col as usize); let (mut start, mut end) = (clamp(self.anchor), clamp(self.head)); if start > end { diff --git a/src/session.rs b/src/session.rs index c08011a..bf6298b 100644 --- a/src/session.rs +++ b/src/session.rs @@ -127,12 +127,12 @@ fn dirs_json(cfg: &SessionConfig) -> jzon::JsonValue { EntryKind::Literal(cmd) => jzon::object! { "cmd": cmd.as_str() }, EntryKind::Managed { agent, resume } => { let mut m = jzon::object! { "agent": agent.as_str() }; - insert_opt_str(&mut m, "resume", resume); + insert_opt_str(&mut m, "resume", resume.as_deref()); m } }; - insert_opt_str(&mut m, "group", &e.group); - insert_opt_str(&mut m, "name", &e.name); + insert_opt_str(&mut m, "group", e.group.as_deref()); + insert_opt_str(&mut m, "name", e.name.as_deref()); let _ = arr.push(m); } let _ = dirs.insert(dir, arr); @@ -1169,7 +1169,7 @@ mod tests { SessionConfig::from([ ("dirs".into(), vec![]), ("name".into(), vec![e("")]), - ("".into(), vec![]), + (String::new(), vec![]), ]) ); } @@ -1198,7 +1198,7 @@ mod tests { } assert_eq!( from_json(r#"{"name": "", "dirs": {}}"#).unwrap().0, - Some("".into()) + Some(String::new()) ); } diff --git a/src/supervisor.rs b/src/supervisor.rs index 80d8c92..68a4347 100644 --- a/src/supervisor.rs +++ b/src/supervisor.rs @@ -19,7 +19,8 @@ use crate::{ harness::{self, Harness, Intent, assets}, path, protocol::{ - Command, Event, LaunchContext, ScreenView, ScrollAction, TaskView, UNASSIGNED, env_get, + Command, Event, FLEETCOM_RUNTIME_DIR, LaunchContext, ScreenView, ScrollAction, TaskView, + UNASSIGNED, env_get, }, session::{self, EntryKind, SessionConfig, SessionEntry}, task::{Exec, Task, WriteRefused}, @@ -288,7 +289,7 @@ impl Recovery { pub struct Supervisor { tasks: Vec, - /// Removed tasks whose process groups may still be winding down: TERMed at + /// Removed tasks whose process groups may still be winding down: `TERMed` at /// removal, escalated to KILL by `reap` at grace end, and dropped once the /// leader's zombie is collected. Invisible to `tick` snapshots, so the row /// disappears instantly while the sweep runs behind it. @@ -433,8 +434,8 @@ impl Supervisor { command, cwd, group, - } => self.spawn(&command, cwd, group), - Command::SpawnAgent { agent, cwd, group } => self.spawn_agent(&agent, cwd, group), + } => self.spawn(&command, &cwd, group), + Command::SpawnAgent { agent, cwd, group } => self.spawn_agent(&agent, &cwd, group), Command::Kill { id } => self.with_task(id, Task::terminate), Command::Remove { id } => { if let Some(i) = self.index_of(id) { @@ -446,7 +447,7 @@ impl Supervisor { Command::Tag { id, on } => self.with_task(id, |t| t.tagged = on), Command::Flagship { id } => self.flagship = id, Command::SetGroup { id, group } => { - self.with_task(id, |t| t.group = normalize_group(group)) + self.with_task(id, |t| t.group = normalize_group(group)); } Command::SetName { id, name } => self.with_task(id, |t| t.name = normalize_label(name)), Command::Resize { rows, cols } => { @@ -487,11 +488,11 @@ impl Supervisor { // which only the core's emulator can see. Command::Paste { id, bytes } => self.deliver(id, "paste", |t| t.send_paste(&bytes)), Command::Mouse { id, kind, col, row } => { - self.deliver(id, "mouse input", |t| t.send_mouse(kind, col, row)) + self.deliver(id, "mouse input", |t| t.send_mouse(kind, col, row)); } // Encode keys here because the child's cursor-key mode is core-side. Command::Key { id, code, mods } => { - self.deliver(id, "key input", |t| t.send_key(code, mods)) + self.deliver(id, "key input", |t| t.send_key(code, mods)); } Command::Scrollback { id, action } => self.with_task(id, |t| t.scroll_view(action)), Command::SaveSession { name } => self.save_session(&name), @@ -784,17 +785,15 @@ impl Supervisor { /// partitioned by session directory. Assets are cached by canonical root, /// and installation failure disables instrumentation for the spawn. fn ensure_capture_assets(&mut self) -> Option<&assets::CaptureAssets> { - let root = self - .launch_env_path(crate::protocol::FLEETCOM_RUNTIME_DIR) - .or_else(|| { - assets::runtime_root().map(|base| { - let key = self - .sessions_root() - .map(PathBuf::into_os_string) - .unwrap_or_default(); - base.join(fnv1a_hex(key.as_encoded_bytes())) - }) - })?; + let root = self.launch_env_path(FLEETCOM_RUNTIME_DIR).or_else(|| { + assets::runtime_root().map(|base| { + let key = self + .sessions_root() + .map(PathBuf::into_os_string) + .unwrap_or_default(); + base.join(fnv1a_hex(key.as_encoded_bytes())) + }) + })?; if let Ok(key) = std::fs::canonicalize(&root) && self.capture.contains_key(&key) { @@ -869,7 +868,7 @@ impl Supervisor { self.cols, self.scrollback, &env, - Arc::clone(&self.waker), + &self.waker, )?; if let Some((h, home, capture_file, resume_id)) = meta { task.harness = Some(h); @@ -932,7 +931,7 @@ impl Supervisor { true } - fn spawn(&mut self, command: &str, cwd: PathBuf, group: Option) { + fn spawn(&mut self, command: &str, cwd: &Path, group: Option) { if command.len() > MAX_COMMAND_LEN { self.status(format!( "command too long ({} bytes, limit {}), not spawning", @@ -947,14 +946,14 @@ impl Supervisor { let Some(launch) = self.launch_or_refuse() else { return; }; - let admitted = self.admit(&Launch::Literal(command), &cwd, &launch.env, group, None); + let admitted = self.admit(&Launch::Literal(command), cwd, &launch.env, group, None); self.report_spawn(admitted); } /// Launch registered `agent` as a managed task with a fresh conversation. Resolve the /// binary on the launch context's current `PATH`; do not accept a path from the client. /// Refuse to spawn when no binary is found. - fn spawn_agent(&mut self, agent: &str, cwd: PathBuf, group: Option) { + fn spawn_agent(&mut self, agent: &str, cwd: &Path, group: Option) { if !self.below_task_ceiling() { return; } @@ -965,7 +964,7 @@ impl Supervisor { Ok(m) => m, Err(why) => return self.status(format!("{why}, not spawning")), }; - let admitted = self.admit(&Launch::Managed(managed), &cwd, &launch.env, group, None); + let admitted = self.admit(&Launch::Managed(managed), cwd, &launch.env, group, None); self.report_spawn(admitted); } diff --git a/src/supervisor_capture_tests.rs b/src/supervisor_capture_tests.rs index f60298e..7fbca5e 100644 --- a/src/supervisor_capture_tests.rs +++ b/src/supervisor_capture_tests.rs @@ -209,7 +209,7 @@ fn spawn_claude_pins_an_id_and_layers_settings() { let (bin, runtime) = (dir.join("bin"), dir.join("run")); install_stub(&bin, "claude", &dir); let mut s = sup_ctx(agent_ctx(&bin, &runtime, dir.to_path_buf())); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); let argv = wait_argv(&mut s, &dir.join("argv")); let si = argv @@ -344,7 +344,7 @@ fn typed_agent_word_is_literal_runs_verbatim_and_saves_as_text() { // Install a namespace by launching a managed sibling. Write a capture for task 1 there, // stamped with task 1's leader PID, and create a registry record keyed to that PID. - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); let ns = s.tasks[1] .capture_file .as_deref() @@ -389,7 +389,7 @@ fn rerun_cannot_read_the_old_runs_stale_capture() { dir.to_path_buf(), &[("FLEETCOM_CONFIG_DIR", &config)], )); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); let id = s.tasks[0].id; // The old run's final capture becomes the new run's launch ID. let old_cap = s.tasks[0].capture_file.clone().expect("capture file set"); @@ -428,13 +428,13 @@ fn reconnect_with_unchanged_root_preserves_capture_files() { let (bin, runtime) = (dir.join("bin"), dir.join("run")); install_stub(&bin, "claude", &dir); let mut s = sup_ctx(agent_ctx(&bin, &runtime, dir.to_path_buf())); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); let cap = s.tasks[0].capture_file.clone().expect("capture file set"); std::fs::write(&cap, "{}").unwrap(); // The client reconnects with an identical env and spawns again. s.set_launch_context(agent_ctx(&bin, &runtime, dir.to_path_buf())); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); assert_eq!(s.tasks.len(), 2); assert!( cap.exists(), @@ -449,16 +449,16 @@ fn returning_to_a_prior_root_preserves_its_live_captures() { let (bin, root_a, root_b) = (dir.join("bin"), dir.join("run-a"), dir.join("run-b")); install_stub(&bin, "claude", &dir); let mut s = sup_ctx(agent_ctx(&bin, &root_a, dir.to_path_buf())); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); let cap_a = s.tasks[0].capture_file.clone().expect("capture file set"); std::fs::write(&cap_a, "{}").unwrap(); // The client reconnects under root B, spawns, then returns to A and // spawns again. s.set_launch_context(agent_ctx(&bin, &root_b, dir.to_path_buf())); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); s.set_launch_context(agent_ctx(&bin, &root_a, dir.to_path_buf())); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); assert_eq!(s.tasks.len(), 3); assert!( @@ -485,7 +485,7 @@ fn remove_deletes_the_capture_file_under_the_spawn_root() { let (bin, root_a, root_b) = (dir.join("bin"), dir.join("run-a"), dir.join("run-b")); install_stub(&bin, "claude", &dir); let mut s = sup_ctx(agent_ctx(&bin, &root_a, dir.to_path_buf())); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); let _ = wait_argv(&mut s, &dir.join("argv")); let id = s.tasks[0].id; wait_for_lifecycle(&mut s, id, |l| l == Lifecycle::Ok); @@ -495,7 +495,7 @@ fn remove_deletes_the_capture_file_under_the_spawn_root() { // Root B is installed by a newer spawn; a same-id file under it must // survive the A task's removal. s.set_launch_context(agent_ctx(&bin, &root_b, dir.to_path_buf())); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); let decoy = s.tasks[1] .capture_file .as_deref() @@ -531,7 +531,7 @@ fn spawn_codex_installs_the_notify_and_embedded_overrides() { dir.to_path_buf(), &[("CODEX_HOME", &dir.join("codex_home"))], )); - s.spawn_agent("codex", dir.to_path_buf(), None); + s.spawn_agent("codex", &dir, None); let argv = wait_argv(&mut s, &dir.join("argv")); let ci = argv @@ -577,7 +577,7 @@ fn spawn_grok_pins_an_id_and_injects_nothing_else() { dir.to_path_buf(), &[("FLEETCOM_CONFIG_DIR", &config)], )); - s.spawn_agent("grok", dir.to_path_buf(), None); + s.spawn_agent("grok", &dir, None); let argv = wait_argv(&mut s, &dir.join("argv")); assert_eq!( @@ -619,7 +619,7 @@ fn spawn_omp_loads_the_capture_extension() { let (bin, runtime) = (dir.join("bin"), dir.join("run")); install_stub(&bin, "omp", &dir); let mut s = sup_ctx(agent_ctx(&bin, &runtime, dir.to_path_buf())); - s.spawn_agent("omp", dir.to_path_buf(), None); + s.spawn_agent("omp", &dir, None); let argv = wait_argv(&mut s, &dir.join("argv")); let ei = argv @@ -697,7 +697,7 @@ fn printed_resume_hints_do_not_change_saved_recovery_or_rerun_targets() { ], )); s.set_recovery_timing(Duration::from_millis(20), Duration::from_millis(100)); - s.spawn_agent(tool, dir.to_path_buf(), None); + s.spawn_agent(tool, &dir, None); s.tasks[0].group = Some("agents".into()); s.tasks[0].name = Some(tool.into()); let expected_id = match tool { @@ -835,7 +835,7 @@ fn resume_id_precedence_registry_over_spawn_under_capture() { ("CLAUDE_CONFIG_DIR", &claude_home), ], )); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); let injected = s.tasks[0] .resume_id .clone() @@ -898,7 +898,7 @@ until [ -e '{d}/done' ]; do sleep 0.05; done"#, ("CLAUDE_CONFIG_DIR", &claude_home), ], )); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); let pinned = s.tasks[0] .resume_id .clone() @@ -1000,8 +1000,8 @@ fn silent_codex_tasks_save_and_recover_without_a_resume_id() { ], )); s.set_recovery_timing(Duration::from_millis(20), Duration::from_millis(100)); - s.spawn_agent("codex", dir.to_path_buf(), None); - s.spawn_agent("codex", dir.to_path_buf(), None); + s.spawn_agent("codex", &dir, None); + s.spawn_agent("codex", &dir, None); assert_eq!(s.tasks.len(), 2); assert!(reap_until(&mut s, Duration::from_secs(5), |s| s .tasks @@ -1104,7 +1104,7 @@ fn codex_capture_resolves_each_notifying_thread_to_the_root() { ("CODEX_HOME", &codex_home), ], )); - s.spawn_agent("codex", dir.to_path_buf(), None); + s.spawn_agent("codex", &dir, None); assert!(s.tasks[0].resume_id.is_none(), "codex pins no id at launch"); assert_eq!(arrive(&s, &codex_home, &title_turn()), None); @@ -1248,7 +1248,7 @@ fn home_only_launch_env_targets_the_clients_dot_codex() { dir.to_path_buf(), &[("FLEETCOM_CONFIG_DIR", &config), ("HOME", &home)], )); - s.spawn_agent("codex", dir.to_path_buf(), None); + s.spawn_agent("codex", &dir, None); assert_eq!( s.tasks[0].harness_home.as_deref(), Some(codex_home.as_path()), @@ -1307,7 +1307,7 @@ fn stale_inherited_notify_chain_is_never_executed() { stale.as_os_str().to_os_string(), )); let mut s = sup_ctx(ctx); - s.spawn_agent("codex", dir.to_path_buf(), None); + s.spawn_agent("codex", &dir, None); let id = s.tasks[0].id; wait_exited(&mut s, id); let cap = s.tasks[0].capture_file.clone().expect("capture file set"); @@ -1344,7 +1344,7 @@ fn managed_save_without_any_id_omits_resume() { ("CODEX_HOME", &codex_home), ], )); - s.spawn_agent("codex", dir.to_path_buf(), None); + s.spawn_agent("codex", &dir, None); let id = s.tasks[0].id; wait_exited(&mut s, id); @@ -1400,7 +1400,7 @@ fn config_toml_notify_chains_through_the_injected_script() { dir.to_path_buf(), &[("CODEX_HOME", &codex_home)], )); - s.spawn_agent("codex", dir.to_path_buf(), None); + s.spawn_agent("codex", &dir, None); let argv = wait_argv(&mut s, &dir.join("argv")); assert!( argv.iter().any(|a| a.starts_with("notify=[")), @@ -1456,7 +1456,7 @@ fn unrepresentable_config_notify_suppresses_injection() { dir.to_path_buf(), &[("CODEX_HOME", &codex_home)], )); - s.spawn_agent("codex", dir.to_path_buf(), None); + s.spawn_agent("codex", &dir, None); assert_sole_notice(s.drain(), "capture unavailable"); let argv = wait_argv(&mut s, &dir.join("argv")); assert_eq!( @@ -1472,7 +1472,7 @@ fn unrepresentable_config_notify_suppresses_injection() { ) .unwrap(); std::fs::remove_file(dir.join("argv")).unwrap(); - s.spawn_agent("codex", dir.to_path_buf(), None); + s.spawn_agent("codex", &dir, None); assert_eq!(notices(s.drain()), Vec::::new()); let argv = wait_argv(&mut s, &dir.join("argv")); assert!( @@ -1521,7 +1521,7 @@ fn recovery_cadence_rewrites_on_capture_drift_and_skips_when_static() { &[("FLEETCOM_CONFIG_DIR", &config)], )); s.set_recovery_timing(Duration::from_millis(20), Duration::from_millis(100)); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); let _ = wait_argv(&mut s, &dir.join("argv")); let rec = config.join("sessions").join("recovery"); @@ -1601,7 +1601,7 @@ fn managed_claude_accepts_its_own_capture_and_refuses_a_foreign_stamp() { let mut ctx = agent_ctx(&bin, &runtime, dir.to_path_buf()); ctx.env = with_shell(ctx.env, install_resident_shell(&dir)); let mut s = sup_ctx(ctx); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); assert!(acknowledged(&s.drain()), "a managed spawn is acknowledged"); let argv = wait_argv(&mut s, &dir.join("argv")); let id = s.tasks[0].id; @@ -1644,11 +1644,11 @@ fn managed_spawn_refuses_an_unknown_word_a_missing_binary_and_a_full_fleet() { let (bin, runtime) = (dir.join("bin"), dir.join("run")); let mut s = sup_ctx(agent_ctx(&bin, &runtime, dir.to_path_buf())); - s.spawn_agent("vim", dir.to_path_buf(), None); + s.spawn_agent("vim", &dir, None); let events = s.drain(); assert!(!acknowledged(&events)); assert_sole_notice(events, "no agent named"); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); let events = s.drain(); assert!(!acknowledged(&events)); assert_sole_notice(events, "not found on PATH"); @@ -1657,10 +1657,10 @@ fn managed_spawn_refuses_an_unknown_word_a_missing_binary_and_a_full_fleet() { install_stub(&bin, "claude", &dir); s.set_max_tasks(1); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); assert!(acknowledged(&s.drain())); assert_eq!(s.tasks.len(), 1); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); assert_sole_notice(s.drain(), "task limit"); spawn(&mut s, "sleep 1", dir.to_path_buf()); assert_sole_notice(s.drain(), "task limit"); @@ -1677,7 +1677,7 @@ fn managed_rerun_resumes_the_captured_id_from_the_binary_path_finds_now() { let (bin, runtime) = (dir.join("bin"), dir.join("run")); install_stub(&bin, "claude", &dir); let mut s = sup_ctx(agent_ctx(&bin, &runtime, dir.to_path_buf())); - s.spawn_agent("claude", dir.to_path_buf(), Some("agents".into())); + s.spawn_agent("claude", &dir, Some("agents".into())); let _ = wait_argv(&mut s, &dir.join("argv")); let id = s.tasks[0].id; s.tasks[0].tagged = true; @@ -1730,7 +1730,7 @@ fn managed_rerun_without_an_id_starts_fresh() { let (bin, runtime) = (dir.join("bin"), dir.join("run")); install_stub(&bin, "omp", &dir); let mut s = sup_ctx(agent_ctx(&bin, &runtime, dir.to_path_buf())); - s.spawn_agent("omp", dir.to_path_buf(), None); + s.spawn_agent("omp", &dir, None); let first = wait_argv(&mut s, &dir.join("argv")); let id = s.tasks[0].id; assert_eq!(first[0], "-e"); @@ -1777,8 +1777,8 @@ fn managed_task_saves_as_a_managed_entry_and_reloads_managed() { dir.to_path_buf(), &[("FLEETCOM_CONFIG_DIR", &config)], )); - s.spawn_agent("claude", dir.to_path_buf(), Some("agents".into())); - s.spawn_agent("omp", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, Some("agents".into())); + s.spawn_agent("omp", &dir, None); assert!(s.tasks.iter().all(|t| t.harness.is_some())); // Wait for both first-run records before touching captures or clearing // them, so a late write from the original launch cannot pass for the @@ -1920,7 +1920,7 @@ fn managed_codex_reports_the_capture_notice_and_leads_a_rerun_with_resume() { dir.to_path_buf(), &[("CODEX_HOME", &codex_home)], )); - s.spawn_agent("codex", dir.to_path_buf(), None); + s.spawn_agent("codex", &dir, None); // Drain the launch notice so the rerun's is the sole one. let _ = s.drain(); let _ = wait_argv(&mut s, &dir.join("argv")); @@ -1978,7 +1978,7 @@ fn registry_waiting_status_reaches_the_dashboard_preview() { dir.to_path_buf(), &[("CLAUDE_CONFIG_DIR", &claude_home)], )); - s.spawn_agent("claude", dir.to_path_buf(), None); + s.spawn_agent("claude", &dir, None); // Key the record to the task's leader PID. let pid = s.tasks[0].pid().expect("a live task has a pid"); diff --git a/src/supervisor_tests.rs b/src/supervisor_tests.rs index b38c933..3a43c0c 100644 --- a/src/supervisor_tests.rs +++ b/src/supervisor_tests.rs @@ -560,7 +560,7 @@ fn kill_delivers_term_before_kill() { assert!(trapped.exists(), "the TERM trap never ran"); } -/// A task that ignores SIGTERM is SIGKILLed once the grace elapses, via the +/// A task that ignores SIGTERM is `SIGKILLed` once the grace elapses, via the /// reap-driven escalation. `Kill` must never leave an immortal task. #[test] fn term_ignoring_task_escalates_to_kill() { @@ -1096,7 +1096,7 @@ fn worst_case_screen_frame_fits_max_frame() { })); assert_eq!(kind, KIND_SCREEN); - let per_cell = payload.len() as f64 / MAX_CELLS as f64; + let per_cell = payload.len() as f64 / f64::from(MAX_CELLS); // Keep the constructed density above 85 bytes per cell. assert!( per_cell >= 85.0, diff --git a/src/task.rs b/src/task.rs index 527fe4b..f4eff93 100644 --- a/src/task.rs +++ b/src/task.rs @@ -230,7 +230,7 @@ impl Task { cols: u16, scrollback: usize, env: &[(OsString, OsString)], - waker: Waker, + waker: &Waker, ) -> io::Result { let pair = native_pty_system() .openpty(PtySize { @@ -246,9 +246,7 @@ impl Task { // Read SHELL from the launch context, not the daemon's environment: a zsh // client may connect to a daemon started from bash and still require zsh // word-splitting. Without SHELL, use the portable default. - let shell = env_get(env, "SHELL") - .map(OsString::from) - .unwrap_or_else(|| "/bin/sh".into()); + let shell = env_get(env, "SHELL").map_or_else(|| "/bin/sh".into(), OsString::from); let mut cmd = CommandBuilder::new(shell); // Use a non-interactive shell. Interactive startup files, // aliases, and shell functions are not loaded. @@ -295,7 +293,7 @@ impl Task { let handle = { let parser = Arc::clone(&parser); let last_activity = Arc::clone(&last_activity); - let waker = Arc::clone(&waker); + let waker = Arc::clone(waker); let input_tx = input_tx.clone(); let pending = Arc::clone(&pending_write); thread::spawn(move || { @@ -378,7 +376,7 @@ impl Task { } /// Check the capture file, then the live registry, then the spawn-time ID. A - /// A managed CLI can select a session after launch, so validate captured IDs + /// managed CLI can select a session after launch, so validate captured IDs /// through the harness with the task leader's PID. If a source has no valid ID, /// check the next source. For literal tasks, return `None`. pub fn current_resume_id(&self) -> Option { @@ -433,7 +431,7 @@ impl Task { /// Report whether the reader reached EOF without driving the reap loop. pub fn reader_done(&self) -> bool { - self.handle.as_ref().is_none_or(|h| h.is_finished()) + self.handle.as_ref().is_none_or(JoinHandle::is_finished) } /// Reap the exited session leader without blocking. @@ -499,8 +497,7 @@ impl Task { pub fn quiet_for(&self, now: Instant) -> Duration { self.last_activity .lock() - .map(|t| now.duration_since(*t)) - .unwrap_or(Duration::ZERO) + .map_or(Duration::ZERO, |t| now.duration_since(*t)) } /// Flush an expired `?2026` synchronized update so a stalled child's diff --git a/src/task_tests.rs b/src/task_tests.rs index 1554c95..75f63f1 100644 --- a/src/task_tests.rs +++ b/src/task_tests.rs @@ -16,7 +16,7 @@ fn no_waker() -> Waker { /// Spawn a task with a 24x80 grid, 2000 rows of scrollback, and this process's /// working directory. fn spawn_with(id: u64, command: &str, exec: Exec, env: &[(OsString, OsString)]) -> Task { - Task::spawn(id, command, exec, &here(), 24, 80, 2000, env, no_waker()).unwrap() + Task::spawn(id, command, exec, &here(), 24, 80, 2000, env, &no_waker()).unwrap() } /// Spawn a literal task with this process's environment. diff --git a/src/terminal/ansi.rs b/src/terminal/ansi.rs index 23f226d..b7eb985 100644 --- a/src/terminal/ansi.rs +++ b/src/terminal/ansi.rs @@ -1104,9 +1104,9 @@ mod tests { let mut wide_cells = 0usize; let mut colored_cells = 0usize; for seed in [ - 0x9e3779b97f4a7c15u64, - 0xdeadbeefcafef00d, - 0x0123456789abcdef, + 0x9e37_79b9_7f4a_7c15_u64, + 0xdead_beef_cafe_f00d, + 0x0123_4567_89ab_cdef, ] { let bytes = escape_soup(seed, 4000, 24, 80); let source = parse_term(&bytes, 24, 80); diff --git a/src/terminal/emulator.rs b/src/terminal/emulator.rs index 7a52420..f62ac79 100644 --- a/src/terminal/emulator.rs +++ b/src/terminal/emulator.rs @@ -223,7 +223,7 @@ impl Emulator { let bottom = grid.screen_lines() as i32 - 1; for line in top..=bottom { for cell in &mut grid[Line(line)][..] { - if !cell.zerowidth().is_some_and(|z| z.len() > MAX_ZEROWIDTH) { + if cell.zerowidth().is_none_or(|z| z.len() <= MAX_ZEROWIDTH) { continue; } // Rebuild the cell because `Cell` has no setter for @@ -409,8 +409,8 @@ impl Emulator { /// oldest stored row, `Live` is the live screen (offset 0). pub fn scroll(&mut self, action: ScrollAction) { self.term.scroll_display(match action { - ScrollAction::Up(n) => Scroll::Delta(n as i32), - ScrollAction::Down(n) => Scroll::Delta(-(n as i32)), + ScrollAction::Up(n) => Scroll::Delta(i32::from(n)), + ScrollAction::Down(n) => Scroll::Delta(-i32::from(n)), ScrollAction::Top => Scroll::Top, ScrollAction::Live => Scroll::Bottom, }); @@ -1277,11 +1277,12 @@ mod tests { /// A scan caps combining marks in every cell across a populated row. #[test] fn zerowidth_spray_across_cells_is_capped() { + use std::fmt::Write as _; let mut emu = Emulator::new(4, 80, 0); let marks = "\u{0301}".repeat(2048); let mut payload = String::new(); for col in 1..=80 { - payload.push_str(&format!("\x1b[2;{col}Hx")); + let _ = write!(payload, "\x1b[2;{col}Hx"); payload.push_str(&marks); } assert!( @@ -1427,7 +1428,7 @@ mod tests { } /// Within one chunk only the last title event matters; an empty OSC title - /// and a ResetTitle (title-stack pop, CSI 23 t) both unset the capture + /// and a `ResetTitle` (title-stack pop, CSI 23 t) both unset the capture /// rather than freezing the previous one. #[test] fn empty_title_and_reset_unset_the_capture() { diff --git a/src/terminal/input.rs b/src/terminal/input.rs index b2e0816..35b6261 100644 --- a/src/terminal/input.rs +++ b/src/terminal/input.rs @@ -194,7 +194,7 @@ fn f_bytes(n: u8, m: Option) -> Option> { } /// Prefix `base` with ESC when `meta` is set. `base` may be a multibyte -/// sequence, such as BackTab's CSI Z. +/// sequence, such as `BackTab`'s CSI Z. fn meta_bytes(meta: bool, base: &[u8]) -> Vec { let mut out = Vec::with_capacity(base.len() + 1); if meta { @@ -207,7 +207,7 @@ fn meta_bytes(meta: bool, base: &[u8]) -> Vec { /// Return xterm's modifier parameter `1 + shift + 2·alt + 4·ctrl`, or /// `None` when no modifier is held. fn mod_param(mods: Mods) -> Option { - let bits = mods.shift as u8 + 2 * mods.alt as u8 + 4 * mods.ctrl as u8; + let bits = u8::from(mods.shift) + 2 * u8::from(mods.alt) + 4 * u8::from(mods.ctrl); (bits != 0).then_some(1 + bits) } diff --git a/src/testutil.rs b/src/testutil.rs index 02580ff..6bf80f2 100644 --- a/src/testutil.rs +++ b/src/testutil.rs @@ -267,7 +267,7 @@ pub fn hook_json(id: &str, source: &str) -> String { /// Build owned rows from string literals. pub fn rows(spec: &[&str]) -> Vec { - spec.iter().map(|s| s.to_string()).collect() + spec.iter().map(ToString::to_string).collect() } /// Build an empty screen for task `id` with the cursor shown at `(0, 0)`, diff --git a/src/ui.rs b/src/ui.rs index c60c86c..0b48f3f 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1278,7 +1278,7 @@ mod tests { lifecycle, quiet_ago, finished_ago, - started_ago: Duration::from_secs(2 * 60 * 60), + started_ago: Duration::from_hours(2), ..view(None) } } @@ -1286,7 +1286,7 @@ mod tests { /// Use exit, quiet, or launch age in the time column according to task state. #[test] fn task_row_time_column_follows_lifecycle() { - let quiet = Some(Duration::from_secs(4 * 60)); // renders "4m" + let quiet = Some(Duration::from_mins(4)); // renders "4m" let exited = Some(Duration::from_secs(3)); // renders "3s" let cases = [ (Lifecycle::Ok, None, exited, "3s"), diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 8af359a..8c21cc1 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -163,9 +163,7 @@ pub fn spawn_task( stream.write_all(&spawn_frame(command, cwd)).unwrap(); assert!( wait_until(Duration::from_secs(5), || { - std::fs::read_to_string(pidfile) - .map(|s| !s.trim().is_empty()) - .unwrap_or(false) + std::fs::read_to_string(pidfile).is_ok_and(|s| !s.trim().is_empty()) }), "the task never wrote its pid" ); @@ -186,7 +184,7 @@ pub fn stop_daemon(daemon: &mut KillOnDrop) { ) .unwrap(); let exited = wait_until(Duration::from_secs(10), || { - daemon.0.try_wait().map(|s| s.is_some()).unwrap_or(false) + daemon.0.try_wait().is_ok_and(|s| s.is_some()) }); assert!(exited, "daemon did not exit on SIGTERM"); } diff --git a/tests/daemon_handshake.rs b/tests/daemon_handshake.rs index 689c745..1032683 100644 --- a/tests/daemon_handshake.rs +++ b/tests/daemon_handshake.rs @@ -73,16 +73,13 @@ fn silent_client_cannot_wedge_the_daemon() { let sock = dir.join("default.sock"); let cwd = dir.display().to_string(); let served_next = wait_until(Duration::from_secs(10), || { - UnixStream::connect(&sock) - .map(|mut s| { - // A successful handshake proves the accept loop is live again. - s.write_all(&hello_frame(PROTOCOL_VERSION, &[], &cwd)) - .is_ok() - && read_frame(&mut s) - .map(|(_, p)| String::from_utf8_lossy(&p).contains("hello_ok")) - .unwrap_or(false) - }) - .unwrap_or(false) + UnixStream::connect(&sock).is_ok_and(|mut s| { + // A successful handshake proves the accept loop is live again. + s.write_all(&hello_frame(PROTOCOL_VERSION, &[], &cwd)) + .is_ok() + && read_frame(&mut s) + .is_ok_and(|(_, p)| String::from_utf8_lossy(&p).contains("hello_ok")) + }) }); assert!(served_next, "daemon wedged behind a silent connection"); } diff --git a/tests/daemon_kill.rs b/tests/daemon_kill.rs index 03340d5..6939424 100644 --- a/tests/daemon_kill.rs +++ b/tests/daemon_kill.rs @@ -37,7 +37,7 @@ fn kill_works_while_a_client_is_attached() { .spawn() .unwrap(); let kill_status = wait_until(Duration::from_secs(15), || { - killer.try_wait().map(|s| s.is_some()).unwrap_or(false) + killer.try_wait().is_ok_and(|s| s.is_some()) }); if !kill_status { let _ = killer.kill(); @@ -52,7 +52,7 @@ fn kill_works_while_a_client_is_attached() { // group-killed, socket removed. assert!( wait_until(Duration::from_secs(5), || { - daemon.0.try_wait().map(|s| s.is_some()).unwrap_or(false) + daemon.0.try_wait().is_ok_and(|s| s.is_some()) }), "daemon still running after --kill returned" ); diff --git a/tests/daemon_lifetime.rs b/tests/daemon_lifetime.rs index 0698c57..7cd9023 100644 --- a/tests/daemon_lifetime.rs +++ b/tests/daemon_lifetime.rs @@ -1,4 +1,4 @@ -//! Test task lifetime after daemon exit. When a SIGKILLed daemon exits, the kernel +//! Test task lifetime after daemon exit. When a `SIGKILLed` daemon exits, the kernel //! closes each PTY master and sends SIGHUP to the task's foreground group. Ordinary //! tasks exit; HUP-immune tasks survive without an owner. Verify both outcomes. diff --git a/tests/daemon_resume.rs b/tests/daemon_resume.rs index 29cc36c..d2a9cb5 100644 --- a/tests/daemon_resume.rs +++ b/tests/daemon_resume.rs @@ -291,8 +291,10 @@ fn codex_capture_file_drives_save_and_load_resumes() { let script = notify .strip_prefix(r#"notify=[""#) .and_then(|v| v.strip_suffix(r#""]"#)) - .map(PathBuf::from) - .unwrap_or_else(|| panic!("spawn must route notify at one script: {argv:?}")); + .map_or_else( + || panic!("spawn must route notify at one script: {argv:?}"), + PathBuf::from, + ); assert_daemon_namespaced(&script, daemon.0.id(), "notify script", &argv); assert_eq!( argv,