Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit 73918ca

Browse files
authored
feat: add Events::count (#32)
1 parent 6d33082 commit 73918ca

File tree

1 file changed

+91
-23
lines changed

1 file changed

+91
-23
lines changed

src/workflow/event.rs

Lines changed: 91 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Workflow events.
22
33
use indexmap::IndexMap;
4-
use serde::Deserialize;
4+
use serde::{Deserialize, Serialize};
55

66
/// "Bare" workflow event triggers.
77
///
@@ -55,27 +55,27 @@ pub enum BareEvent {
5555
/// Workflow event triggers, with bodies.
5656
///
5757
/// Like [`BareEvent`], but with per-event properties.
58-
#[derive(Default, Deserialize)]
58+
#[derive(Default, Deserialize, Serialize)]
5959
#[serde(default, rename_all = "snake_case")]
6060
pub struct Events {
6161
pub branch_protection_rule: OptionalBody<GenericEvent>,
6262
pub check_run: OptionalBody<GenericEvent>,
6363
pub check_suite: OptionalBody<GenericEvent>,
64-
// TODO: create + delete
65-
// TODO: deployment + deployment_status
64+
// NOTE: `create` and `delete` are omitted, since they are always bare.
65+
// NOTE: `deployment` and `deployment_status` are omitted, since they are always bare.
6666
pub discussion: OptionalBody<GenericEvent>,
6767
pub discussion_comment: OptionalBody<GenericEvent>,
68-
// TODO: fork + gollum
68+
// NOTE: `fork` and `gollum` are omitted, since they are always bare.
6969
pub issue_comment: OptionalBody<GenericEvent>,
7070
pub issues: OptionalBody<GenericEvent>,
7171
pub label: OptionalBody<GenericEvent>,
7272
pub merge_group: OptionalBody<GenericEvent>,
7373
pub milestone: OptionalBody<GenericEvent>,
74-
// TODO: page_build
74+
// NOTE: `page_build` is omitted, since it is always bare.
7575
pub project: OptionalBody<GenericEvent>,
7676
pub project_card: OptionalBody<GenericEvent>,
7777
pub project_column: OptionalBody<GenericEvent>,
78-
// TODO: public
78+
// NOTE: `public` is omitted, since it is always bare.
7979
pub pull_request: OptionalBody<PullRequest>,
8080
pub pull_request_comment: OptionalBody<GenericEvent>,
8181
pub pull_request_review: OptionalBody<GenericEvent>,
@@ -87,22 +87,75 @@ pub struct Events {
8787
pub release: OptionalBody<GenericEvent>,
8888
pub repository_dispatch: OptionalBody<GenericEvent>,
8989
pub schedule: OptionalBody<Vec<Cron>>,
90-
// TODO: status
90+
// NOTE: `status` is omitted, since it is always bare.
9191
pub watch: OptionalBody<GenericEvent>,
9292
pub workflow_call: OptionalBody<WorkflowCall>,
9393
// TODO: Custom type.
9494
pub workflow_dispatch: OptionalBody<WorkflowDispatch>,
9595
pub workflow_run: OptionalBody<WorkflowRun>,
9696
}
9797

98+
impl Events {
99+
/// Count the number of present event triggers.
100+
///
101+
/// **IMPORTANT**: This must be kept in sync with the number of fields in `Events`.
102+
pub fn count(&self) -> u32 {
103+
// This is a little goofy, but it's faster than reflecting over the struct
104+
// or doing a serde round-trip.
105+
let mut count = 0;
106+
107+
macro_rules! count_if_present {
108+
($($field:ident),*) => {
109+
$(
110+
if !matches!(self.$field, OptionalBody::Missing) {
111+
count += 1;
112+
}
113+
)*
114+
};
115+
}
116+
117+
count_if_present!(
118+
branch_protection_rule,
119+
check_run,
120+
check_suite,
121+
discussion,
122+
discussion_comment,
123+
issue_comment,
124+
issues,
125+
label,
126+
merge_group,
127+
milestone,
128+
project,
129+
project_card,
130+
project_column,
131+
pull_request,
132+
pull_request_comment,
133+
pull_request_review,
134+
pull_request_review_comment,
135+
pull_request_target,
136+
push,
137+
registry_package,
138+
release,
139+
repository_dispatch,
140+
schedule,
141+
watch,
142+
workflow_call,
143+
workflow_dispatch,
144+
workflow_run
145+
);
146+
147+
count
148+
}
149+
}
150+
98151
/// A generic container type for distinguishing between
99152
/// a missing key, an explicitly null key, and an explicit value `T`.
100153
///
101154
/// This is needed for modeling `on:` triggers, since GitHub distinguishes
102155
/// between the non-presence of an event (no trigger) and the presence
103156
/// of an empty event body (e.g. `pull_request:`), which means "trigger
104157
/// with the defaults for this event type."
105-
#[derive(Default)]
158+
#[derive(Default, Serialize)]
106159
pub enum OptionalBody<T> {
107160
Default,
108161
#[default]
@@ -132,15 +185,15 @@ impl<T> From<Option<T>> for OptionalBody<T> {
132185
}
133186

134187
/// A generic event trigger body.
135-
#[derive(Deserialize)]
188+
#[derive(Deserialize, Serialize)]
136189
#[serde(rename_all = "kebab-case")]
137190
pub struct GenericEvent {
138191
#[serde(default, deserialize_with = "crate::common::scalar_or_vector")]
139192
pub types: Vec<String>,
140193
}
141194

142195
/// The body of a `pull_request` event trigger.
143-
#[derive(Deserialize)]
196+
#[derive(Deserialize, Serialize)]
144197
#[serde(rename_all = "kebab-case")]
145198
pub struct PullRequest {
146199
#[serde(default)]
@@ -154,7 +207,7 @@ pub struct PullRequest {
154207
}
155208

156209
/// The body of a `push` event trigger.
157-
#[derive(Deserialize)]
210+
#[derive(Deserialize, Serialize)]
158211
#[serde(rename_all = "kebab-case")]
159212
pub struct Push {
160213
#[serde(flatten)]
@@ -168,14 +221,14 @@ pub struct Push {
168221
}
169222

170223
/// The body of a `cron` event trigger.
171-
#[derive(Deserialize)]
224+
#[derive(Deserialize, Serialize)]
172225
#[serde(rename_all = "kebab-case")]
173226
pub struct Cron {
174227
pub cron: String,
175228
}
176229

177230
/// The body of a `workflow_call` event trigger.
178-
#[derive(Deserialize)]
231+
#[derive(Deserialize, Serialize)]
179232
#[serde(rename_all = "kebab-case")]
180233
pub struct WorkflowCall {
181234
#[serde(default)]
@@ -187,7 +240,7 @@ pub struct WorkflowCall {
187240
}
188241

189242
/// A single input in a `workflow_call` event trigger body.
190-
#[derive(Deserialize)]
243+
#[derive(Deserialize, Serialize)]
191244
#[serde(rename_all = "kebab-case")]
192245
pub struct WorkflowCallInput {
193246
pub description: Option<String>,
@@ -198,31 +251,31 @@ pub struct WorkflowCallInput {
198251
}
199252

200253
/// A single output in a `workflow_call` event trigger body.
201-
#[derive(Deserialize)]
254+
#[derive(Deserialize, Serialize)]
202255
#[serde(rename_all = "kebab-case")]
203256
pub struct WorkflowCallOutput {
204257
pub description: Option<String>,
205258
pub value: String,
206259
}
207260

208261
/// A single secret in a `workflow_call` event trigger body.
209-
#[derive(Deserialize)]
262+
#[derive(Deserialize, Serialize)]
210263
#[serde(rename_all = "kebab-case")]
211264
pub struct WorkflowCallSecret {
212265
pub description: Option<String>,
213266
pub required: bool,
214267
}
215268

216269
/// The body of a `workflow_dispatch` event trigger.
217-
#[derive(Deserialize)]
270+
#[derive(Deserialize, Serialize)]
218271
#[serde(rename_all = "kebab-case")]
219272
pub struct WorkflowDispatch {
220273
#[serde(default)]
221274
pub inputs: IndexMap<String, WorkflowDispatchInput>, // TODO: WorkflowDispatchInput
222275
}
223276

224277
/// A single input in a `workflow_dispatch` event trigger body.
225-
#[derive(Deserialize)]
278+
#[derive(Deserialize, Serialize)]
226279
#[serde(rename_all = "kebab-case")]
227280
pub struct WorkflowDispatchInput {
228281
pub description: Option<String>,
@@ -237,7 +290,7 @@ pub struct WorkflowDispatchInput {
237290
}
238291

239292
/// The body of a `workflow_run` event trigger.
240-
#[derive(Deserialize)]
293+
#[derive(Deserialize, Serialize)]
241294
#[serde(rename_all = "kebab-case")]
242295
pub struct WorkflowRun {
243296
pub workflows: Vec<String>,
@@ -248,25 +301,40 @@ pub struct WorkflowRun {
248301
}
249302

250303
/// Branch filtering variants for event trigger bodies.
251-
#[derive(Deserialize)]
304+
#[derive(Deserialize, Serialize)]
252305
#[serde(rename_all = "kebab-case")]
253306
pub enum BranchFilters {
254307
Branches(Vec<String>),
255308
BranchesIgnore(Vec<String>),
256309
}
257310

258311
/// Tag filtering variants for event trigger bodies.
259-
#[derive(Deserialize)]
312+
#[derive(Deserialize, Serialize)]
260313
#[serde(rename_all = "kebab-case")]
261314
pub enum TagFilters {
262315
Tags(Vec<String>),
263316
TagsIgnore(Vec<String>),
264317
}
265318

266319
/// Path filtering variants for event trigger bodies.
267-
#[derive(Deserialize)]
320+
#[derive(Deserialize, Serialize)]
268321
#[serde(rename_all = "kebab-case")]
269322
pub enum PathFilters {
270323
Paths(Vec<String>),
271324
PathsIgnore(Vec<String>),
272325
}
326+
327+
#[cfg(test)]
328+
mod tests {
329+
#[test]
330+
fn test_events_count() {
331+
let events = "
332+
push:
333+
pull_request:
334+
workflow_dispatch:
335+
issue_comment:";
336+
337+
let events = serde_yaml::from_str::<super::Events>(events).unwrap();
338+
assert_eq!(events.count(), 4);
339+
}
340+
}

0 commit comments

Comments
 (0)