pub fn display_cmd(cmd: &[String]) -> String {
cmd.join(" ")
}
pub fn format_try_thread_name(digest: u32) -> String {
format!("try-{digest:08x}")
}
pub fn default_try_name_prefix() -> &'static str {
"try-"
}
pub fn try_thread_name_collision_kind() -> &'static str {
"try_thread_name_collision"
}
pub fn plan_try_drop_outcome(ok: bool, err_msg: Option<String>) -> (bool, Option<String>) {
if ok { (true, None) } else { (false, err_msg) }
}
pub fn try_drop_status_fragment(thread_name: &str, thread_dropped: bool) -> String {
if thread_dropped {
format!("thread '{thread_name}' dropped")
} else {
format!("thread '{thread_name}' NOT dropped (cleanup failed)")
}
}
pub fn try_failed_message(
cmd_display: &str,
exit_label: &str,
thread_name: &str,
thread_dropped: bool,
) -> String {
format!(
"`{cmd_display}` failed (exit {exit_label}); {}",
try_drop_status_fragment(thread_name, thread_dropped)
)
}
pub fn try_exit_label(exit_code: Option<i32>) -> String {
exit_code
.map(|c| c.to_string())
.unwrap_or_else(|| "signal".into())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TrySuccessMessageFacts<'a> {
pub cmd_display: &'a str,
pub thread_name: &'a str,
pub auto_merge: bool,
pub captured_state: Option<&'a str>,
pub merge_state: Option<&'a str>,
}
pub fn try_success_message(facts: TrySuccessMessageFacts<'_>) -> String {
if facts.auto_merge {
match (facts.captured_state, facts.merge_state) {
(Some(state), Some(merge)) => format!(
"`{}` succeeded; captured {}, merged into parent as {}",
facts.cmd_display, state, merge
),
(Some(state), None) => format!(
"`{}` succeeded; captured {}, merge into parent skipped",
facts.cmd_display, state
),
_ => format!("`{}` succeeded; nothing to capture", facts.cmd_display),
}
} else {
match facts.captured_state {
Some(state) => format!(
"`{}` succeeded; thread '{}' ready (state {}). Check readiness with `heddle ready --thread {}` before landing.",
facts.cmd_display, facts.thread_name, state, facts.thread_name
),
None => format!(
"`{}` succeeded; thread '{}' ready (no capture).",
facts.cmd_display, facts.thread_name
),
}
}
}
pub fn try_status_token(success: bool) -> &'static str {
if success { "completed" } else { "failed" }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_cmd_joins_with_space() {
assert_eq!(display_cmd(&["cargo".into(), "test".into()]), "cargo test");
assert_eq!(display_cmd(&[]), "");
assert_eq!(display_cmd(&["true".into()]), "true");
}
#[test]
fn format_try_thread_name_is_hex_padded() {
assert_eq!(format_try_thread_name(0), "try-00000000");
assert_eq!(format_try_thread_name(0xdead_beef), "try-deadbeef");
assert!(format_try_thread_name(1).starts_with(default_try_name_prefix()));
}
#[test]
fn plan_try_drop_outcome_maps_ok_and_err() {
assert_eq!(plan_try_drop_outcome(true, None), (true, None));
assert_eq!(
plan_try_drop_outcome(true, Some("ignored".into())),
(true, None)
);
assert_eq!(
plan_try_drop_outcome(false, Some("lock held".into())),
(false, Some("lock held".into()))
);
assert_eq!(plan_try_drop_outcome(false, None), (false, None));
}
#[test]
fn try_failed_and_success_messages() {
assert_eq!(
try_failed_message("cargo test", "1", "try-abc", true),
"`cargo test` failed (exit 1); thread 'try-abc' dropped"
);
assert_eq!(
try_failed_message("true", "signal", "t", false),
"`true` failed (exit signal); thread 't' NOT dropped (cleanup failed)"
);
assert_eq!(try_exit_label(Some(2)), "2");
assert_eq!(try_exit_label(None), "signal");
let msg = try_success_message(TrySuccessMessageFacts {
cmd_display: "true",
thread_name: "try-1",
auto_merge: false,
captured_state: Some("state-a"),
merge_state: None,
});
assert!(msg.contains("ready (state state-a)"));
assert!(msg.contains("heddle ready --thread try-1"));
let merged = try_success_message(TrySuccessMessageFacts {
cmd_display: "true",
thread_name: "try-1",
auto_merge: true,
captured_state: Some("s"),
merge_state: Some("m"),
});
assert!(merged.contains("merged into parent as m"));
assert_eq!(try_status_token(true), "completed");
assert_eq!(try_status_token(false), "failed");
assert_eq!(
try_thread_name_collision_kind(),
"try_thread_name_collision"
);
}
}