nomad_client_rs/extensions/
mod.rs

1use serde::{Deserialize, Serialize};
2
3use crate::models::alloc_file_info::AllocFileInfo;
4use crate::models::event::{Event, EventPayload};
5use crate::models::{TaskEvent, TaskState};
6
7impl AllocFileInfo {
8    pub fn is_empty(&self) -> bool {
9        self.size.map_or(false, |value| value <= 1)
10    }
11}
12
13impl Event {
14    pub fn get_alloc_id(&self) -> Option<String> {
15        if let Some(payload) = &self.payload {
16            match payload {
17                EventPayload::Allocation(allocation) => {
18                    allocation.job_id.as_ref().map(|job_id| job_id.to_string())
19                }
20                _ => None,
21            };
22        }
23
24        None
25    }
26}
27
28impl TaskState {
29    pub fn last_exit_code(&self) -> Option<i32> {
30        match &self.events {
31            Some(events) => {
32                for event in events.iter().rev() {
33                    match event.exit_code() {
34                        Some(exit_code) => return Some(exit_code),
35                        None => continue,
36                    }
37                }
38            }
39            None => return None,
40        }
41
42        None
43    }
44
45    pub fn first_exit_code(&self) -> Option<i32> {
46        match &self.events {
47            Some(events) => {
48                for event in events {
49                    match event.exit_code() {
50                        Some(exit_code) => return Some(exit_code),
51                        None => continue,
52                    }
53                }
54            }
55            None => return None,
56        }
57
58        None
59    }
60}
61
62#[derive(Clone, Debug, Default, Deserialize, Serialize)]
63pub enum TaskStates {
64    #[default]
65    None,
66    #[serde(rename = "pending")]
67    Pending,
68    #[serde(rename = "running")]
69    Running,
70    #[serde(rename = "dead")]
71    Dead,
72}
73
74impl TaskEvent {
75    pub fn exit_code(&self) -> Option<i32> {
76        if let Some(Ok(exit_code)) = self
77            .details
78            .as_ref()?
79            .get("exit_code")
80            .map(|s| s.parse::<i32>())
81        {
82            return Some(exit_code);
83        }
84        None
85    }
86
87    pub fn driver_message(&self) -> Option<String> {
88        self.details
89            .as_ref()?
90            .get("driver_message")
91            .map(|s| s.into())
92    }
93
94    pub fn restart_reason(&self) -> Option<String> {
95        self.details
96            .as_ref()?
97            .get("restart_reason")
98            .map(|s| s.into())
99    }
100
101    pub fn kill_reason(&self) -> Option<String> {
102        self.details.as_ref()?.get("kill_reason").map(|s| s.into())
103    }
104
105    pub fn has_error(&self) -> bool {
106        if let Some(details) = &self.details {
107            for key in error_keys() {
108                if details.contains_key(key) {
109                    return true;
110                }
111            }
112        }
113
114        false
115    }
116
117    pub fn error(&self, error_key: &str) -> Option<String> {
118        self.details.as_ref()?.get(error_key).map(|s| s.into())
119    }
120
121    pub fn all_errors(&self) -> Option<Vec<(&str, String)>> {
122        let mut errors: Vec<(&str, String)> = Vec::new();
123
124        for key in error_keys() {
125            match self.details.as_ref()?.get(key) {
126                Some(error) => errors.push((key, error.into())),
127                None => continue,
128            }
129        }
130
131        if errors.is_empty() {
132            None
133        } else {
134            Some(errors)
135        }
136    }
137}
138
139pub fn error_keys() -> Vec<&'static str> {
140    vec![
141        "validation_error",
142        "kill_error",
143        "setup_error",
144        "driver_error",
145        "download_error",
146        "vault_renewal_error",
147    ]
148}