Skip to main content

ralph_api/
loop_support.rs

1use std::path::Path;
2use std::process::Command;
3
4use chrono::{SecondsFormat, Utc};
5use ralph_core::{MergeQueueError, WorktreeError};
6
7use crate::errors::ApiError;
8
9pub fn loop_not_found_error(loop_id: &str) -> ApiError {
10    ApiError::loop_not_found(format!("Loop '{loop_id}' not found"))
11        .with_details(serde_json::json!({ "loopId": loop_id }))
12}
13
14pub fn map_merge_error(error: MergeQueueError) -> ApiError {
15    match error {
16        MergeQueueError::NotFound(loop_id) => loop_not_found_error(&loop_id),
17        MergeQueueError::InvalidTransition(loop_id, from, to) => ApiError::precondition_failed(
18            format!("Invalid loop transition for '{loop_id}': {from:?} -> {to:?}"),
19        )
20        .with_details(serde_json::json!({
21            "loopId": loop_id,
22            "from": format!("{from:?}"),
23            "to": format!("{to:?}")
24        })),
25        other => ApiError::internal(format!("merge queue operation failed: {other}")),
26    }
27}
28
29pub fn map_worktree_error(loop_id: &str, error: WorktreeError) -> ApiError {
30    ApiError::internal(format!(
31        "worktree cleanup failed for loop '{loop_id}': {error}"
32    ))
33    .with_details(serde_json::json!({ "loopId": loop_id }))
34}
35
36pub fn current_commit(workspace_root: &Path) -> String {
37    let output = Command::new("git")
38        .args(["rev-parse", "--short", "HEAD"])
39        .current_dir(workspace_root)
40        .output();
41
42    match output {
43        Ok(output) if output.status.success() => {
44            let sha = String::from_utf8_lossy(&output.stdout).trim().to_string();
45            if sha.is_empty() {
46                "manual".to_string()
47            } else {
48                sha
49            }
50        }
51        _ => "manual".to_string(),
52    }
53}
54
55pub fn is_pid_alive(pid: u32) -> bool {
56    Command::new("kill")
57        .args(["-0", &pid.to_string()])
58        .status()
59        .is_ok_and(|status| status.success())
60}
61
62pub(crate) fn now_ts() -> String {
63    Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true)
64}