1mod list;
2
3use facet::Facet;
4
5pub use self::list::Rules;
6pub(crate) use self::list::{RuleDecoder, RuleRef};
7
8#[derive(Clone, Copy, Debug, Eq, Facet, Ord, PartialEq, PartialOrd)]
10#[repr(u8)]
11pub enum MergeMethod {
12 #[facet(rename = "merge")]
14 Merge,
15 #[facet(rename = "squash")]
17 Squash,
18 #[facet(rename = "rebase")]
20 Rebase,
21}
22
23impl MergeMethod {
24 pub const ALL: [Self; 3] = [Self::Merge, Self::Squash, Self::Rebase];
26}
27
28#[derive(Clone, Debug, Eq, Facet, Ord, PartialEq, PartialOrd)]
30#[facet(deny_unknown_fields)]
31pub struct RequiredCheck {
32 pub context: String,
34 #[facet(skip_serializing_if = Option::is_none)]
36 pub integration_id: Option<i64>,
37}
38
39#[derive(Clone, Copy, Debug, Eq, Facet, Ord, PartialEq, PartialOrd)]
41#[repr(u8)]
42pub enum DismissalActorType {
43 #[facet(rename = "User")]
45 User,
46 #[facet(rename = "Team")]
48 Team,
49 #[facet(rename = "IntegrationInstallation")]
51 IntegrationInstallation,
52 #[facet(rename = "RepositoryRole")]
54 RepositoryRole,
55}
56
57#[derive(Clone, Copy, Debug, Eq, Facet, Ord, PartialEq, PartialOrd)]
59#[facet(deny_unknown_fields)]
60pub struct DismissalActor {
61 pub id: i64,
63 #[facet(rename = "type")]
65 pub kind: DismissalActorType,
66}
67
68#[derive(Clone, Debug, Eq, Facet, PartialEq)]
70#[facet(deny_unknown_fields)]
71pub struct DismissalRestriction {
72 pub enabled: bool,
74 pub allowed_actors: Vec<DismissalActor>,
76}
77
78#[derive(Clone, Debug, Eq, Facet, PartialEq)]
80#[facet(deny_unknown_fields)]
81#[expect(
82 clippy::struct_excessive_bools,
83 reason = "these mirror independent GitHub pull request rule parameters"
84)]
85pub struct PullRequestRule {
86 pub dismiss_stale_reviews_on_push: bool,
88 pub require_code_owner_review: bool,
90 pub require_last_push_approval: bool,
92 pub required_approving_review_count: u8,
94 pub required_review_thread_resolution: bool,
96 pub allowed_merge_methods: Vec<MergeMethod>,
98 #[facet(skip_serializing_if = Option::is_none)]
100 pub dismissal_restriction: Option<DismissalRestriction>,
101}
102
103#[derive(Clone, Debug, Eq, Facet, PartialEq)]
105#[facet(deny_unknown_fields)]
106pub struct StatusChecksRule {
107 pub required_status_checks: Vec<RequiredCheck>,
109 pub strict_required_status_checks_policy: bool,
111 pub do_not_enforce_on_create: bool,
113}
114
115#[derive(Clone, Copy, Debug, Eq, Facet, PartialEq)]
117#[facet(deny_unknown_fields)]
118pub struct UpdateRule {
119 pub update_allows_fetch_and_merge: bool,
121}
122
123#[derive(Clone, Debug, Eq, Facet, PartialEq)]
125#[facet(deny_unknown_fields)]
126pub struct DeploymentsRule {
127 pub required_deployment_environments: Vec<String>,
129}
130
131#[derive(Clone, Copy, Debug, Eq, Facet, PartialEq)]
133#[repr(u8)]
134pub enum PatternOperator {
135 #[facet(rename = "starts_with")]
137 StartsWith,
138 #[facet(rename = "ends_with")]
140 EndsWith,
141 #[facet(rename = "contains")]
143 Contains,
144 #[facet(rename = "regex")]
146 Regex,
147}
148
149#[derive(Clone, Debug, Eq, Facet, PartialEq)]
151#[facet(deny_unknown_fields)]
152pub struct PatternRule {
153 pub negate: bool,
155 pub operator: PatternOperator,
157 pub pattern: String,
159 #[facet(skip_serializing_if = Option::is_none)]
161 pub name: Option<String>,
162}
163
164#[derive(Clone, Debug, Eq, Facet, Ord, PartialEq, PartialOrd)]
166#[facet(deny_unknown_fields)]
167pub struct WorkflowRef {
168 pub path: String,
170 #[facet(rename = "ref", skip_serializing_if = Option::is_none)]
172 pub reference: Option<String>,
173 pub repository_id: i64,
175 #[facet(skip_serializing_if = Option::is_none)]
177 pub sha: Option<String>,
178}
179
180#[derive(Clone, Debug, Eq, Facet, PartialEq)]
182#[facet(deny_unknown_fields)]
183pub struct WorkflowsRule {
184 pub workflows: Vec<WorkflowRef>,
186 pub do_not_enforce_on_create: bool,
188}
189
190#[derive(Clone, Copy, Debug, Eq, Facet, Ord, PartialEq, PartialOrd)]
192#[repr(u8)]
193pub enum AlertsThreshold {
194 #[facet(rename = "none")]
196 None,
197 #[facet(rename = "errors")]
199 Errors,
200 #[facet(rename = "errors_and_warnings")]
202 ErrorsAndWarnings,
203 #[facet(rename = "all")]
205 All,
206}
207
208#[derive(Clone, Copy, Debug, Eq, Facet, Ord, PartialEq, PartialOrd)]
210#[repr(u8)]
211pub enum SecurityAlertsThreshold {
212 #[facet(rename = "none")]
214 None,
215 #[facet(rename = "critical")]
217 Critical,
218 #[facet(rename = "high_or_higher")]
220 HighOrHigher,
221 #[facet(rename = "medium_or_higher")]
223 MediumOrHigher,
224 #[facet(rename = "all")]
226 All,
227}
228
229#[derive(Clone, Debug, Eq, Facet, Ord, PartialEq, PartialOrd)]
231#[facet(deny_unknown_fields)]
232pub struct CodeScanningTool {
233 pub tool: String,
235 pub alerts_threshold: AlertsThreshold,
237 pub security_alerts_threshold: SecurityAlertsThreshold,
239}
240
241#[derive(Clone, Debug, Eq, Facet, PartialEq)]
243#[facet(deny_unknown_fields)]
244pub struct CodeScanningRule {
245 pub code_scanning_tools: Vec<CodeScanningTool>,
247}
248
249#[derive(Clone, Copy, Debug, Eq, Facet, PartialEq)]
251#[repr(u8)]
252pub enum GroupingStrategy {
253 #[facet(rename = "ALLGREEN")]
255 AllGreen,
256 #[facet(rename = "HEADGREEN")]
258 HeadGreen,
259}
260
261#[derive(Clone, Copy, Debug, Eq, Facet, PartialEq)]
263#[repr(u8)]
264pub enum QueueMergeMethod {
265 #[facet(rename = "MERGE")]
267 Merge,
268 #[facet(rename = "SQUASH")]
270 Squash,
271 #[facet(rename = "REBASE")]
273 Rebase,
274}
275
276#[derive(Clone, Copy, Debug, Eq, Facet, PartialEq)]
278#[facet(deny_unknown_fields)]
279pub struct MergeQueueRule {
280 pub check_response_timeout_minutes: u32,
282 pub grouping_strategy: GroupingStrategy,
284 pub max_entries_to_build: u32,
286 pub max_entries_to_merge: u32,
288 pub merge_method: QueueMergeMethod,
290 pub min_entries_to_merge: u32,
292 pub min_entries_to_merge_wait_minutes: u32,
294}
295
296impl Rules {
297 pub(super) fn normalize(&mut self) {
300 if let Some(pull_request) = &mut self.pull_request {
301 pull_request.allowed_merge_methods.sort_unstable();
302 pull_request.allowed_merge_methods.dedup();
303 if let Some(restriction) = &mut pull_request.dismissal_restriction {
304 restriction.allowed_actors.sort_unstable();
305 restriction.allowed_actors.dedup();
306 }
307 if pull_request
308 .dismissal_restriction
309 .as_ref()
310 .is_some_and(|restriction| {
311 !restriction.enabled && restriction.allowed_actors.is_empty()
312 })
313 {
314 pull_request.dismissal_restriction = None;
315 }
316 }
317 if let Some(checks) = &mut self.required_status_checks {
318 checks.required_status_checks.sort_unstable();
319 checks.required_status_checks.dedup();
320 }
321 if let Some(deployments) = &mut self.required_deployments {
322 deployments.required_deployment_environments.sort_unstable();
323 deployments.required_deployment_environments.dedup();
324 }
325 if let Some(workflows) = &mut self.workflows {
326 workflows.workflows.sort_unstable();
327 workflows.workflows.dedup();
328 }
329 if let Some(scanning) = &mut self.code_scanning {
330 scanning.code_scanning_tools.sort_unstable();
331 scanning.code_scanning_tools.dedup();
332 }
333 }
334}