Skip to main content

ephemeris_forge_forgejo/config/
repository.rs

1use facet::Facet;
2
3const MAX_TOPIC_LENGTH: usize = 35;
4
5/// A repository topic in the form Forgejo stores.
6#[derive(Clone, Debug, Eq, Facet, Ord, PartialEq, PartialOrd)]
7#[facet(proxy = String)]
8pub struct TopicName(String);
9
10/// A topic Forgejo would rewrite or drop.
11#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
12#[error(
13    "topic \"{name}\" must be at most 35 characters of lowercase letters, digits, hyphens and \
14     dots, starting with a letter or digit"
15)]
16pub struct InvalidTopic {
17    /// The rejected topic.
18    pub name: String,
19}
20
21impl TopicName {
22    /// Whether Forgejo stores `topic` unchanged.
23    #[must_use]
24    pub fn is_canonical(topic: &str) -> bool {
25        let mut chars = topic.chars();
26        topic.len() <= MAX_TOPIC_LENGTH
27            && chars
28                .next()
29                .is_some_and(|first| first.is_ascii_lowercase() || first.is_ascii_digit())
30            && chars.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || matches!(c, '-' | '.'))
31    }
32
33    /// Borrow the topic.
34    #[must_use]
35    pub fn as_str(&self) -> &str {
36        &self.0
37    }
38}
39
40impl TryFrom<String> for TopicName {
41    type Error = InvalidTopic;
42
43    fn try_from(name: String) -> Result<Self, Self::Error> {
44        if Self::is_canonical(&name) {
45            Ok(Self(name))
46        } else {
47            Err(InvalidTopic { name })
48        }
49    }
50}
51
52impl From<&TopicName> for String {
53    fn from(value: &TopicName) -> Self {
54        value.0.clone()
55    }
56}
57
58ephemeris_forge::string_enum! {
59    /// The merge strategy preselected in the merge dialog.
60    pub enum MergeStyle("merge style") {
61        /// Merge only when the branch can fast-forward.
62        FastForwardOnly = "fast-forward-only",
63        /// Create a merge commit.
64        Merge = "merge",
65        /// Rebase the commits onto the base branch.
66        Rebase = "rebase",
67        /// Rebase the commits, then create a merge commit.
68        RebaseMerge = "rebase-merge",
69        /// Squash the commits into one.
70        Squash = "squash",
71    }
72}
73
74ephemeris_forge::string_enum! {
75    /// The strategy used to update an out-of-date pull request.
76    pub enum UpdateStyle("update style") {
77        /// Merge the base branch into the pull request branch.
78        Merge = "merge",
79        /// Rebase the pull request branch onto the base branch.
80        Rebase = "rebase",
81    }
82}
83
84ephemeris_forge::string_enum! {
85    /// How an external issue tracker numbers its issues.
86    pub enum TrackerStyle("tracker style") {
87        /// Issues are numbers such as `123`.
88        Numeric = "numeric",
89        /// Issues are letters and numbers such as `ABC-123`.
90        Alphanumeric = "alphanumeric",
91        /// Issues are found with a regular expression.
92        Regexp = "regexp",
93    }
94}
95
96/// Settings of the built-in issue tracker. Null leaves a setting alone.
97#[derive(Clone, Copy, Debug, Eq, Facet, PartialEq)]
98#[facet(deny_unknown_fields)]
99pub struct InternalTracker {
100    /// Whether time tracking is enabled.
101    pub enable_time_tracker: Option<bool>,
102    /// Whether only contributors may track time.
103    pub allow_only_contributors_to_track_time: Option<bool>,
104    /// Whether issues and pull requests may depend on each other.
105    pub enable_issue_dependencies: Option<bool>,
106}
107
108impl InternalTracker {
109    /// Whether any setting is declared.
110    #[must_use]
111    pub const fn declares_any(self) -> bool {
112        self.enable_time_tracker.is_some()
113            || self.allow_only_contributors_to_track_time.is_some()
114            || self.enable_issue_dependencies.is_some()
115    }
116}
117
118/// Settings of an external issue tracker.
119#[derive(Clone, Debug, Eq, Facet, PartialEq)]
120#[facet(deny_unknown_fields)]
121pub struct ExternalTracker {
122    /// The URL of the tracker.
123    pub external_tracker_url: String,
124    /// The issue URL format with the placeholders `{user}`, `{repo}` and
125    /// `{index}`.
126    pub external_tracker_format: String,
127    /// How the tracker numbers its issues.
128    pub external_tracker_style: Option<TrackerStyle>,
129    /// The expression that finds issues when the style is `regexp`.
130    pub external_tracker_regexp_pattern: Option<String>,
131}
132
133/// Settings of an external wiki.
134#[derive(Clone, Debug, Eq, Facet, PartialEq)]
135#[facet(deny_unknown_fields)]
136pub struct ExternalWiki {
137    /// The URL of the wiki.
138    pub external_wiki_url: String,
139}
140
141/// Desired settings for a repository.
142#[derive(Clone, Debug, Eq, Facet, PartialEq)]
143#[facet(deny_unknown_fields)]
144pub struct Repository {
145    /// The one-line summary shown on the repository's landing page.
146    pub description: Option<String>,
147    /// Topic labels used for discovery and filtering.
148    pub topics: Option<Vec<TopicName>>,
149    /// Whether a merge is rejected unless the branch can fast-forward.
150    pub allow_fast_forward_only_merge: Option<bool>,
151    /// Whether merging is allowed to create a merge commit.
152    pub allow_merge_commits: Option<bool>,
153    /// Whether merging is allowed to rebase commits onto the base branch.
154    pub allow_rebase: Option<bool>,
155    /// Whether rebase merges may be done with an explicit merge commit.
156    pub allow_rebase_explicit: Option<bool>,
157    /// Whether a pull request branch may be updated by rebasing it.
158    pub allow_rebase_update: Option<bool>,
159    /// Whether merging is allowed to squash commits into one.
160    pub allow_squash_merge: Option<bool>,
161    /// Whether the source branch is deleted automatically after merging.
162    pub default_delete_branch_after_merge: Option<bool>,
163    /// The merge strategy preselected in the merge dialog.
164    pub default_merge_style: Option<MergeStyle>,
165    /// The strategy preselected for updating an out-of-date pull request.
166    pub default_update_style: Option<UpdateStyle>,
167    /// Whether CI/CD actions are enabled.
168    pub has_actions: Option<bool>,
169    /// Whether the issue tracker is enabled.
170    pub has_issues: Option<bool>,
171    /// Whether the package registry is enabled.
172    pub has_packages: Option<bool>,
173    /// Whether project boards are enabled.
174    pub has_projects: Option<bool>,
175    /// Whether pull requests are enabled.
176    pub has_pull_requests: Option<bool>,
177    /// Whether releases are enabled.
178    pub has_releases: Option<bool>,
179    /// Whether the wiki is enabled.
180    pub has_wiki: Option<bool>,
181    /// The URL with more information about the repository.
182    pub website: Option<String>,
183    /// Whether the repository is private. Null leaves visibility alone.
184    pub private: Option<bool>,
185    /// Whether the repository is a template.
186    pub template: Option<bool>,
187    /// The default branch. Null leaves it alone.
188    pub default_branch: Option<String>,
189    /// Whether the repository is archived.
190    pub archived: Option<bool>,
191    /// The external wiki. Null leaves the wiki unit alone.
192    pub external_wiki: Option<ExternalWiki>,
193    /// The built-in issue tracker settings. They apply when the external
194    /// tracker is null.
195    pub internal_tracker: InternalTracker,
196    /// The external issue tracker. Null uses the built-in tracker.
197    pub external_tracker: Option<ExternalTracker>,
198    /// Whether whitespace is ignored when checking pull requests for
199    /// conflicts.
200    pub ignore_whitespace_conflicts: Option<bool>,
201    /// Whether a pull request may be marked as merged by hand. Forgejo does
202    /// not report the setting. Null leaves it alone.
203    pub allow_manual_merge: Option<bool>,
204    /// Whether manual merges are detected automatically. Forgejo does not
205    /// report the setting. Null leaves it alone.
206    pub autodetect_manual_merge: Option<bool>,
207    /// Whether maintainers may edit pull requests by default.
208    pub default_allow_maintainer_edit: Option<bool>,
209    /// Whether anyone with write access may edit the wiki.
210    pub globally_editable_wiki: Option<bool>,
211    /// The branch that holds the wiki. Null leaves it alone.
212    pub wiki_branch: Option<String>,
213    /// The mirror sync interval, such as `8h30m0s`. Null leaves it alone.
214    pub mirror_interval: Option<String>,
215    /// Whether a mirror removes obsolete remote-tracking references. Forgejo
216    /// does not report the setting. Null leaves it alone.
217    pub enable_prune: Option<bool>,
218}