11//! Workflow events.
22
33use 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" ) ]
6060pub 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 ) ]
106159pub 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" ) ]
137190pub 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" ) ]
145198pub 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" ) ]
159212pub 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" ) ]
173226pub 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" ) ]
180233pub 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" ) ]
192245pub 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" ) ]
203256pub 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" ) ]
211264pub 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" ) ]
219272pub 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" ) ]
227280pub 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" ) ]
242295pub 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" ) ]
253306pub 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" ) ]
261314pub 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" ) ]
269322pub 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