refactor(app): clarify application and binary roles
Rename the application Cargo package from irtt-cli to irtt-rs (crates/irtt-cli -> crates/irtt-app), and hard-rename its scriptable client binary from irtt-cli to irtt-client, now that the application also ships a first-class server and the old name reads as an umbrella CLI for both roles. Give each dedicated binary (irtt-client, irtt-tui, irtt-server) its own entry point instead of funneling through the multicall dispatcher's argv0/subcommand logic, so a dedicated binary only links the applet code for the role it implements and always runs that role regardless of the name it's invoked or copied under. irtt-rs remains the feature-selectable multicall dispatcher and is now the only binary that inspects argv0 or subcommands. Move future application releases to the irtt-rs/vX.Y.Z tag namespace (regenerated via `dist generate`), and bump the application package to 0.6.0 as a breaking distribution change. Library crate versions are unaffected.
This commit is contained in:
49 files changed
+365
-213
No files matched your search
@@ -0,0 +1,141 @@
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
#[cfg(any(feature = "client", feature = "tui"))]
|
||||
use irtt_client::managed::{ManagedEvent, ManagedEventSubscription, ManagedEventTryRecvError};
|
||||
|
||||
#[cfg(any(feature = "client", feature = "tui"))]
|
||||
pub(crate) const MANAGED_EVENT_WORK_BUDGET: usize = 128;
|
||||
|
||||
#[cfg(any(feature = "client", feature = "tui"))]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum ManagedDrainState {
|
||||
Empty,
|
||||
Closed,
|
||||
BudgetExhausted,
|
||||
}
|
||||
|
||||
/// Drain a bounded amount of lossy presentation work before returning to the
|
||||
/// frontend control loop.
|
||||
#[cfg(any(feature = "client", feature = "tui"))]
|
||||
pub(crate) fn drain_managed_events<E>(
|
||||
events: &mut ManagedEventSubscription,
|
||||
dropped_events: &mut u64,
|
||||
mut process: impl FnMut(ManagedEvent) -> Result<(), E>,
|
||||
) -> Result<ManagedDrainState, E> {
|
||||
for _ in 0..MANAGED_EVENT_WORK_BUDGET {
|
||||
match events.try_recv() {
|
||||
Ok(event) => process(event)?,
|
||||
Err(ManagedEventTryRecvError::Lagged(count)) => {
|
||||
*dropped_events = dropped_events.saturating_add(count);
|
||||
}
|
||||
Err(ManagedEventTryRecvError::Empty) => return Ok(ManagedDrainState::Empty),
|
||||
Err(ManagedEventTryRecvError::Closed) => return Ok(ManagedDrainState::Closed),
|
||||
}
|
||||
}
|
||||
Ok(ManagedDrainState::BudgetExhausted)
|
||||
}
|
||||
|
||||
pub fn is_shutdown_requested(shutdown_requested: &AtomicBool) -> bool {
|
||||
shutdown_requested.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn should_print_final_summary(continuous: bool, interrupted: bool) -> bool {
|
||||
!continuous || interrupted
|
||||
}
|
||||
|
||||
pub fn peer_close_run_error(
|
||||
continuous: bool,
|
||||
interrupted: bool,
|
||||
peer_closed_target_outcomes: u64,
|
||||
) -> Option<String> {
|
||||
if !continuous || interrupted || peer_closed_target_outcomes == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let sessions = if peer_closed_target_outcomes == 1 {
|
||||
"target session"
|
||||
} else {
|
||||
"target sessions"
|
||||
};
|
||||
Some(format!(
|
||||
"continuous run ended because of peer closure ({peer_closed_target_outcomes} {sessions})"
|
||||
))
|
||||
}
|
||||
|
||||
pub fn request_managed_stop_for_peer_close(
|
||||
continuous: bool,
|
||||
interrupted: bool,
|
||||
peer_closed_target_outcomes: u64,
|
||||
stop_requested: &mut bool,
|
||||
) -> bool {
|
||||
if !continuous || interrupted || peer_closed_target_outcomes == 0 {
|
||||
return false;
|
||||
}
|
||||
request_managed_stop_once(stop_requested)
|
||||
}
|
||||
|
||||
pub fn request_managed_stop_once(stop_requested: &mut bool) -> bool {
|
||||
if *stop_requested {
|
||||
return false;
|
||||
}
|
||||
*stop_requested = true;
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn peer_close_run_error_is_run_mode_and_interruption_aware() {
|
||||
assert_eq!(peer_close_run_error(false, false, 1), None);
|
||||
assert_eq!(peer_close_run_error(true, false, 0), None);
|
||||
assert_eq!(peer_close_run_error(true, true, 1), None);
|
||||
assert_eq!(
|
||||
peer_close_run_error(true, false, 1).as_deref(),
|
||||
Some("continuous run ended because of peer closure (1 target session)")
|
||||
);
|
||||
assert_eq!(
|
||||
peer_close_run_error(true, false, 3).as_deref(),
|
||||
Some("continuous run ended because of peer closure (3 target sessions)")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grouped_peer_close_stop_policy_is_run_mode_and_one_shot() {
|
||||
let mut stop_requested = false;
|
||||
assert!(!request_managed_stop_for_peer_close(
|
||||
false,
|
||||
false,
|
||||
1,
|
||||
&mut stop_requested,
|
||||
));
|
||||
assert!(!request_managed_stop_for_peer_close(
|
||||
true,
|
||||
true,
|
||||
1,
|
||||
&mut stop_requested,
|
||||
));
|
||||
assert!(!request_managed_stop_for_peer_close(
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
&mut stop_requested,
|
||||
));
|
||||
assert!(!stop_requested);
|
||||
|
||||
assert!(request_managed_stop_for_peer_close(
|
||||
true,
|
||||
false,
|
||||
1,
|
||||
&mut stop_requested,
|
||||
));
|
||||
assert!(stop_requested);
|
||||
assert!(!request_managed_stop_for_peer_close(
|
||||
true,
|
||||
false,
|
||||
1,
|
||||
&mut stop_requested,
|
||||
));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user