refactor(client): make managed failure classification exhaustive

classify_client_error now matches every ClientError variant explicitly, so a
new variant fails to compile here until its real classification is chosen.
NoTokioRuntime classifies as InvalidState and DatagramLengthMismatch as
Socket; ManagedTargetFailureKind::Other survives only as a deliberate
category with no default path into it.

ManagedTargetFailurePhase and ManagedTargetFailureKind gain controlled
Display impls, and the CLI client/TUI failure diagnostics use them instead
of leaking Debug enum spelling into user-facing text.
This commit is contained in:
Nils Andreas Svee committed 2026-08-15 01:03:35 +07:00
1 parent 90c04b6569
commit e3003c348f
3 files changed
+279 -13

No files matched your search

+4 -4
View File
@@ -277,13 +277,13 @@ fn target_failure_messages(target: &ManagedTargetOutcome) -> Vec<String> {
let mut messages = Vec::with_capacity(2);
if let ManagedTargetEndReason::Failed(failure) = &target.end_reason {
messages.push(format!(
"irtt-rs: target {} failed ({:?} {:?}): {}",
"irtt-rs: target {} failed ({} {}): {}",
target.target.id, failure.phase, failure.kind, failure.message
));
}
if let Some(failure) = &target.cleanup_failure {
messages.push(format!(
"irtt-rs: target {} cleanup failed ({:?} {:?}): {}",
"irtt-rs: target {} cleanup failed ({} {}): {}",
target.target.id, failure.phase, failure.kind, failure.message
));
}
@@ -480,8 +480,8 @@ mod tests {
assert_eq!(
target_failure_messages(&target),
[
"irtt-rs: target edge failed (Receiving Protocol): primary failure",
"irtt-rs: target edge cleanup failed (Closing Socket): cleanup failure",
"irtt-rs: target edge failed (receiving protocol): primary failure",
"irtt-rs: target edge cleanup failed (closing socket): cleanup failure",
]
);
}
+7 -7
View File
@@ -194,7 +194,7 @@ impl TuiState {
(TargetStatus::Replaced, "target replaced".to_owned(), None)
}
ManagedTargetEndReason::Failed(failure) => {
let message = format!("{:?}: {}", failure.kind, failure.message);
let message = format!("{}: {}", failure.kind, failure.message);
(
TargetStatus::Failed,
format!("target failed: {message}"),
@@ -205,7 +205,7 @@ impl TuiState {
let cleanup_warning = outcome.cleanup_failure.as_ref().map(|failure| {
format!(
"cleanup failed ({:?} {:?}): {}",
"cleanup failed ({} {}): {}",
failure.phase, failure.kind, failure.message
)
});
@@ -2430,7 +2430,7 @@ mod tests {
assert!(state.targets[1]
.last_warning
.as_deref()
.is_some_and(|warning| warning.contains("Timeout")));
.is_some_and(|warning| warning.contains("timeout")));
}
#[test]
@@ -2474,11 +2474,11 @@ mod tests {
assert!(state.targets[0]
.last_warning
.as_deref()
.is_some_and(|warning| warning.contains("cleanup failed (Closing Socket)")));
.is_some_and(|warning| warning.contains("cleanup failed (closing socket)")));
assert!(state
.recent_events
.iter()
.any(|event| event.contains("cleanup failed (Closing Socket)")));
.any(|event| event.contains("cleanup failed (closing socket)")));
}
#[test]
@@ -2509,11 +2509,11 @@ mod tests {
assert!(state
.recent_events
.iter()
.any(|event| event.contains("target failed: Protocol: primary failure")));
.any(|event| event.contains("target failed: protocol: primary failure")));
assert!(state
.recent_events
.iter()
.any(|event| event.contains("cleanup failed (Closing Socket): cleanup failure")));
.any(|event| event.contains("cleanup failed (closing socket): cleanup failure")));
}
#[test]