1use derive_setters::Setters;
5use indexmap::IndexMap;
6use merge::Merge;
7use serde::{Deserialize, Serialize};
8
9use crate::concurrency::Concurrency;
10use crate::defaults::Defaults;
11use crate::env::Env;
13use crate::error::Result;
14use crate::generate::Generate;
15use crate::job::Job;
16use crate::permissions::Permissions;
17use crate::Event;
18
19#[derive(Debug, Default, Serialize, Deserialize, Clone)]
20#[serde(transparent)]
21pub struct Jobs(pub(crate) IndexMap<String, Job>);
22impl Jobs {
23 pub fn add(mut self, key: String, value: Job) -> Self {
24 self.0.insert(key, value);
25 self
26 }
27
28 pub fn get(&self, key: &str) -> Option<&Job> {
38 self.0.get(key)
39 }
40}
41
42#[derive(Debug, Default, Setters, Serialize, Deserialize, Clone)]
49#[serde(rename_all = "kebab-case")]
50#[setters(strip_option, into)]
51pub struct Workflow {
52 #[serde(skip_serializing_if = "Option::is_none")]
55 pub name: Option<String>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub env: Option<Env>,
60
61 #[serde(skip_serializing_if = "Option::is_none")]
64 pub run_name: Option<String>,
65
66 #[serde(skip_serializing_if = "Option::is_none")]
69 pub on: Option<Event>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub permissions: Option<Permissions>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub jobs: Option<Jobs>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
82 pub concurrency: Option<Concurrency>,
83
84 #[serde(skip_serializing_if = "Option::is_none")]
86 pub defaults: Option<Defaults>,
87
88 #[serde(skip_serializing_if = "Option::is_none")]
90 pub timeout_minutes: Option<u32>,
91}
92
93#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
95#[serde(rename_all = "kebab-case")]
96pub struct EventAction {
97 #[serde(skip_serializing_if = "Vec::is_empty")]
99 branches: Vec<String>,
100
101 #[serde(skip_serializing_if = "Vec::is_empty")]
103 branches_ignore: Vec<String>,
104}
105
106impl Workflow {
107 pub fn new<T: ToString>(name: T) -> Self {
109 Self { name: Some(name.to_string()), ..Default::default() }
110 }
111
112 pub fn to_string(&self) -> Result<String> {
114 Ok(serde_yml::to_string(self)?)
115 }
116
117 pub fn add_job<T: ToString, J: Into<Job>>(mut self, id: T, job: J) -> Self {
119 let key = id.to_string();
120 let jobs = self.jobs.take().unwrap_or_default().add(key, job.into());
121
122 self.jobs = Some(jobs);
123 self
124 }
125
126 pub fn parse(yml: &str) -> Result<Self> {
128 Ok(serde_yml::from_str(yml)?)
129 }
130
131 pub fn generate(self) -> Result<()> {
133 Generate::new(self).generate()
134 }
135
136 pub fn add_event<T: Into<Event>>(mut self, that: T) -> Self {
138 if let Some(mut this) = self.on.take() {
139 this.merge(that.into());
140 self.on = Some(this);
141 } else {
142 self.on = Some(that.into());
143 }
144 self
145 }
146
147 pub fn add_env<T: Into<Env>>(mut self, new_env: T) -> Self {
149 let mut env = self.env.take().unwrap_or_default();
150
151 env.0.extend(new_env.into().0);
152 self.env = Some(env);
153 self
154 }
155}