use super::stream_effect::{TerminalClassification, TerminalResult};
pub(crate) enum TerminalOutcome {
Success(TerminalResult),
Failure {
terminal: Option<TerminalResult>,
detail: String,
prefer_detail: bool,
},
}
pub(crate) fn assess_terminal(
pending: Option<TerminalResult>,
exit_status: std::process::ExitStatus,
stderr: &str,
program_label: &'static str,
) -> TerminalOutcome {
if !exit_status.success() {
let detail = non_zero_exit_detail(pending.as_ref(), exit_status, stderr, program_label);
return TerminalOutcome::Failure {
terminal: pending,
detail,
prefer_detail: true,
};
}
match pending {
Some(
terminal @ TerminalResult {
classification: TerminalClassification::Success { .. },
..
},
) => TerminalOutcome::Success(terminal),
Some(
terminal @ TerminalResult {
classification:
TerminalClassification::Failure { .. } | TerminalClassification::Invalid { .. },
..
},
) => {
let detail = terminal
.error
.clone()
.or_else(|| terminal.result_text.clone())
.unwrap_or_else(|| format!("{program_label}: terminal result was not success"));
TerminalOutcome::Failure {
terminal: Some(terminal),
detail,
prefer_detail: false,
}
}
None => TerminalOutcome::Failure {
terminal: None,
detail: format!("{program_label}: stream ended without a terminal result message"),
prefer_detail: true,
},
}
}
fn non_zero_exit_detail(
pending: Option<&TerminalResult>,
exit_status: std::process::ExitStatus,
stderr: &str,
program_label: &'static str,
) -> String {
let process_detail = if stderr.is_empty() {
format!("{program_label}: process exited with {exit_status}")
} else {
format!("{program_label}: process exited with {exit_status}: {stderr}")
};
let Some(stream_error) = stream_failure_text(pending) else {
return process_detail;
};
if stderr.is_empty() {
stream_error
} else {
format!("{process_detail}\n{stream_error}")
}
}
fn stream_failure_text(pending: Option<&TerminalResult>) -> Option<String> {
let terminal = pending?;
match &terminal.classification {
TerminalClassification::Success { .. } => None,
TerminalClassification::Failure { .. } | TerminalClassification::Invalid { .. } => terminal
.error
.as_deref()
.map(str::trim)
.filter(|text| !text.is_empty())
.map(str::to_owned)
.or_else(|| {
terminal
.result_text
.as_deref()
.map(str::trim)
.filter(|text| !text.is_empty())
.map(str::to_owned)
}),
}
}
#[cfg(test)]
#[path = "terminal_tests.rs"]
mod tests;