use super::prompt_kind::{PromptKind, formatter_failure_updates_lifecycle};
use crate::config::HistoryForgetConfig;
use crate::history::{HistoryRuntime, HistorySaveOutcome, HistoryStore};
use crate::repl::state::{PendingHistoryContext, ReplState, SpongeQueue};
use reedline::HistoryItemId;
fn record_command_outcome(
store: Option<HistoryStore>,
history_id: Option<HistoryItemId>,
failed: bool,
forget_config: &HistoryForgetConfig,
sponge_queue: &mut SpongeQueue,
) {
if let (Some(store), Some(history_id)) = (&store, history_id) {
let exit_status = if failed { 1i64 } else { 0i64 };
if let Err(error) = store.set_exit_status(history_id, exit_status) {
log::warn!("Failed to update history exit status: {error}");
}
}
if forget_config.enabled {
let effective_delay = if forget_config.on_exit_only {
usize::MAX
} else {
forget_config.delay
};
if let Some(id_to_delete) = sponge_queue.record_command(failed, history_id, effective_delay)
&& let Some(store) = &store
&& let Err(error) = store.delete(id_to_delete)
{
log::warn!("Failed to remove forgotten history entry {id_to_delete}: {error}");
}
}
}
pub(super) fn outcome_failure_projection(outcome: arf_libr::CommandOutcome) -> Option<bool> {
match outcome {
arf_libr::CommandOutcome::Success => Some(false),
arf_libr::CommandOutcome::Failure => Some(true),
arf_libr::CommandOutcome::Unavailable => None,
}
}
pub(super) fn initialize_error_handler_once(state: &mut ReplState, prompt_kind: PromptKind) {
if prompt_kind.is_command() && !state.error_handler_setup_attempted {
state.error_handler_setup_attempted = true;
if !arf_libr::command_outcome_tracking_available() {
log::info!(
"R library lacks safe error-state inspection; command outcomes are unavailable"
);
} else {
match arf_harp::eval_string_in_base(arf_libr::global_error_handler_code()) {
Ok(_) => {
log::info!("R error handler initialized after startup");
arf_libr::mark_global_error_handler_initialized();
}
Err(error) => {
log::warn!("Failed to initialize R error handler: {error:?}");
}
}
}
}
}
pub(super) fn finish_previous_command(state: &mut ReplState, prompt_kind: PromptKind) {
if prompt_kind.is_command() && !state.prompt_config.is_shell_enabled() {
let pending_history_context = std::mem::take(&mut state.pending_history_context);
let outcome = match pending_history_context {
PendingHistoryContext::Command { store, history_id } => {
let outcome = arf_libr::command_outcome();
if let Some(failed) = outcome_failure_projection(outcome) {
record_command_outcome(
store,
history_id,
failed,
&state.forget_config,
&mut state.sponge_queue,
);
}
Some(outcome)
}
PendingHistoryContext::None => None,
};
if crate::ipc::current_execution_id().is_some() {
let execution_outcome = outcome.unwrap_or_else(arf_libr::command_outcome);
crate::ipc::finish_execution(match execution_outcome {
arf_libr::CommandOutcome::Success => crate::ipc::ExecutionStatus::Ok,
arf_libr::CommandOutcome::Failure => crate::ipc::ExecutionStatus::Error,
arf_libr::CommandOutcome::Unavailable => crate::ipc::ExecutionStatus::Unknown,
});
}
if let Some(outcome) = outcome {
match outcome_failure_projection(outcome) {
Some(failed) => state.prompt_config.set_last_command_failed(failed),
None => state.prompt_config.set_last_command_outcome_unknown(),
}
}
state.prompt_config.set_command_duration();
arf_libr::reset_command_error_state();
}
}
pub(super) fn record_formatter_failure(
state: &mut ReplState,
prompt_kind: PromptKind,
history_handle: &HistoryRuntime,
save_outcome: Option<HistorySaveOutcome>,
) {
if formatter_failure_updates_lifecycle(prompt_kind) {
let history_id = save_outcome.and_then(HistorySaveOutcome::saved_id);
record_command_outcome(
history_handle.store(),
history_id,
true,
&state.forget_config,
&mut state.sponge_queue,
);
state.prompt_config.set_last_command_failed(true);
state.prompt_config.clear_command_duration();
}
}