Skip to main content

hm_exec/
outcome.rs

1//! The typed result of a backend run.
2
3use chrono::{DateTime, Utc};
4use hm_plugin_protocol::events::BuildRef;
5use uuid::Uuid;
6
7/// Headline verdict. The CLI projects this to a process exit code; the int is
8/// a CLI-local concern, NOT the contract.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum BuildStatus {
11    Passed,
12    Failed,
13    Canceled,
14    TimedOut,
15}
16
17impl BuildStatus {
18    /// Process exit code for `hm run`. 130 = SIGINT-cancel, 124 = timeout.
19    #[must_use]
20    pub const fn exit_code(self) -> i32 {
21        match self {
22            Self::Passed => 0,
23            Self::Failed => 1,
24            Self::Canceled => 130,
25            Self::TimedOut => 124,
26        }
27    }
28
29    #[must_use]
30    pub const fn is_success(self) -> bool {
31        matches!(self, Self::Passed)
32    }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum StepStatus {
37    Passed,
38    Failed,
39    Skipped,
40    Canceled,
41    CacheHit,
42    TimedOut,
43}
44
45#[derive(Debug, Clone)]
46pub struct StepResultSummary {
47    pub step_id: Uuid,
48    pub key: String,
49    pub status: StepStatus,
50    pub exit_code: Option<i32>,
51    pub duration_ms: u64,
52}
53
54/// The terminal result of a build.
55#[derive(Debug, Clone)]
56pub struct BuildOutcome {
57    pub build: BuildRef,
58    pub status: BuildStatus,
59    pub steps: Vec<StepResultSummary>,
60    pub started_at: DateTime<Utc>,
61    pub finished_at: DateTime<Utc>,
62    /// `Some` for cloud (dashboard URL); `None` for local.
63    pub watch_url: Option<String>,
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn status_maps_to_process_exit_codes() {
72        assert_eq!(BuildStatus::Passed.exit_code(), 0);
73        assert_eq!(BuildStatus::Failed.exit_code(), 1);
74        assert_eq!(BuildStatus::Canceled.exit_code(), 130);
75        assert_eq!(BuildStatus::TimedOut.exit_code(), 124);
76    }
77}