Skip to content

Commit 72169fe

Browse files
authored
refactor: drop stale and redundant dependencies (#199)
Audit the full direct dependency set in `Cargo.toml`, then remove or replace crates that were dead, redundant with the standard library, or duplicated with an existing dependency. Five crates (`arrayvec`, `ctrlc`, `directories`, `signal-hook 0.4.4`, plus the macOS `objc2`/`block2`/`dispatch2` chain pulled in by `ctrlc`) are now completely gone from the build graph; three more (`lazy_static`, `once_cell`, `fastrand`) remain transitively but are no longer directly referenced by our code. Replacements: - `lazy_static!` → `std::sync::LazyLock` in `src/ui/tui/progress.rs` (edition 2024 already mandates Rust 1.85+). - `once_cell::sync::Lazy` / `OnceCell` → `std::sync::LazyLock` / `OnceLock` across `executor/output_sync.rs`, `pty/terminal.rs`, `utils/logging.rs`, `ssh/ssh_config/env_cache/global.rs`, `ssh/config_cache/global.rs`. - `fastrand::u64(range)` → `rand::random_range(range)` in the three forwarding reconnect-jitter sites; `rand` 0.10 was already a direct dependency. - `directories::ProjectDirs` / `BaseDirs` → `dirs::config_dir` / `dirs::home_dir`, unifying on `dirs` which was already used in 16 other sites and produces equivalent platform paths. - `ctrlc::set_handler` → `tokio::signal::ctrl_c` inside a `tokio::spawn`, guarded by `Handle::try_current` to preserve the previous best-effort semantics in non-runtime contexts. The only caller (`execute_traditional`) is already async, and the matching test runs under `#[tokio::test]`. - `signal-hook = "0.4.4"` → `"0.3"` so we share the same instance `crossterm` already pulls in via `signal-hook-mio`. Our usage (`Signals::new`, `Signals::forever`, `consts::SIGWINCH`) has identical signatures across both major lines, so no source change was needed — only the `Cargo.toml` pin. The `arrayvec` crate was declared but unused anywhere in the source tree, so it is simply dropped. Build, full `cargo test -p bssh` (1303 tests), `cargo clippy --all-targets`, and `cargo fmt --check` all pass. `Cargo.lock` loses 76 lines including the entire `signal-hook 0.4.4` subtree.
1 parent 3e005d6 commit 72169fe

14 files changed

Lines changed: 54 additions & 135 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 82 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,30 +37,24 @@ futures = "0.3.32"
3737
async-trait = "0.1.89"
3838
indicatif = "0.18.4"
3939
rpassword = "7.4.0"
40-
directories = "6.0.0"
4140
dirs = "6.0"
4241
chrono = { version = "0.4.44", features = ["serde"] }
4342
glob = "0.3.3"
4443
whoami = "2.1.1"
4544
owo-colors = "4.3.0"
4645
unicode-width = "0.2.2"
4746
terminal_size = "0.4.4"
48-
once_cell = "1.21.4"
4947
zeroize = { version = "1.8.2", features = ["derive"] }
5048
secrecy = { version = "0.10.3", features = ["serde"] }
5149
rustyline = "18.0.0"
5250
crossterm = "0.29"
5351
ratatui = "0.30"
5452
regex = "1.12.3"
55-
lazy_static = "1.5"
56-
ctrlc = "3.5.2"
57-
signal-hook = "0.4.4"
53+
signal-hook = "0.3"
5854
nix = { version = "0.31", features = ["fs", "poll", "process", "signal", "term"] }
59-
arrayvec = "0.7.6"
6055
smallvec = "1.15.1"
6156
lru = "0.17.0"
6257
uuid = { version = "1.23.1", features = ["v4"] }
63-
fastrand = "2.4.1"
6458
tokio-util = "0.7.18"
6559
socket2 = "0.6"
6660
shell-words = "1.1.1"
@@ -85,7 +79,6 @@ security-framework = "3.7.0"
8579
[dev-dependencies]
8680
tempfile = "3.27.0"
8781
mockito = "1.7.2"
88-
once_cell = "1.21.4"
8982
tokio-test = "0.4"
9083
serial_test = "3.4"
9184
insta = "1.47"

src/commands/interactive_signal.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,29 @@ pub fn reset_interrupt() {
3333
INTERRUPTED.store(false, Ordering::Relaxed);
3434
}
3535

36-
/// Set up signal handlers for interactive mode
36+
/// Set up signal handlers for interactive mode.
37+
///
38+
/// Spawns a tokio task that waits for a Ctrl+C signal and flips both the
39+
/// global interrupt flag and the returned shutdown flag. Callers must invoke
40+
/// this from within a tokio runtime; without one the spawn is skipped and the
41+
/// shutdown flag is returned unarmed (matching the prior best-effort semantics).
3742
pub fn setup_signal_handlers() -> Result<Arc<AtomicBool>> {
3843
let shutdown = Arc::new(AtomicBool::new(false));
3944
let shutdown_clone = Arc::clone(&shutdown);
4045

41-
// Handle Ctrl+C
42-
// Note: set_handler can only be called once per process, so we ignore errors in tests
43-
if let Err(e) = ctrlc::set_handler(move || {
44-
info!("Received Ctrl+C signal");
45-
INTERRUPTED.store(true, Ordering::Relaxed);
46-
shutdown_clone.store(true, Ordering::Relaxed);
47-
}) {
48-
// In tests, this might already be registered
49-
debug!("Could not set Ctrl-C handler: {}", e);
50-
// Return the shutdown flag anyway for use in the code
46+
if tokio::runtime::Handle::try_current().is_ok() {
47+
tokio::spawn(async move {
48+
match signal::ctrl_c().await {
49+
Ok(()) => {
50+
info!("Received Ctrl+C signal");
51+
INTERRUPTED.store(true, Ordering::Relaxed);
52+
shutdown_clone.store(true, Ordering::Relaxed);
53+
}
54+
Err(e) => debug!("Failed to install Ctrl-C handler: {}", e),
55+
}
56+
});
57+
} else {
58+
debug!("No tokio runtime active; Ctrl-C handler not installed");
5159
}
5260

5361
Ok(shutdown)

src/config/loader.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
//! Configuration loading and priority management.
1616
1717
use anyhow::{Context, Result};
18-
use directories::ProjectDirs;
1918
use std::env;
2019
use std::path::{Path, PathBuf};
2120
use tokio::fs;
@@ -222,8 +221,8 @@ impl Config {
222221
.join("bssh")
223222
.join("config.yaml");
224223
return Ok(xdg_config);
225-
} else if let Some(proj_dirs) = ProjectDirs::from("", "", "bssh") {
226-
let xdg_config = proj_dirs.config_dir().join("config.yaml");
224+
} else if let Some(config_dir) = dirs::config_dir() {
225+
let xdg_config = config_dir.join("bssh").join("config.yaml");
227226
return Ok(xdg_config);
228227
}
229228

src/executor/output_sync.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,14 @@
1515
//! Thread-safe output synchronization for preventing race conditions
1616
//! when multiple nodes write to stdout/stderr simultaneously.
1717
18-
use once_cell::sync::Lazy;
1918
use std::io::{self, Write};
20-
use std::sync::Mutex;
19+
use std::sync::{LazyLock, Mutex};
2120

2221
/// Global stdout mutex to prevent interleaved output
23-
static STDOUT_MUTEX: Lazy<Mutex<io::Stdout>> = Lazy::new(|| Mutex::new(io::stdout()));
22+
static STDOUT_MUTEX: LazyLock<Mutex<io::Stdout>> = LazyLock::new(|| Mutex::new(io::stdout()));
2423

2524
/// Global stderr mutex to prevent interleaved output
26-
static STDERR_MUTEX: Lazy<Mutex<io::Stderr>> = Lazy::new(|| Mutex::new(io::stderr()));
25+
static STDERR_MUTEX: LazyLock<Mutex<io::Stderr>> = LazyLock::new(|| Mutex::new(io::stderr()));
2726

2827
/// Thread-safe println! that prevents output interleaving
2928
///

src/forwarding/dynamic/forwarder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ impl DynamicForwarder {
193193
);
194194

195195
// Add jitter to avoid thundering herd
196-
let jitter = Duration::from_millis(fastrand::u64(
196+
let jitter = Duration::from_millis(rand::random_range(
197197
0..=retry_delay.as_millis() as u64 / 4,
198198
));
199199
retry_delay += jitter;

src/forwarding/local.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ impl LocalForwarder {
232232
);
233233

234234
// Add jitter to avoid thundering herd
235-
let jitter = Duration::from_millis(fastrand::u64(
235+
let jitter = Duration::from_millis(rand::random_range(
236236
0..=retry_delay.as_millis() as u64 / 4,
237237
));
238238
retry_delay += jitter;

src/forwarding/remote.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ impl RemoteForwarder {
241241
);
242242

243243
// Add jitter to avoid thundering herd
244-
let jitter = Duration::from_millis(fastrand::u64(
244+
let jitter = Duration::from_millis(rand::random_range(
245245
0..=retry_delay.as_millis() as u64 / 4,
246246
));
247247
retry_delay += jitter;

src/pty/terminal.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
1717
use std::io::Write;
1818
use std::sync::{
19-
Arc, Mutex,
19+
Arc, LazyLock, Mutex,
2020
atomic::{AtomicBool, Ordering},
2121
};
2222

@@ -26,11 +26,10 @@ use crossterm::{
2626
execute,
2727
terminal::{disable_raw_mode, enable_raw_mode},
2828
};
29-
use once_cell::sync::Lazy;
3029

3130
/// Global terminal cleanup synchronization
3231
/// Ensures only one cleanup attempt happens even with multiple guards
33-
static TERMINAL_MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
32+
static TERMINAL_MUTEX: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
3433
static RAW_MODE_ACTIVE: AtomicBool = AtomicBool::new(false);
3534

3635
/// Terminal state information that needs to be preserved and restored

src/ssh/config_cache/global.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414

1515
use super::config::CacheConfig;
1616
use super::manager::SshConfigCache;
17-
use once_cell::sync::Lazy;
17+
use std::sync::LazyLock;
1818
use tracing::debug;
1919

2020
/// Global SSH config cache instance
21-
pub static GLOBAL_CACHE: Lazy<SshConfigCache> = Lazy::new(|| {
21+
pub static GLOBAL_CACHE: LazyLock<SshConfigCache> = LazyLock::new(|| {
2222
let config = CacheConfig::from_env();
2323

2424
debug!(

0 commit comments

Comments
 (0)