Skip to main content

ephemeris_forge/
limitation.rs

1use std::fmt;
2
3use crate::{TeamName, UserName};
4
5/// A setting the forge exposes but the plan cannot change or manage.
6#[derive(Clone, Debug, Eq, PartialEq)]
7pub enum Limitation {
8    /// A declared team is not in the organization.
9    TeamNotInOrganization {
10        /// The declared team.
11        team: TeamName,
12        /// The forge's name.
13        forge: &'static str,
14    },
15    /// A team exists, but its organization-wide permission is not the
16    /// declared one.
17    TeamOrganizationPermission {
18        /// The declared team.
19        team: TeamName,
20        /// The organization-wide permission.
21        actual: String,
22        /// The declared permission.
23        expected: String,
24    },
25    /// A team reaches the repository through an all-repositories team.
26    TeamInherited {
27        /// The team.
28        team: TeamName,
29        /// The forge's name.
30        forge: &'static str,
31    },
32    /// The forge only changes team permissions organization-wide.
33    TeamPermissionIsOrganizationWide {
34        /// The team.
35        team: TeamName,
36        /// The permission it has.
37        actual: String,
38        /// The declared permission.
39        expected: String,
40        /// The forge's name.
41        forge: &'static str,
42    },
43    /// A team needs a custom role, which personal repositories lack.
44    TeamRoleNeedsOrganization {
45        /// The team.
46        team: TeamName,
47        /// The custom role.
48        role: String,
49    },
50    /// A collaborator needs a custom role, which personal repositories lack.
51    CollaboratorRoleNeedsOrganization {
52        /// The collaborator.
53        user: UserName,
54        /// The custom role.
55        role: String,
56    },
57    /// The forge does not report a setting, so it cannot be compared.
58    NotReported {
59        /// The setting.
60        field: &'static str,
61    },
62    /// The forge does not report a setting, so it is written on every apply.
63    WrittenOnEveryApply {
64        /// The setting.
65        field: &'static str,
66    },
67    /// A setting does not exist for this repository.
68    Unavailable {
69        /// The setting.
70        field: &'static str,
71    },
72    /// A setting is fixed by the owner.
73    EnforcedByOwner {
74        /// The setting.
75        field: &'static str,
76    },
77    /// A declared setting has no effect because another one switches it off.
78    Ignored {
79        /// The declared setting.
80        field: &'static str,
81        /// The setting that switches it off.
82        switch: &'static str,
83    },
84    /// The mirror settings are declared for a repository that is no mirror.
85    MirrorOnly,
86    /// A GitHub Actions settings group cannot be read or written.
87    ActionsUnavailable {
88        /// The group.
89        group: &'static str,
90    },
91    /// A GitHub Actions settings group exists for private repositories only.
92    ActionsPrivateOnly {
93        /// The group.
94        group: &'static str,
95    },
96    /// A security setting cannot be read.
97    SecurityUnreadable {
98        /// The setting.
99        field: &'static str,
100    },
101    /// The repository has no custom properties.
102    CustomPropertiesUnsupported,
103    /// An environment protection rule is not managed.
104    EnvironmentRuleUnmanaged {
105        /// The environment.
106        environment: String,
107        /// The rule type.
108        rule: String,
109    },
110    /// The variables of an environment that does not exist are not managed.
111    VariablesWithoutEnvironment {
112        /// The environment, or none for the repository.
113        environment: Option<String>,
114    },
115    /// A webhook of another kind is left alone.
116    WebhookKindLeftAlone {
117        /// The hook's ID.
118        id: u64,
119        /// The hook's kind.
120        kind: String,
121    },
122    /// A hand-set secret is kept, so the delivery settings stay unchanged.
123    WebhookSecretKept {
124        /// The delivery URL.
125        url: String,
126    },
127    /// A ruleset has required reviewers, which an update drops.
128    RequiredReviewersDropped {
129        /// The ruleset.
130        ruleset: String,
131    },
132    /// A ruleset has a rule type the configuration cannot declare.
133    UnmanagedRuleType {
134        /// The ruleset.
135        ruleset: String,
136        /// The rule type.
137        kind: String,
138    },
139    /// A branch rule cannot be addressed by the API routes.
140    UnaddressableRule {
141        /// The rule's name.
142        name: String,
143    },
144}
145
146impl fmt::Display for Limitation {
147    #[expect(clippy::too_many_lines, reason = "one arm per limitation")]
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        match self {
150            Self::TeamNotInOrganization { team, forge } => {
151                write!(f, "team {team} is not in the {forge} organization")
152            }
153            Self::TeamOrganizationPermission {
154                team,
155                actual,
156                expected,
157            } => write!(
158                f,
159                "team {team} cannot be assigned: its organization-wide permission is {actual}, \
160                 expected {expected}"
161            ),
162            Self::TeamInherited { team, forge } => write!(
163                f,
164                "team {team} is inherited from an all-repositories {forge} team and cannot be \
165                 removed here"
166            ),
167            Self::TeamPermissionIsOrganizationWide {
168                team,
169                actual,
170                expected,
171                forge,
172            } => write!(
173                f,
174                "team {team} has permission {actual} and {expected} is declared, but {forge} only \
175                 changes team permissions organization-wide"
176            ),
177            Self::TeamRoleNeedsOrganization { team, role } => write!(
178                f,
179                "team {team} needs {role}, which only organization repositories have"
180            ),
181            Self::CollaboratorRoleNeedsOrganization { user, role } => write!(
182                f,
183                "collaborator {user} needs {role}, which only organization repositories have"
184            ),
185            Self::NotReported { field } => {
186                write!(f, "{field} is not reported for this repository")
187            }
188            Self::WrittenOnEveryApply { field } => write!(
189                f,
190                "{field} is not reported by Forgejo, so it is written on every apply"
191            ),
192            Self::Unavailable { field } => {
193                write!(f, "{field} is not available for this repository")
194            }
195            Self::EnforcedByOwner { field } => write!(f, "{field} is enforced by the owner"),
196            Self::Ignored { field, switch } => {
197                write!(f, "{field} is ignored while {switch} is false")
198            }
199            Self::MirrorOnly => {
200                f.write_str("mirror_interval and enable_prune only apply to a mirror")
201            }
202            Self::ActionsUnavailable { group } => {
203                write!(f, "actions {group} is unavailable for this repository")
204            }
205            Self::ActionsPrivateOnly { group } => {
206                write!(f, "actions {group} applies to private repositories only")
207            }
208            Self::SecurityUnreadable { field } => {
209                write!(f, "security: {field} cannot be read, so it is not managed")
210            }
211            Self::CustomPropertiesUnsupported => f.write_str(
212                "custom properties: the repository does not support them, so they are not managed",
213            ),
214            Self::EnvironmentRuleUnmanaged { environment, rule } => write!(
215                f,
216                "environment {environment}: the {rule} protection rule is not managed"
217            ),
218            Self::VariablesWithoutEnvironment { environment } => {
219                if let Some(environment) = environment {
220                    write!(f, "environment {environment} ")?;
221                }
222                f.write_str(
223                    "variables: the environment does not exist, so its variables are not managed",
224                )
225            }
226            Self::WebhookKindLeftAlone { id, kind } => {
227                write!(f, "webhook {id} is a {kind} hook and is left alone")
228            }
229            Self::WebhookSecretKept { url } => write!(
230                f,
231                "webhook {url}: the delivery settings were left unchanged because GitHub would \
232                 clear the secret set by hand"
233            ),
234            Self::RequiredReviewersDropped { ruleset } => write!(
235                f,
236                "ruleset \"{ruleset}\" has required_reviewers in its pull_request rule, which an \
237                 update drops"
238            ),
239            Self::UnmanagedRuleType { ruleset, kind } => {
240                write!(
241                    f,
242                    "ruleset \"{ruleset}\" contains unmanaged rule type \"{kind}\""
243                )
244            }
245            Self::UnaddressableRule { name } => write!(
246                f,
247                "live branch rule \"{name}\" cannot be addressed by the API routes, so it is left \
248                 in place"
249            ),
250        }
251    }
252}