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
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fmt::Write;
pub type JobProps = Vec<BTreeMap<String, String>>;
pub fn format_job_props(props: &JobProps) -> String {
let mut buf = String::new();
let mut first_group = true;
for group in props.iter() {
if !first_group {
write!(buf, ":").unwrap();
}
first_group = false;
let mut first_prop = true;
for (k, v) in group.iter() {
if !first_prop {
write!(buf, ",").unwrap();
}
first_prop = false;
write!(buf, "{}", k).unwrap();
if v.len() > 0 {
write!(buf, "={}", v).unwrap();
}
}
}
buf
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct JobSpec {
pub kind: String,
pub id: Option<String>,
pub passive: Option<String>,
pub props: JobProps,
}
impl JobSpec {
pub fn props(input: &[&[(&str, &str)]]) -> Vec<BTreeMap<String, String>> {
if input.len() == 0 {
vec![Default::default()]
} else {
input
.iter()
.map(|ps| {
ps.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect()
})
.collect()
}
}
pub fn new(kind: &str, id: Option<&str>, passive: Option<&str>, props: JobProps) -> Self {
assert!(props.len() > 0);
Self {
kind: kind.to_owned(),
id: id.map(Into::into),
passive: passive.map(Into::into),
props,
}
}
pub fn compatible(&self, other: &Self) -> bool {
const IGN_PROP_KEYS: &[&'static str] = &["apply", "commit"];
let mut left = self.clone();
let mut right = other.clone();
for key in IGN_PROP_KEYS.iter() {
left.props[0].remove(*key);
right.props[0].remove(*key);
}
left == right
}
}
impl std::fmt::Display for JobSpec {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"job[{}:{}]",
self.kind,
if self.id.is_some() {
self.id.as_ref().unwrap()
} else {
"-"
}
)
}
}