1use derive_setters::Setters;
5use indexmap::IndexMap;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9use crate::concurrency::Concurrency;
10use crate::step::{Step, StepType, StepValue};
11use crate::{
12 Artifacts, Container, Defaults, Env, Expression, Permissions, RetryStrategy, Secret, Strategy,
13};
14
15#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)]
17#[serde(transparent)]
18pub struct RunsOn(Value);
19
20impl<T> From<T> for RunsOn
21where
22 T: Into<Value>,
23{
24 fn from(value: T) -> Self {
26 RunsOn(value.into())
27 }
28}
29
30#[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)]
32#[serde(rename_all = "kebab-case")]
33#[setters(strip_option, into)]
34pub struct Job {
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub needs: Option<Vec<String>>,
37 #[serde(skip_serializing_if = "Option::is_none", rename = "if")]
38 pub cond: Option<Expression>,
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub name: Option<String>,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub runs_on: Option<RunsOn>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub permissions: Option<Permissions>,
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub env: Option<Env>,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub strategy: Option<Strategy>,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub steps: Option<Vec<StepValue>>,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub uses: Option<String>,
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub container: Option<Container>,
55 #[serde(skip_serializing_if = "Option::is_none")]
56 pub outputs: Option<IndexMap<String, String>>,
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub concurrency: Option<Concurrency>,
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub timeout_minutes: Option<u32>,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub services: Option<IndexMap<String, Container>>,
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub secrets: Option<IndexMap<String, Secret>>,
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub defaults: Option<Defaults>,
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub continue_on_error: Option<bool>,
69 #[serde(skip_serializing_if = "Option::is_none")]
70 pub retry: Option<RetryStrategy>,
71 #[serde(skip_serializing_if = "Option::is_none")]
72 pub artifacts: Option<Artifacts>,
73}
74
75impl Job {
76 pub fn new<T: ToString>(name: T) -> Self {
78 Self {
79 name: Some(name.to_string()),
80 runs_on: Some(RunsOn(Value::from("ubuntu-latest"))),
81 ..Default::default()
82 }
83 }
84
85 pub fn add_step<S: Into<Step<T>>, T: StepType>(mut self, step: S) -> Self {
87 let mut steps = self.steps.take().unwrap_or_default();
88 let step: Step<T> = step.into();
89 let step: StepValue = T::to_value(step);
90 steps.push(step);
91 self.steps = Some(steps);
92 self
93 }
94
95 pub fn add_env<T: Into<Env>>(mut self, new_env: T) -> Self {
97 let mut env = self.env.take().unwrap_or_default();
98
99 env.0.extend(new_env.into().0);
100 self.env = Some(env);
101 self
102 }
103
104 pub fn add_needs<J: ToString>(mut self, job_id: J) -> Self {
105 if let Some(needs) = self.needs.as_mut() {
106 needs.push(job_id.to_string());
107 } else {
108 self.needs = Some(vec![job_id.to_string()]);
109 }
110 self
111 }
112}