Skip to main content

hara_native/work/
types.rs

1use super::*;
2
3/// Validated portable identifier for one native work run.
4#[derive(Clone, Debug, PartialEq, Eq, Hash)]
5pub struct WorkId(String);
6
7impl WorkId {
8    pub fn new(value: impl Into<String>) -> Result<Self, String> {
9        let value = value.into();
10        if value.trim().is_empty() {
11            return Err("work run ID cannot be blank".into());
12        }
13        Ok(Self(value))
14    }
15
16    pub fn as_str(&self) -> &str {
17        &self.0
18    }
19}
20
21impl fmt::Display for WorkId {
22    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
23        formatter.write_str(&self.0)
24    }
25}
26
27/// Monotonic state of a live native work run.
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub enum WorkRunState {
30    Queued,
31    Running,
32    Waiting,
33    Cancelling,
34    Completed,
35    Failed,
36    Cancelled,
37}
38
39impl WorkRunState {
40    pub fn terminal(self) -> bool {
41        matches!(self, Self::Completed | Self::Failed | Self::Cancelled)
42    }
43}
44
45/// Non-blocking status snapshot for a live work run.
46#[derive(Clone, Debug, PartialEq)]
47pub struct WorkRunStatus {
48    pub id: WorkId,
49    pub state: WorkRunState,
50    pub started_at_millis: u64,
51    pub finished_at_millis: Option<u64>,
52    pub error: Option<PromiseRejection>,
53    pub cancel_reason: Option<Value>,
54    pub parent_id: Option<WorkId>,
55    pub child_count: usize,
56    pub deadline_remaining_millis: Option<u64>,
57    pub detached: bool,
58}
59
60/// Process-host lifecycle metadata.
61#[derive(Clone, Debug, PartialEq, Eq)]
62pub struct WorkHostStatus {
63    pub state: &'static str,
64    pub run_count: usize,
65    pub queued_count: usize,
66}
67
68/// A portable absolute deadline on the runtime's monotonic nanosecond clock.
69///
70/// Unlike `std::time::Instant`, this remains available in browser Wasm where
71/// the native standard-library clock is intentionally absent.
72#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
73pub struct WorkDeadline(u64);
74
75impl WorkDeadline {
76    pub fn at_monotonic_nanos(value: u64) -> Self {
77        Self(value)
78    }
79
80    pub fn after(timeout: Duration) -> Self {
81        let timeout = u64::try_from(timeout.as_nanos()).unwrap_or(u64::MAX);
82        Self(monotonic_nanos().saturating_add(timeout))
83    }
84
85    pub fn monotonic_nanos(self) -> u64 {
86        self.0
87    }
88
89    pub fn expired(self) -> bool {
90        monotonic_nanos() >= self.0
91    }
92
93    pub fn remaining_millis(self) -> u64 {
94        self.0
95            .saturating_sub(monotonic_nanos())
96            .saturating_div(1_000_000)
97    }
98}
99
100/// Submission-time scope and deadline options.
101#[derive(Clone, Debug, Default)]
102pub struct WorkOptions {
103    pub id: Option<WorkId>,
104    pub timeout: Option<Duration>,
105    pub deadline: Option<WorkDeadline>,
106    pub detached: bool,
107}
108
109impl WorkOptions {
110    pub fn with_id(id: impl Into<String>) -> Result<Self, String> {
111        Ok(Self {
112            id: Some(WorkId::new(id)?),
113            ..Self::default()
114        })
115    }
116}