octocrab/
models.rs

1//! Serde mappings from GitHub's JSON to structs.
2
3use std::collections::HashMap;
4use std::fmt;
5use std::ops::{Deref, DerefMut};
6
7use chrono::{DateTime, Utc};
8use serde::{de, Deserialize, Deserializer, Serialize};
9use url::Url;
10
11use crate::params::users::emails::EmailVisibilityState;
12pub use apps::App;
13
14pub mod actions;
15pub mod activity;
16pub mod apps;
17pub mod checks;
18pub mod code_scannings;
19pub mod commits;
20pub mod events;
21pub mod gists;
22pub mod hooks;
23pub mod issues;
24pub mod orgs;
25pub mod pulls;
26pub mod reactions;
27pub mod repos;
28pub mod teams;
29pub mod timelines;
30pub mod webhook_events;
31pub mod workflows;
32
33mod date_serde;
34
35type BaseIdType = u64;
36
37macro_rules! id_type {
38    // This macro takes an argument of designator `ident` and
39    // creates a function named `$func_name`.
40    // The `ident` designator is used for variable/function names.
41    ($($name:ident),+) => {$(
42        #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
43        pub struct $name(pub BaseIdType);
44        impl fmt::Display for $name {
45            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46                self.0.fmt(f)
47            }
48        }
49        impl Deref for $name {
50            type Target = BaseIdType;
51            fn deref(&self) -> &Self::Target {
52                &self.0
53            }
54        }
55        impl DerefMut for $name {
56            fn deref_mut(&mut self) -> &mut Self::Target {
57                &mut self.0
58            }
59        }
60        impl $name {
61            pub fn into_inner(self) -> BaseIdType {
62                self.0
63            }
64        }
65        impl From<BaseIdType> for $name {
66            fn from(value: BaseIdType) -> Self {
67                Self(value)
68            }
69        }
70        impl AsRef<BaseIdType> for $name {
71            fn as_ref(&self) -> &BaseIdType {
72                &self.0
73            }
74        }
75        impl<'de> Deserialize<'de> for $name {
76            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
77                where D: Deserializer<'de>
78            {
79                struct IdVisitor;
80                impl<'de> de::Visitor<'de> for IdVisitor {
81                    type Value = $name;
82                    fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
83                        where E: de::Error {
84                        Ok($name(value))
85                    }
86                    fn visit_str<E>(self, id: &str) -> Result<Self::Value, E>
87                        where E: de::Error {
88                        id.parse::<u64>().map($name).map_err(de::Error::custom)
89                    }
90                    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
91                        write!(f, "expected {} as number or string", stringify!($name)) // TODO: $name
92                    }
93                }
94
95                deserializer.deserialize_any(IdVisitor)
96            }
97         }
98    )+};
99}
100
101id_type!(
102    ActorId, // A Bot, EnterpriseUserAccount, Mannequin, Organization or User
103    AppId,
104    ArtifactId,
105    AssetId,
106    BranchProtectionRuleId,
107    CardId,
108    CheckSuiteId,
109    CheckRunId,
110    CodeScanningId,
111    CommentId,
112    InstallationId,
113    IssueEventId,
114    IssueId,
115    JobId,
116    HookId,
117    HookDeliveryId,
118    LabelId,
119    MilestoneId,
120    NotificationId,
121    OrgId,
122    ProjectId,
123    ProjectColumnId,
124    PullRequestId,
125    PushId,
126    ReactionId,
127    ReleaseId,
128    RepositoryId,
129    ReviewId,
130    RunId,
131    RunnerId,
132    RunnerGroupId,
133    RunnerLabelId,
134    StatusId,
135    TeamId,
136    TimelineEventId,
137    ThreadId,
138    UploaderId,
139    UserId,
140    UserOrOrgId,
141    WorkflowId,
142    TeamInvitationId
143);
144
145macro_rules! convert_into {
146    ($($from:ident -> $to:ident),+) => {$(
147        impl From<$from> for $to {
148            fn from(v: $from) -> $to {
149                $to(v.0)
150            }
151        }
152    )+};
153}
154
155convert_into!(OrgId -> ActorId,
156              UserId -> ActorId,
157              OrgId -> UserOrOrgId,
158              UserId -> UserOrOrgId,
159              PullRequestId -> IssueId);
160
161#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
162#[non_exhaustive]
163pub struct Contents {
164    #[serde(rename = "type")]
165    pub contents_type: String,
166    pub encoding: String,
167    pub size: u64,
168    pub name: String,
169    pub path: String,
170    pub content: String,
171    pub sha: String,
172    pub url: Url,
173    pub git_url: Url,
174    pub html_url: Url,
175    pub download_url: Url,
176}
177
178/// Issue events are triggered by activity in issues and pull requests.
179/// <https://docs.github.com/en/webhooks-and-events/events/issue-event-types>
180#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
181#[serde(rename_all = "snake_case")]
182#[non_exhaustive]
183pub enum Event {
184    /// The issue or pull request was added to a project board.
185    AddedToProject,
186    /// The issue or pull request was assigned to a user.
187    Assigned,
188    /// Auto merge was disabled for a pull request.
189    AutoMergeDisabled,
190    /// Auto merge was enabled for a pull request.
191    AutoMergeEnabled,
192    /// GitHub unsuccessfully attempted to automatically change the base branch of the pull request.
193    AutomaticBaseChangeFailed,
194    /// GitHub successfully attempted to automatically change the base branch of the pull request.
195    AutomaticBaseChangeSucceeded,
196    /// Auto Rebase Enable
197    AutoRebaseEnabled,
198    /// Auto Squash Enable
199    AutoSquashEnabled,
200    /// The base reference branch of the pull request changed.
201    BaseRefChanged,
202    /// Not documented in the Github issue events documentation.
203    BaseRefForcePushed,
204    /// The issue or pull request was closed. When the commit_id is present, it identifies the commit that closed the issue using "closes / fixes" syntax.
205    Closed,
206    /// A comment was added to the issue or pull request.
207    Commented,
208    /// A comment that was removed from the issue or pull request.
209    /// This isn't documented as part of the issues-event-types API but returned by the API.
210    CommentDeleted,
211    /// A commit was added to the pull request's HEAD branch.
212    Committed,
213    /// The issue or pull request was linked to another issue or pull request.
214    Connected,
215    /// The pull request was converted to draft mode.
216    ConvertToDraft,
217    /// The issue was created by converting a note in a project board to an issue.
218    ConvertedNoteToIssue,
219    /// The issue was closed and converted to a discussion.
220    ConvertedToDiscussion,
221    /// The issue or pull request was referenced from another issue or pull request.
222    #[serde(rename = "cross-referenced")]
223    CrossReferenced,
224    /// The issue or pull request was removed from a milestone.
225    Demilestoned,
226    /// The pull request was deployed.
227    Deployed,
228    /// The pull request deployment environment was changed.
229    DeploymentEnvironmentChanged,
230    /// The issue or pull request was unlinked from another issue or pull request.
231    Disconnected,
232    /// The pull request's HEAD branch was deleted.
233    HeadRefDeleted,
234    /// The pull request's HEAD branch was force pushed.
235    HeadRefForcePushed,
236    /// The pull request's HEAD branch was restored to the last known commit.
237    HeadRefRestored,
238    /// A label was added to the issue or pull request.
239    Labeled,
240    /// A comment on a line of source in a pull request. Not documented in the issue and events documentation.
241    #[serde(rename = "line-commented")]
242    LineCommented,
243    /// The issue or pull request was locked.
244    Locked,
245    /// The actor was @mentioned in an issue or pull request body.
246    Mentioned,
247    /// A user with write permissions marked an issue as a duplicate of another issue, or a pull request as a duplicate of another pull request.
248    MarkedAsDuplicate,
249    /// The pull request was merged. The commit_id attribute is the SHA1 of the HEAD commit that was merged. The commit_repository is always the same as the main repository.
250    Merged,
251    /// The issue or pull request was added to a milestone.
252    Milestoned,
253    /// The issue or pull request was moved between columns in a project board.
254    MovedColumnsInProject,
255    /// The issue was pinned.
256    Pinned,
257    /// A draft pull request was marked as ready for review.
258    ReadyForReview,
259    /// The issue was referenced from a commit message. The commit_id attribute is the commit SHA1 of where that happened and the commit_repository is where that commit was pushed.
260    Referenced,
261    /// The issue or pull request was removed from a project board.
262    RemovedFromProject,
263    /// The issue or pull request title was changed.
264    Renamed,
265    /// The issue or pull request was reopened.
266    Reopened,
267    /// The pull request review was dismissed.
268    ReviewDismissed,
269    /// A pull request review was requested.
270    ReviewRequested,
271    /// A pull request review request was removed.
272    ReviewRequestRemoved,
273    /// The pull request was reviewed.
274    Reviewed,
275    /// Someone subscribed to receive notifications for an issue or pull request.
276    Subscribed,
277    /// The issue was transferred to another repository.
278    Transferred,
279    /// A user was unassigned from the issue.
280    Unassigned,
281    /// A label was removed from the issue.
282    Unlabeled,
283    /// The issue was unlocked.
284    Unlocked,
285    /// An issue that a user had previously marked as a duplicate of another issue is no longer considered a duplicate, or a pull request that a user had previously marked as a duplicate of another pull request is no longer considered a duplicate.
286    UnmarkedAsDuplicate,
287    /// The issue was unpinned.
288    Unpinned,
289    /// Someone unsubscribed from receiving notifications for an issue or pull request.
290    Unsubscribed,
291    /// An organization owner blocked a user from the organization.
292    UserBlocked,
293}
294
295#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
296#[serde(rename_all = "lowercase")]
297#[non_exhaustive]
298pub enum IssueState {
299    Open,
300    Closed,
301}
302
303#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
304#[non_exhaustive]
305pub struct IssueEvent {
306    #[serde(skip_serializing_if = "Option::is_none")]
307    pub id: Option<IssueEventId>,
308    #[serde(skip_serializing_if = "Option::is_none")]
309    pub node_id: Option<String>,
310    #[serde(skip_serializing_if = "Option::is_none")]
311    pub url: Option<String>,
312    pub actor: Author,
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub assignee: Option<Author>,
315    #[serde(skip_serializing_if = "Option::is_none")]
316    pub assignees: Option<Vec<Author>>,
317    #[serde(skip_serializing_if = "Option::is_none")]
318    pub assigner: Option<Author>,
319    #[serde(skip_serializing_if = "Option::is_none")]
320    pub labels: Option<Vec<Label>>,
321    #[serde(skip_serializing_if = "Option::is_none")]
322    pub milestone: Option<Milestone>,
323    #[serde(skip_serializing_if = "Option::is_none")]
324    pub project_card: Option<ProjectCard>,
325    #[serde(skip_serializing_if = "Option::is_none")]
326    pub event: Option<Event>,
327    #[serde(skip_serializing_if = "Option::is_none")]
328    pub commit_id: Option<String>,
329    #[serde(skip_serializing_if = "Option::is_none")]
330    pub commit_url: Option<String>,
331    pub created_at: DateTime<Utc>,
332}
333
334#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
335#[non_exhaustive]
336pub struct ProjectCard {
337    pub id: CardId,
338    pub url: Url,
339    pub project_id: ProjectId,
340    pub project_url: Url,
341    #[serde(skip_serializing_if = "Option::is_none")]
342    pub column_name: Option<String>,
343    #[serde(skip_serializing_if = "Option::is_none")]
344    pub previous_column_name: Option<String>,
345    #[serde(skip_serializing_if = "Option::is_none")]
346    pub column_url: Option<Url>,
347}
348
349#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
350#[non_exhaustive]
351pub struct Project {
352    pub owner_url: Url,
353    pub url: Url,
354    pub html_url: Url,
355    pub columns_url: Url,
356    pub id: ProjectId,
357    pub node_id: String,
358    pub name: String,
359    #[serde(skip_serializing_if = "Option::is_none")]
360    pub body: Option<String>,
361    pub number: u32,
362    #[serde(skip_serializing_if = "Option::is_none")]
363    pub state: Option<String>,
364    pub creator: Author,
365    pub created_at: DateTime<Utc>,
366    #[serde(skip_serializing_if = "Option::is_none")]
367    pub updated_at: Option<DateTime<Utc>>,
368}
369
370#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
371#[non_exhaustive]
372pub enum ProjectCardContentType {
373    Issue,
374    PullRequest,
375}
376
377#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
378#[non_exhaustive]
379pub struct ProjectColumn {
380    pub url: Url,
381    pub project_url: Url,
382    pub cards_url: Url,
383    pub id: ProjectColumnId,
384    pub node_id: String,
385    pub name: String,
386    pub created_at: DateTime<Utc>,
387    #[serde(skip_serializing_if = "Option::is_none")]
388    pub updated_at: Option<DateTime<Utc>>,
389}
390
391#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
392#[non_exhaustive]
393pub struct IssuePullRequest {
394    #[serde(skip_serializing_if = "Option::is_none")]
395    pub url: Option<String>,
396    #[serde(skip_serializing_if = "Option::is_none")]
397    pub html_url: Option<String>,
398    #[serde(skip_serializing_if = "Option::is_none")]
399    pub diff_url: Option<String>,
400    #[serde(skip_serializing_if = "Option::is_none")]
401    pub patch_url: Option<String>,
402}
403
404#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
405#[non_exhaustive]
406pub struct Author {
407    pub login: String,
408    pub id: UserId,
409    pub node_id: String,
410    pub avatar_url: Url,
411    pub gravatar_id: String,
412    pub url: Url,
413    pub html_url: Url,
414    pub followers_url: Url,
415    pub following_url: Url,
416    pub gists_url: Url,
417    pub starred_url: Url,
418    pub subscriptions_url: Url,
419    pub organizations_url: Url,
420    pub repos_url: Url,
421    pub events_url: Url,
422    pub received_events_url: Url,
423    pub r#type: String,
424    pub site_admin: bool,
425    pub name: Option<String>,
426    pub patch_url: Option<String>,
427    #[serde(skip_serializing_if = "Option::is_none")]
428    pub email: Option<String>,
429}
430
431/// If a string is empty then deserialize it as none
432fn empty_string_is_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
433where
434    D: Deserializer<'de>,
435{
436    // try to deserialize our input string
437    let cast = String::deserialize(deserializer)?;
438    // if this string is empty then return None
439    if cast.is_empty() {
440        Ok(None)
441    } else {
442        Ok(Some(cast))
443    }
444}
445
446/// The full profile for a user
447#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
448#[non_exhaustive]
449pub struct UserProfile {
450    pub login: String,
451    pub id: UserId,
452    pub node_id: String,
453    pub avatar_url: Url,
454    pub gravatar_id: String,
455    pub url: Url,
456    pub html_url: Url,
457    pub followers_url: Url,
458    pub following_url: Url,
459    pub gists_url: Url,
460    pub starred_url: Url,
461    pub subscriptions_url: Url,
462    pub organizations_url: Url,
463    pub repos_url: Url,
464    pub events_url: Url,
465    pub received_events_url: Url,
466    pub r#type: String,
467    pub site_admin: bool,
468    pub name: Option<String>,
469    pub company: Option<String>,
470    #[serde(deserialize_with = "empty_string_is_none")]
471    pub blog: Option<String>,
472    pub location: Option<String>,
473    #[serde(skip_serializing_if = "Option::is_none")]
474    pub email: Option<String>,
475    pub hireable: Option<bool>,
476    pub bio: Option<String>,
477    pub twitter_username: Option<String>,
478    pub public_repos: u64,
479    pub public_gists: u64,
480    pub followers: u64,
481    pub following: u64,
482    pub created_at: DateTime<Utc>,
483    pub updated_at: DateTime<Utc>,
484}
485
486/// Data for updating a user profile,
487/// e.g. name, email, company, location, bio, blog, twitter, hireable
488#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
489#[non_exhaustive]
490pub struct UpdateUserProfile {
491    pub name: Option<String>,
492    pub email: Option<String>,
493    pub blog: Option<String>,
494    pub twitter_username: Option<String>,
495    pub company: Option<String>,
496    pub location: Option<String>,
497    pub hireable: Option<bool>,
498    pub bio: Option<String>,
499}
500
501/// The simple profile for a GitHub user
502#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
503#[non_exhaustive]
504pub struct SimpleUser {
505    #[serde(skip_serializing_if = "Option::is_none")]
506    pub name: Option<String>,
507    #[serde(skip_serializing_if = "Option::is_none")]
508    pub email: Option<String>,
509    pub login: String,
510    pub id: UserId,
511    pub node_id: String,
512    pub avatar_url: Url,
513    pub gravatar_id: String,
514    pub url: Url,
515    pub html_url: Url,
516    pub followers_url: Url,
517    pub following_url: Url,
518    pub gists_url: Url,
519    pub starred_url: Url,
520    pub subscriptions_url: Url,
521    pub organizations_url: Url,
522    pub repos_url: Url,
523    pub events_url: Url,
524    pub received_events_url: Url,
525    pub r#type: String,
526    pub site_admin: bool,
527    pub starred_at: Option<DateTime<Utc>>,
528}
529
530/// A user that is following another user
531#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
532#[non_exhaustive]
533pub struct Follower {
534    pub login: String,
535    pub id: UserId,
536    pub node_id: String,
537    pub avatar_url: Url,
538    pub gravatar_id: String,
539    pub url: Url,
540    pub html_url: Url,
541    pub followers_url: Url,
542    pub following_url: Url,
543    pub gists_url: Url,
544    pub starred_url: Url,
545    pub subscriptions_url: Url,
546    pub organizations_url: Url,
547    pub repos_url: Url,
548    pub events_url: Url,
549    pub received_events_url: Url,
550    pub r#type: String,
551    pub site_admin: bool,
552    pub patch_url: Option<String>,
553}
554
555/// A user that is being followed by another user
556#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
557#[non_exhaustive]
558pub struct Followee {
559    pub login: String,
560    pub id: UserId,
561    pub node_id: String,
562    pub avatar_url: Url,
563    pub gravatar_id: String,
564    pub url: Url,
565    pub html_url: Url,
566    pub followers_url: Url,
567    pub following_url: Url,
568    pub gists_url: Url,
569    pub starred_url: Url,
570    pub subscriptions_url: Url,
571    pub organizations_url: Url,
572    pub repos_url: Url,
573    pub events_url: Url,
574    pub received_events_url: Url,
575    pub r#type: String,
576    pub site_admin: bool,
577    pub patch_url: Option<String>,
578}
579
580#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
581#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
582#[non_exhaustive]
583pub enum AuthorAssociation {
584    Collaborator,
585    Contributor,
586    FirstTimer,
587    FirstTimeContributor,
588    Mannequin,
589    Member,
590    None,
591    Owner,
592    #[serde(untagged)]
593    Other(String),
594}
595
596#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
597#[non_exhaustive]
598pub struct Collaborator {
599    #[serde(flatten)]
600    pub author: Author,
601    pub permissions: Permissions,
602}
603
604#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
605#[non_exhaustive]
606pub struct Contributor {
607    #[serde(flatten)]
608    pub author: Author,
609    pub contributions: u32,
610}
611
612#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
613#[non_exhaustive]
614pub struct StarGazer {
615    pub starred_at: Option<DateTime<Utc>>,
616    pub user: Option<Author>,
617}
618
619#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
620#[non_exhaustive]
621pub struct Label {
622    pub id: LabelId,
623    pub node_id: String,
624    pub url: Url,
625    pub name: String,
626    #[serde(skip_serializing_if = "Option::is_none")]
627    pub description: Option<String>,
628    pub color: String,
629    pub default: bool,
630}
631
632#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
633#[non_exhaustive]
634pub struct Milestone {
635    pub url: Url,
636    pub html_url: Url,
637    #[serde(skip_serializing_if = "Option::is_none")]
638    pub labels_url: Option<Url>,
639    pub id: MilestoneId,
640    pub node_id: String,
641    pub number: i64,
642    #[serde(skip_serializing_if = "Option::is_none")]
643    pub state: Option<String>,
644    pub title: String,
645    #[serde(skip_serializing_if = "Option::is_none")]
646    pub description: Option<String>,
647    #[serde(skip_serializing_if = "Option::is_none")]
648    pub creator: Option<Author>,
649    #[serde(skip_serializing_if = "Option::is_none")]
650    pub open_issues: Option<i64>,
651    #[serde(skip_serializing_if = "Option::is_none")]
652    pub closed_issues: Option<i64>,
653    pub created_at: DateTime<Utc>,
654    #[serde(skip_serializing_if = "Option::is_none")]
655    pub updated_at: Option<DateTime<Utc>>,
656    #[serde(skip_serializing_if = "Option::is_none")]
657    pub closed_at: Option<DateTime<Utc>>,
658    #[serde(skip_serializing_if = "Option::is_none")]
659    pub due_on: Option<DateTime<Utc>>,
660}
661
662#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
663#[non_exhaustive]
664pub struct Repository {
665    pub id: RepositoryId,
666    #[serde(skip_serializing_if = "Option::is_none")]
667    pub node_id: Option<String>,
668    pub name: String,
669    #[serde(skip_serializing_if = "Option::is_none")]
670    pub full_name: Option<String>,
671    #[serde(skip_serializing_if = "Option::is_none")]
672    pub owner: Option<Author>,
673    #[serde(skip_serializing_if = "Option::is_none")]
674    pub private: Option<bool>,
675    #[serde(skip_serializing_if = "Option::is_none")]
676    pub html_url: Option<Url>,
677    #[serde(skip_serializing_if = "Option::is_none")]
678    pub description: Option<String>,
679    #[serde(skip_serializing_if = "Option::is_none")]
680    pub fork: Option<bool>,
681    pub url: Url,
682    #[serde(skip_serializing_if = "Option::is_none")]
683    pub archive_url: Option<Url>,
684    #[serde(skip_serializing_if = "Option::is_none")]
685    pub assignees_url: Option<Url>,
686    #[serde(skip_serializing_if = "Option::is_none")]
687    pub blobs_url: Option<Url>,
688    #[serde(skip_serializing_if = "Option::is_none")]
689    pub branches_url: Option<Url>,
690    #[serde(skip_serializing_if = "Option::is_none")]
691    pub collaborators_url: Option<Url>,
692    #[serde(skip_serializing_if = "Option::is_none")]
693    pub comments_url: Option<Url>,
694    #[serde(skip_serializing_if = "Option::is_none")]
695    pub commits_url: Option<Url>,
696    #[serde(skip_serializing_if = "Option::is_none")]
697    pub compare_url: Option<Url>,
698    #[serde(skip_serializing_if = "Option::is_none")]
699    pub contents_url: Option<Url>,
700    #[serde(skip_serializing_if = "Option::is_none")]
701    pub contributors_url: Option<Url>,
702    #[serde(skip_serializing_if = "Option::is_none")]
703    pub deployments_url: Option<Url>,
704    #[serde(skip_serializing_if = "Option::is_none")]
705    pub downloads_url: Option<Url>,
706    #[serde(skip_serializing_if = "Option::is_none")]
707    pub events_url: Option<Url>,
708    #[serde(skip_serializing_if = "Option::is_none")]
709    pub forks_url: Option<Url>,
710    #[serde(skip_serializing_if = "Option::is_none")]
711    pub git_commits_url: Option<Url>,
712    #[serde(skip_serializing_if = "Option::is_none")]
713    pub git_refs_url: Option<Url>,
714    #[serde(skip_serializing_if = "Option::is_none")]
715    pub git_tags_url: Option<Url>,
716    #[serde(skip_serializing_if = "Option::is_none")]
717    pub git_url: Option<Url>,
718    #[serde(skip_serializing_if = "Option::is_none")]
719    pub issue_comment_url: Option<Url>,
720    #[serde(skip_serializing_if = "Option::is_none")]
721    pub issue_events_url: Option<Url>,
722    #[serde(skip_serializing_if = "Option::is_none")]
723    pub issues_url: Option<Url>,
724    #[serde(skip_serializing_if = "Option::is_none")]
725    pub keys_url: Option<Url>,
726    #[serde(skip_serializing_if = "Option::is_none")]
727    pub labels_url: Option<Url>,
728    #[serde(skip_serializing_if = "Option::is_none")]
729    pub languages_url: Option<Url>,
730    #[serde(skip_serializing_if = "Option::is_none")]
731    pub merges_url: Option<Url>,
732    #[serde(skip_serializing_if = "Option::is_none")]
733    pub milestones_url: Option<Url>,
734    #[serde(skip_serializing_if = "Option::is_none")]
735    pub notifications_url: Option<Url>,
736    #[serde(skip_serializing_if = "Option::is_none")]
737    pub pulls_url: Option<Url>,
738    #[serde(skip_serializing_if = "Option::is_none")]
739    pub releases_url: Option<Url>,
740    #[serde(skip_serializing_if = "Option::is_none")]
741    pub ssh_url: Option<String>,
742    #[serde(skip_serializing_if = "Option::is_none")]
743    pub stargazers_url: Option<Url>,
744    #[serde(skip_serializing_if = "Option::is_none")]
745    pub statuses_url: Option<Url>,
746    #[serde(skip_serializing_if = "Option::is_none")]
747    pub subscribers_url: Option<Url>,
748    #[serde(skip_serializing_if = "Option::is_none")]
749    pub subscription_url: Option<Url>,
750    #[serde(skip_serializing_if = "Option::is_none")]
751    pub tags_url: Option<Url>,
752    #[serde(skip_serializing_if = "Option::is_none")]
753    pub teams_url: Option<Url>,
754    #[serde(skip_serializing_if = "Option::is_none")]
755    pub trees_url: Option<Url>,
756    #[serde(skip_serializing_if = "Option::is_none")]
757    pub clone_url: Option<Url>,
758    #[serde(skip_serializing_if = "Option::is_none")]
759    pub mirror_url: Option<Url>,
760    #[serde(skip_serializing_if = "Option::is_none")]
761    pub hooks_url: Option<Url>,
762    #[serde(skip_serializing_if = "Option::is_none")]
763    pub svn_url: Option<Url>,
764    #[serde(skip_serializing_if = "Option::is_none")]
765    pub homepage: Option<String>,
766    #[serde(skip_serializing_if = "Option::is_none")]
767    pub language: Option<::serde_json::Value>,
768    #[serde(skip_serializing_if = "Option::is_none")]
769    pub forks_count: Option<u32>,
770    #[serde(skip_serializing_if = "Option::is_none")]
771    pub stargazers_count: Option<u32>,
772    #[serde(skip_serializing_if = "Option::is_none")]
773    pub watchers_count: Option<u32>,
774    #[serde(skip_serializing_if = "Option::is_none")]
775    pub size: Option<u32>,
776    #[serde(skip_serializing_if = "Option::is_none")]
777    pub default_branch: Option<String>,
778    #[serde(skip_serializing_if = "Option::is_none")]
779    pub open_issues_count: Option<u32>,
780    #[serde(skip_serializing_if = "Option::is_none")]
781    pub is_template: Option<bool>,
782    #[serde(skip_serializing_if = "Option::is_none")]
783    pub topics: Option<Vec<String>>,
784    #[serde(skip_serializing_if = "Option::is_none")]
785    pub has_issues: Option<bool>,
786    #[serde(skip_serializing_if = "Option::is_none")]
787    pub has_projects: Option<bool>,
788    #[serde(skip_serializing_if = "Option::is_none")]
789    pub has_wiki: Option<bool>,
790    #[serde(skip_serializing_if = "Option::is_none")]
791    pub has_pages: Option<bool>,
792    #[serde(skip_serializing_if = "Option::is_none")]
793    pub has_downloads: Option<bool>,
794    #[serde(skip_serializing_if = "Option::is_none")]
795    pub archived: Option<bool>,
796    #[serde(skip_serializing_if = "Option::is_none")]
797    pub disabled: Option<bool>,
798    #[serde(skip_serializing_if = "Option::is_none")]
799    pub visibility: Option<String>,
800    #[serde(
801        skip_serializing_if = "Option::is_none",
802        default,
803        deserialize_with = "date_serde::deserialize_opt"
804    )]
805    pub pushed_at: Option<DateTime<Utc>>,
806    #[serde(
807        skip_serializing_if = "Option::is_none",
808        default,
809        deserialize_with = "date_serde::deserialize_opt"
810    )]
811    pub created_at: Option<DateTime<Utc>>,
812    #[serde(
813        skip_serializing_if = "Option::is_none",
814        default,
815        deserialize_with = "date_serde::deserialize_opt"
816    )]
817    pub updated_at: Option<DateTime<Utc>>,
818    #[serde(skip_serializing_if = "Option::is_none")]
819    pub permissions: Option<Permissions>,
820    #[serde(skip_serializing_if = "Option::is_none")]
821    pub allow_rebase_merge: Option<bool>,
822    #[serde(skip_serializing_if = "Option::is_none")]
823    pub template_repository: Option<Box<Repository>>,
824    #[serde(skip_serializing_if = "Option::is_none")]
825    pub allow_squash_merge: Option<bool>,
826    #[serde(skip_serializing_if = "Option::is_none")]
827    pub allow_merge_commit: Option<bool>,
828    #[serde(skip_serializing_if = "Option::is_none")]
829    pub allow_update_branch: Option<bool>,
830    #[serde(skip_serializing_if = "Option::is_none")]
831    pub allow_forking: Option<bool>,
832    #[serde(skip_serializing_if = "Option::is_none")]
833    pub subscribers_count: Option<i64>,
834    #[serde(skip_serializing_if = "Option::is_none")]
835    pub network_count: Option<i64>,
836    #[serde(skip_serializing_if = "Option::is_none")]
837    pub license: Option<License>,
838    #[serde(skip_serializing_if = "Option::is_none")]
839    pub allow_auto_merge: Option<bool>,
840    #[serde(skip_serializing_if = "Option::is_none")]
841    pub delete_branch_on_merge: Option<bool>,
842    #[serde(skip_serializing_if = "Option::is_none")]
843    pub parent: Option<Box<Repository>>,
844    #[serde(skip_serializing_if = "Option::is_none")]
845    pub source: Option<Box<Repository>>,
846}
847
848#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
849pub struct RepositoryFile {
850    pub name: Option<String>,
851    pub key: Option<String>,
852    pub url: Option<Url>,
853    pub html_url: Option<Url>,
854}
855
856#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
857pub struct RepositoryMetrics {
858    pub health_percentage: u64,
859    pub description: Option<String>,
860    pub documentation: Option<String>,
861    pub files: HashMap<String, Option<RepositoryFile>>,
862    pub updated_at: Option<DateTime<Utc>>,
863    pub content_reports_enabled: Option<bool>,
864}
865
866#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
867#[non_exhaustive]
868pub struct License {
869    pub key: String,
870    pub name: String,
871    pub node_id: String,
872    pub spdx_id: String,
873    #[serde(skip_serializing_if = "Option::is_none")]
874    pub url: Option<Url>,
875    pub html_url: Option<Url>,
876    pub description: Option<String>,
877    pub implementation: Option<String>,
878    pub permissions: Option<Vec<String>>,
879    pub conditions: Option<Vec<String>>,
880    pub limitations: Option<Vec<String>>,
881    pub body: Option<String>,
882    pub featured: Option<bool>,
883}
884
885#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
886#[non_exhaustive]
887pub struct Code {
888    pub name: String,
889    pub path: String,
890    pub sha: String,
891    pub url: Url,
892    pub git_url: Url,
893    pub html_url: Url,
894    pub repository: Repository,
895}
896
897#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
898#[non_exhaustive]
899pub struct Permissions {
900    #[serde(default)]
901    pub admin: bool,
902    pub push: bool,
903    pub pull: bool,
904    #[serde(default)]
905    pub triage: bool,
906    #[serde(default)]
907    pub maintain: bool,
908}
909
910#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
911#[non_exhaustive]
912pub struct CheckRuns {
913    pub total_count: i32,
914    pub check_runs: Vec<CheckRun>,
915}
916
917#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
918#[non_exhaustive]
919pub struct CheckRun {
920    #[serde(skip_serializing_if = "Option::is_none")]
921    pub id: Option<CheckRunId>,
922    #[serde(skip_serializing_if = "Option::is_none")]
923    pub head_sha: Option<String>,
924    #[serde(skip_serializing_if = "Option::is_none")]
925    pub node_id: Option<String>,
926    #[serde(skip_serializing_if = "Option::is_none")]
927    pub external_id: Option<String>,
928    #[serde(skip_serializing_if = "Option::is_none")]
929    pub url: Option<String>,
930    #[serde(skip_serializing_if = "Option::is_none")]
931    pub html_url: Option<String>,
932    #[serde(skip_serializing_if = "Option::is_none")]
933    pub details_url: Option<String>,
934    #[serde(skip_serializing_if = "Option::is_none")]
935    pub status: Option<CheckStatus>,
936    #[serde(skip_serializing_if = "Option::is_none")]
937    pub conclusion: Option<String>,
938    #[serde(skip_serializing_if = "Option::is_none")]
939    pub started_at: Option<DateTime<Utc>>,
940    #[serde(skip_serializing_if = "Option::is_none")]
941    pub completed_at: Option<DateTime<Utc>>,
942}
943
944#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
945#[serde(rename_all = "snake_case")]
946#[non_exhaustive]
947pub enum CheckStatus {
948    Queued,
949    Completed,
950    InProgress,
951}
952
953#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
954#[non_exhaustive]
955pub struct CombinedStatus {
956    pub state: StatusState,
957    pub sha: String,
958    pub total_count: i64,
959    pub statuses: Vec<Status>,
960    #[serde(skip_serializing)]
961    pub repository: Option<Repository>,
962    #[serde(skip_serializing)]
963    pub commit_url: Option<Url>,
964    #[serde(skip_serializing)]
965    pub url: Option<Url>,
966}
967
968#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
969#[non_exhaustive]
970pub struct Status {
971    #[serde(skip_serializing_if = "Option::is_none")]
972    pub id: Option<StatusId>,
973    #[serde(skip_serializing_if = "Option::is_none")]
974    pub node_id: Option<String>,
975    #[serde(skip_serializing_if = "Option::is_none")]
976    pub avatar_url: Option<String>,
977    #[serde(skip_serializing_if = "Option::is_none")]
978    pub description: Option<String>,
979    #[serde(skip_serializing_if = "Option::is_none")]
980    pub url: Option<String>,
981    #[serde(skip_serializing_if = "Option::is_none")]
982    pub target_url: Option<String>,
983    #[serde(skip_serializing_if = "Option::is_none")]
984    pub created_at: Option<DateTime<Utc>>,
985    #[serde(skip_serializing_if = "Option::is_none")]
986    pub updated_at: Option<DateTime<Utc>>,
987    pub state: StatusState,
988    #[serde(skip_serializing_if = "Option::is_none")]
989    pub creator: Option<Author>,
990    #[serde(skip_serializing_if = "Option::is_none")]
991    pub context: Option<String>,
992}
993
994#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
995#[serde(rename_all = "snake_case")]
996#[non_exhaustive]
997pub enum StatusState {
998    Failure,
999    Pending,
1000    Success,
1001    Error,
1002}
1003
1004#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
1005#[non_exhaustive]
1006pub struct InstallationRepositories {
1007    pub total_count: i64,
1008    pub repositories: Vec<Repository>,
1009}
1010
1011#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
1012#[serde(rename_all = "snake_case")]
1013#[non_exhaustive]
1014pub struct Installation {
1015    pub id: InstallationId,
1016    pub account: Author,
1017    #[serde(skip_serializing_if = "Option::is_none")]
1018    pub access_tokens_url: Option<String>,
1019    #[serde(skip_serializing_if = "Option::is_none")]
1020    pub repositories_url: Option<String>,
1021    #[serde(skip_serializing_if = "Option::is_none")]
1022    pub html_url: Option<String>,
1023    #[serde(skip_serializing_if = "Option::is_none")]
1024    pub app_id: Option<AppId>,
1025    #[serde(skip_serializing_if = "Option::is_none")]
1026    pub target_id: Option<UserOrOrgId>,
1027    #[serde(skip_serializing_if = "Option::is_none")]
1028    pub target_type: Option<String>,
1029    pub permissions: InstallationPermissions,
1030    /// List of events in the installation.
1031    ///
1032    /// Note that for Webhook events, the list of events in the
1033    /// list is guaranteed to match variants from
1034    /// [WebhookEventType](webhook_events::WebhookEventType)
1035    pub events: Vec<String>,
1036    #[serde(skip_serializing_if = "Option::is_none")]
1037    pub single_file_name: Option<String>,
1038    #[serde(skip_serializing_if = "Option::is_none")]
1039    pub repository_selection: Option<String>,
1040    #[serde(
1041        skip_serializing_if = "Option::is_none",
1042        default,
1043        deserialize_with = "date_serde::deserialize_opt"
1044    )]
1045    pub created_at: Option<DateTime<Utc>>,
1046    #[serde(
1047        skip_serializing_if = "Option::is_none",
1048        default,
1049        deserialize_with = "date_serde::deserialize_opt"
1050    )]
1051    pub updated_at: Option<DateTime<Utc>>,
1052}
1053
1054#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
1055#[serde(rename_all = "snake_case")]
1056#[non_exhaustive]
1057pub struct InstallationPermissions {
1058    #[serde(skip_serializing_if = "Option::is_none")]
1059    pub actions: Option<String>,
1060    #[serde(skip_serializing_if = "Option::is_none")]
1061    pub checks: Option<String>,
1062    #[serde(skip_serializing_if = "Option::is_none")]
1063    pub contents: Option<String>,
1064    #[serde(skip_serializing_if = "Option::is_none")]
1065    pub issues: Option<String>,
1066    #[serde(skip_serializing_if = "Option::is_none")]
1067    pub metadata: Option<String>,
1068    #[serde(skip_serializing_if = "Option::is_none")]
1069    pub single_file: Option<String>,
1070    #[serde(skip_serializing_if = "Option::is_none")]
1071    pub statuses: Option<String>,
1072}
1073
1074#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
1075#[serde(rename_all = "snake_case")]
1076#[non_exhaustive]
1077pub struct InstallationToken {
1078    pub token: String,
1079    #[serde(skip_serializing_if = "Option::is_none")]
1080    pub expires_at: Option<String>,
1081    pub permissions: InstallationPermissions,
1082    #[serde(skip_serializing_if = "Option::is_none")]
1083    pub repositories: Option<Vec<Repository>>,
1084}
1085
1086#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
1087#[serde(rename_all = "snake_case")]
1088#[non_exhaustive]
1089pub struct PublicKey {
1090    pub key_id: String,
1091    pub key: String,
1092}
1093
1094#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
1095pub struct RateLimit {
1096    pub resources: Resources,
1097    pub rate: Rate,
1098}
1099
1100#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
1101pub struct Resources {
1102    pub core: Rate,
1103    pub search: Rate,
1104    pub graphql: Option<Rate>,
1105    pub integration_manifest: Option<Rate>,
1106    pub scim: Option<Rate>,
1107    pub source_import: Option<Rate>,
1108    pub code_scanning_upload: Option<Rate>,
1109    pub actions_runner_registration: Option<Rate>,
1110}
1111
1112#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
1113pub struct Rate {
1114    pub limit: usize,
1115    pub used: usize,
1116    pub remaining: usize,
1117    pub reset: u64,
1118}
1119
1120#[derive(Debug, Clone, Serialize, Deserialize)]
1121pub struct UserEmailInfo {
1122    pub email: String,
1123    pub primary: bool,
1124    pub verified: bool,
1125    pub visibility: Option<EmailVisibilityState>,
1126}
1127
1128#[derive(Debug, Clone, Serialize, Deserialize)]
1129pub struct VerifiedEmailInfo {
1130    pub email: String,
1131    pub verified: bool,
1132}
1133
1134#[derive(Debug, Clone, Serialize, Deserialize)]
1135pub struct SubKeyInfo {
1136    pub id: u64,
1137    pub primary_key_id: u64,
1138    pub key_id: String,
1139    pub public_key: String,
1140    pub emails: Vec<VerifiedEmailInfo>,
1141    pub subkeys: Option<Vec<SubKeyInfo>>,
1142    pub can_sign: bool,
1143    pub can_encrypt_comms: bool,
1144    pub can_encrypt_storage: bool,
1145    pub can_certify: bool,
1146    pub created_at: String,
1147    #[serde(skip_serializing_if = "Option::is_none")]
1148    pub expires_at: Option<String>,
1149    #[serde(skip_serializing_if = "Option::is_none")]
1150    pub raw_key: Option<String>,
1151    pub revoked: bool,
1152}
1153
1154#[derive(Debug, Clone, Serialize, Deserialize)]
1155pub struct GpgKey {
1156    pub id: u64,
1157    pub name: String,
1158    pub primary_key_id: u64,
1159    pub key_id: String,
1160    pub public_key: String,
1161    pub emails: Vec<VerifiedEmailInfo>,
1162    pub subkeys: Vec<SubKeyInfo>,
1163    pub can_sign: bool,
1164    pub can_encrypt_comms: bool,
1165    pub can_encrypt_storage: bool,
1166    pub can_certify: bool,
1167    pub created_at: DateTime<Utc>,
1168    #[serde(
1169        skip_serializing_if = "Option::is_none",
1170        default,
1171        deserialize_with = "date_serde::deserialize_opt"
1172    )]
1173    pub expires_at: Option<DateTime<Utc>>,
1174    pub revoked: bool,
1175    #[serde(skip_serializing_if = "Option::is_none")]
1176    pub raw_key: Option<String>,
1177}
1178
1179#[derive(Debug, Clone, Serialize, Deserialize)]
1180pub struct GitSshKey {
1181    pub key: String,
1182    pub id: u64,
1183    pub url: String,
1184    pub title: String,
1185    pub created_at: DateTime<Utc>,
1186    pub verified: bool,
1187    pub read_only: bool,
1188}
1189
1190#[derive(Debug, Clone, Serialize, Deserialize)]
1191pub struct SocialAccount {
1192    pub provider: String,
1193    pub url: String,
1194}
1195
1196#[derive(Debug, Clone, Serialize, Deserialize)]
1197pub struct SshSigningKey {
1198    pub key: String,
1199    pub id: u64,
1200    pub title: String,
1201    pub created_at: DateTime<Utc>,
1202}