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::secret::Secret;
18use crate::Event;
19
20#[derive(Debug, Default, Serialize, Deserialize, Clone)]
21#[serde(transparent)]
22pub struct Jobs(pub(crate) IndexMap<String, Job>);
23impl Jobs {
24 pub fn add(mut self, key: String, value: Job) -> Self {
25 self.0.insert(key, value);
26 self
27 }
28
29 pub fn get(&self, key: &str) -> Option<&Job> {
39 self.0.get(key)
40 }
41}
42
43#[derive(Debug, Default, Setters, Serialize, Deserialize, Clone)]
50#[serde(rename_all = "kebab-case")]
51#[setters(strip_option, into)]
52pub struct Workflow {
53 #[serde(skip_serializing_if = "Option::is_none")]
56 pub name: Option<String>,
57
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub env: Option<Env>,
61
62 #[serde(skip_serializing_if = "Option::is_none")]
65 pub run_name: Option<String>,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
70 pub on: Option<Event>,
71
72 #[serde(skip_serializing_if = "Option::is_none")]
74 pub permissions: Option<Permissions>,
75
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub jobs: Option<Jobs>,
79
80 #[serde(skip_serializing_if = "Option::is_none")]
83 pub concurrency: Option<Concurrency>,
84
85 #[serde(skip_serializing_if = "Option::is_none")]
87 pub defaults: Option<Defaults>,
88
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub secrets: Option<IndexMap<String, Secret>>,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub timeout_minutes: Option<u32>,
96}
97
98#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
100#[serde(rename_all = "kebab-case")]
101pub struct EventAction {
102 #[serde(skip_serializing_if = "Vec::is_empty")]
104 branches: Vec<String>,
105
106 #[serde(skip_serializing_if = "Vec::is_empty")]
108 branches_ignore: Vec<String>,
109}
110
111impl Workflow {
112 pub fn new<T: ToString>(name: T) -> Self {
114 Self { name: Some(name.to_string()), ..Default::default() }
115 }
116
117 pub fn to_string(&self) -> Result<String> {
119 Ok(serde_yaml::to_string(self)?)
120 }
121
122 pub fn add_job<T: ToString, J: Into<Job>>(mut self, id: T, job: J) -> Self {
124 let key = id.to_string();
125 let jobs = self.jobs.take().unwrap_or_default().add(key, job.into());
126
127 self.jobs = Some(jobs);
128 self
129 }
130
131 pub fn parse(yml: &str) -> Result<Self> {
133 Ok(serde_yaml::from_str(yml)?)
134 }
135
136 pub fn generate(self) -> Result<()> {
138 Generate::new(self).generate()
139 }
140
141 pub fn add_event<T: Into<Event>>(mut self, that: T) -> Self {
143 if let Some(mut this) = self.on.take() {
144 this.merge(that.into());
145 self.on = Some(this);
146 } else {
147 self.on = Some(that.into());
148 }
149 self
150 }
151
152 pub fn add_env<T: Into<Env>>(mut self, new_env: T) -> Self {
154 let mut env = self.env.take().unwrap_or_default();
155
156 env.0.extend(new_env.into().0);
157 self.env = Some(env);
158 self
159 }
160}