1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use std::collections::HashMap;
use chrono::{offset::Utc, DateTime};
use itertools::Itertools;
use log::error;
pub mod protocol {
include!(concat!(env!("OUT_DIR"), "/jobserver_proto.rs"));
impl From<&Vec<String>> for MatrixVarValues {
fn from(vec: &Vec<String>) -> Self {
MatrixVarValues {
values: vec.clone(),
}
}
}
impl From<&MatrixVarValues> for Vec<String> {
fn from(mvv: &MatrixVarValues) -> Self {
mvv.values.clone()
}
}
pub fn convert_map(
map: &std::collections::HashMap<String, Vec<String>>,
) -> std::collections::HashMap<String, MatrixVarValues> {
map.iter().map(|(k, v)| (k.clone(), v.into())).collect()
}
pub fn reverse_map(
map: &std::collections::HashMap<String, MatrixVarValues>,
) -> std::collections::HashMap<String, Vec<String>> {
map.into_iter()
.map(|(k, v)| (k.clone(), v.into()))
.collect()
}
}
pub const SERVER_ADDR: &str = "127.0.0.1:3030";
pub fn cmd_replace_vars(cmd: &str, vars: &HashMap<String, String>) -> String {
vars.iter().fold(cmd.to_string(), |cmd, (key, value)| {
cmd.replace(&format!("{{{}}}", key), &value)
})
}
pub fn cmd_replace_machine(cmd: &str, machine: &str) -> String {
cmd.replace("{MACHINE}", &machine)
}
pub fn cmd_to_path(jid: u64, cmd: &str, log_dir: &str) -> String {
let mut name = format!(
"{}/{}-{}",
log_dir,
jid,
cmd.replace(" ", "_")
.replace("{", "_")
.replace("}", "_")
.replace("/", "_")
);
name.truncate(200);
name
}
pub fn cartesian_product<'v>(
vars: &'v HashMap<String, Vec<String>>,
) -> impl Iterator<Item = HashMap<String, String>> + 'v {
vars.iter()
.map(|(k, vs)| vs.iter().map(move |v| (k.clone(), v.clone())))
.multi_cartesian_product()
.map(|config: Vec<(String, String)>| config.into_iter().collect())
}
const TS_FORMAT: &str = "%+";
pub fn serialize_ts(ts: DateTime<Utc>) -> String {
ts.format(TS_FORMAT).to_string()
}
pub fn deserialize_ts(s: String) -> DateTime<Utc> {
DateTime::parse_from_str(&s, TS_FORMAT)
.map(|dt| dt.with_timezone(&Utc))
.unwrap_or_else(|err| {
error!("Unable to deserialize timestamp: {:?}, {}", s, err);
Utc::now()
})
}
pub fn human_ts(d: chrono::Duration) -> String {
let total_seconds = d.num_seconds();
const SECONDS_PER_MINUTE: i64 = 60;
const SECONDS_PER_HOUR: i64 = 60 * SECONDS_PER_MINUTE;
const SECONDS_PER_DAY: i64 = 24 * SECONDS_PER_HOUR;
let display_days = d.num_days();
let display_hours = (total_seconds - display_days * SECONDS_PER_DAY) / SECONDS_PER_HOUR;
let display_minutes =
(total_seconds - display_days * SECONDS_PER_DAY - display_hours * SECONDS_PER_HOUR)
/ SECONDS_PER_MINUTE;
let display_seconds = total_seconds
- display_days * SECONDS_PER_DAY
- display_hours * SECONDS_PER_HOUR
- display_minutes * SECONDS_PER_MINUTE;
let mut display_ts = String::new();
if display_days > 0 {
display_ts.push_str(&format!("{}d", display_days));
}
if display_hours > 0 {
display_ts.push_str(&format!("{}h", display_hours));
}
if display_minutes > 0 && display_days == 0 {
display_ts.push_str(&format!("{}m", display_minutes));
}
if display_seconds > 0 && display_days == 0 && display_hours == 0 {
display_ts.push_str(&format!("{}s", display_seconds));
}
if total_seconds == 0 {
display_ts.push_str("0s");
}
display_ts
}