ephemeris_forge_forgejo/config/
branch_rules.rs1use std::borrow::Borrow;
2use std::collections::BTreeMap;
3use std::fmt;
4
5use ephemeris_forge::{Mode, NameSet};
6use facet::Facet;
7
8use super::PushAccess;
9
10#[derive(Clone, Debug, Eq, Facet, Ord, PartialEq, PartialOrd)]
12#[facet(proxy = String)]
13pub struct RuleName(String);
14
15#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
17#[error("branch rule name \"{name}\" cannot address a Forgejo branch protection route")]
18pub struct InvalidRuleName {
19 pub name: String,
21}
22
23impl RuleName {
24 #[must_use]
26 pub fn is_addressable(name: &str) -> bool {
27 !name.is_empty() && !name.contains(['/', '?', '#'])
28 }
29
30 #[must_use]
32 pub fn as_str(&self) -> &str {
33 &self.0
34 }
35}
36
37impl TryFrom<String> for RuleName {
38 type Error = InvalidRuleName;
39
40 fn try_from(name: String) -> Result<Self, Self::Error> {
41 if Self::is_addressable(&name) {
42 Ok(Self(name))
43 } else {
44 Err(InvalidRuleName { name })
45 }
46 }
47}
48
49impl From<&RuleName> for String {
50 fn from(value: &RuleName) -> Self {
51 value.0.clone()
52 }
53}
54
55impl AsRef<str> for RuleName {
56 fn as_ref(&self) -> &str {
57 &self.0
58 }
59}
60
61impl Borrow<str> for RuleName {
62 fn borrow(&self) -> &str {
63 &self.0
64 }
65}
66
67impl fmt::Display for RuleName {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 f.write_str(&self.0)
70 }
71}
72
73#[derive(Clone, Debug, Eq, Facet, PartialEq)]
75#[facet(deny_unknown_fields)]
76#[expect(
77 clippy::struct_excessive_bools,
78 reason = "these are independent forge switches"
79)]
80pub struct BranchRule {
81 pub apply_to_admins: bool,
83 pub approvals_whitelist_teams: Vec<String>,
85 pub approvals_whitelist_username: Vec<String>,
87 pub block_on_official_review_requests: bool,
90 pub block_on_outdated_branch: bool,
92 pub block_on_rejected_reviews: bool,
94 pub dismiss_stale_approvals: bool,
96 pub enable_approvals_whitelist: bool,
99 pub enable_merge_whitelist: bool,
101 pub enable_status_check: bool,
103 pub ignore_stale_approvals: bool,
105 pub merge_whitelist_teams: Vec<String>,
107 pub merge_whitelist_usernames: Vec<String>,
109 pub protected_file_patterns: Vec<String>,
111 pub push: PushAccess,
113 pub push_whitelist_deploy_keys: bool,
115 pub push_whitelist_teams: Vec<String>,
117 pub push_whitelist_usernames: Vec<String>,
119 pub require_signed_commits: bool,
121 pub required_approvals: i64,
123 pub status_check_contexts: Vec<String>,
125 pub unprotected_file_patterns: Vec<String>,
128}
129
130#[derive(Clone, Debug, Eq, Facet, PartialEq)]
132#[facet(deny_unknown_fields)]
133pub struct BranchRules {
134 pub mode: Mode,
136 pub rules: BTreeMap<RuleName, BranchRule>,
138}
139
140impl BranchRule {
141 #[must_use]
143 pub fn contexts(&self) -> NameSet {
144 NameSet::new(self.status_check_contexts.iter().cloned())
145 }
146}