Skip to main content

ephemeris_forge_github/api/model/
repository.rs

1use facet::Facet;
2
3use super::Analysis;
4use crate::{
5    MergeCommitMessage, MergeCommitTitle, PullRequestCreationPolicy, Reviewer, ReviewerMode,
6    ReviewerType, SquashMergeCommitMessage, SquashMergeCommitTitle, Status, Visibility,
7};
8
9/// What the authenticated caller may do with the repository.
10#[derive(Clone, Copy, Debug, Default, Eq, Facet, PartialEq)]
11pub struct ViewerPermissions {
12    /// Whether the caller administers the repository.
13    #[facet(default)]
14    pub admin: bool,
15}
16
17/// Fields returned by GitHub's repository endpoint.
18#[derive(Clone, Debug, Default, Eq, Facet, PartialEq)]
19#[expect(
20    clippy::struct_excessive_bools,
21    reason = "these are independent GitHub repository switches"
22)]
23pub struct Repository {
24    /// The repository's slug.
25    pub name: String,
26    /// The one-line summary shown on the repository's landing page, empty when
27    /// unset.
28    #[facet(default)]
29    pub description: String,
30    /// The project home page, empty when unset.
31    #[facet(default)]
32    pub homepage: String,
33    /// Who can see the repository.
34    #[facet(default)]
35    pub visibility: Option<Visibility>,
36    /// Topic labels used for discovery and filtering.
37    pub topics: Vec<String>,
38    /// Whether the repository rejects writes because it is archived.
39    pub archived: bool,
40    /// The branch rulesets and checks default to, when set.
41    pub default_branch: Option<String>,
42    /// Whether merging is allowed to create a merge commit.
43    pub allow_merge_commit: bool,
44    /// Whether merging is allowed to rebase commits onto the base branch.
45    pub allow_rebase_merge: bool,
46    /// Whether merging is allowed to squash commits into one.
47    pub allow_squash_merge: bool,
48    /// Whether pull requests may merge automatically once checks pass.
49    #[facet(default)]
50    pub allow_auto_merge: bool,
51    /// Whether the source branch is deleted automatically after merging.
52    pub delete_branch_on_merge: bool,
53    /// Whether a pull request branch may be updated by rebasing it.
54    pub allow_update_branch: bool,
55    /// Whether the issue tracker is enabled.
56    pub has_issues: bool,
57    /// Whether project boards are enabled.
58    pub has_projects: bool,
59    /// Whether the wiki is enabled.
60    pub has_wiki: bool,
61    /// Whether pull requests are enabled.
62    #[facet(default = true)]
63    pub has_pull_requests: bool,
64    /// Whether discussions are enabled.
65    #[facet(default)]
66    pub has_discussions: bool,
67    /// Who can open pull requests.
68    #[facet(default)]
69    pub pull_request_creation_policy: PullRequestCreationPolicy,
70    /// Whether the repository is a template.
71    #[facet(default)]
72    pub is_template: bool,
73    /// The title of a squash merge commit.
74    #[facet(default)]
75    pub squash_merge_commit_title: SquashMergeCommitTitle,
76    /// The message of a squash merge commit.
77    #[facet(default)]
78    pub squash_merge_commit_message: SquashMergeCommitMessage,
79    /// The title of a merge commit.
80    #[facet(default)]
81    pub merge_commit_title: MergeCommitTitle,
82    /// The message of a merge commit.
83    #[facet(default)]
84    pub merge_commit_message: MergeCommitMessage,
85    /// Whether private forks are allowed, when GitHub reports it.
86    #[facet(default)]
87    pub allow_forking: Option<bool>,
88    /// Whether web commits need a sign-off.
89    #[facet(default)]
90    pub web_commit_signoff_required: bool,
91    /// The security features GitHub reports. Absent features are unreadable.
92    #[facet(default)]
93    pub security_and_analysis: Analysis,
94    /// What the authenticated caller may do. Absent for anonymous reads.
95    #[facet(default)]
96    pub permissions: Option<ViewerPermissions>,
97}
98
99/// A security feature switch as GitHub answers with it.
100#[derive(Clone, Debug, Facet)]
101pub(crate) struct StatusWire {
102    status: Status,
103}
104
105/// A delegated bypass reviewer as GitHub answers with it.
106#[derive(Clone, Debug, Facet)]
107pub(crate) struct ReviewerWire {
108    reviewer_id: u64,
109    reviewer_type: ReviewerType,
110    #[facet(default)]
111    mode: Option<ReviewerMode>,
112}
113
114/// The delegated bypass options as GitHub answers with them.
115#[derive(Clone, Debug, Default, Facet)]
116pub(crate) struct BypassOptionsWire {
117    #[facet(default)]
118    reviewers: Vec<ReviewerWire>,
119}
120
121/// The `security_and_analysis` object as GitHub answers with it.
122#[derive(Clone, Debug, Default, Facet)]
123pub(crate) struct AnalysisWire {
124    #[facet(default)]
125    advanced_security: Option<StatusWire>,
126    #[facet(default)]
127    code_security: Option<StatusWire>,
128    #[facet(default)]
129    secret_scanning: Option<StatusWire>,
130    #[facet(default)]
131    secret_scanning_push_protection: Option<StatusWire>,
132    #[facet(default)]
133    secret_scanning_ai_detection: Option<StatusWire>,
134    #[facet(default)]
135    secret_scanning_non_provider_patterns: Option<StatusWire>,
136    #[facet(default)]
137    secret_scanning_delegated_alert_dismissal: Option<StatusWire>,
138    #[facet(default)]
139    secret_scanning_delegated_bypass: Option<StatusWire>,
140    #[facet(default)]
141    secret_scanning_delegated_bypass_options: Option<BypassOptionsWire>,
142}
143
144impl From<&Analysis> for AnalysisWire {
145    fn from(value: &Analysis) -> Self {
146        let status = |status: Option<Status>| status.map(|status| StatusWire { status });
147        Self {
148            advanced_security: status(value.advanced_security),
149            code_security: status(value.code_security),
150            secret_scanning: status(value.secret_scanning),
151            secret_scanning_push_protection: status(value.secret_scanning_push_protection),
152            secret_scanning_ai_detection: status(value.secret_scanning_ai_detection),
153            secret_scanning_non_provider_patterns: status(
154                value.secret_scanning_non_provider_patterns,
155            ),
156            secret_scanning_delegated_alert_dismissal: status(
157                value.secret_scanning_delegated_alert_dismissal,
158            ),
159            secret_scanning_delegated_bypass: status(value.secret_scanning_delegated_bypass),
160            secret_scanning_delegated_bypass_options: Some(BypassOptionsWire {
161                reviewers: value
162                    .secret_scanning_delegated_bypass_reviewers
163                    .iter()
164                    .map(|reviewer| ReviewerWire {
165                        reviewer_id: reviewer.reviewer_id,
166                        reviewer_type: reviewer.reviewer_type,
167                        mode: Some(reviewer.mode),
168                    })
169                    .collect(),
170            }),
171        }
172    }
173}
174
175impl From<AnalysisWire> for Analysis {
176    fn from(value: AnalysisWire) -> Self {
177        let status = |wire: Option<StatusWire>| wire.map(|wire| wire.status);
178        let mut reviewers: Vec<Reviewer> = value
179            .secret_scanning_delegated_bypass_options
180            .unwrap_or_default()
181            .reviewers
182            .into_iter()
183            .map(|reviewer| Reviewer {
184                reviewer_id: reviewer.reviewer_id,
185                reviewer_type: reviewer.reviewer_type,
186                mode: reviewer.mode.unwrap_or(ReviewerMode::Always),
187            })
188            .collect();
189        reviewers.sort_unstable();
190        Self {
191            advanced_security: status(value.advanced_security),
192            code_security: status(value.code_security),
193            secret_scanning: status(value.secret_scanning),
194            secret_scanning_push_protection: status(value.secret_scanning_push_protection),
195            secret_scanning_ai_detection: status(value.secret_scanning_ai_detection),
196            secret_scanning_non_provider_patterns: status(
197                value.secret_scanning_non_provider_patterns,
198            ),
199            secret_scanning_delegated_alert_dismissal: status(
200                value.secret_scanning_delegated_alert_dismissal,
201            ),
202            secret_scanning_delegated_bypass: status(value.secret_scanning_delegated_bypass),
203            secret_scanning_delegated_bypass_reviewers: reviewers,
204        }
205    }
206}