1use crate::http::{GitHubClient, UrlBuilder};
2use chrono::{DateTime, TimeZone};
3
4pub trait CommentFetcher: Sync {
6 fn comments_url(&self) -> &str;
7
8 fn fetch_comments<Tz: TimeZone>(
9 &self,
10 since: Option<DateTime<Tz>>,
11 ) -> impl std::future::Future<Output = Result<Vec<IssueComment>, Box<dyn std::error::Error>>> + Send
12 where
13 Tz::Offset: Send,
14 {
15 async {
16 let url = UrlBuilder::new(self.comments_url())
17 .param("since", since.map(|s| s.to_rfc3339()))
18 .build();
19
20 let client = GitHubClient::new()?;
21 let response = client
22 .get(&url)
23 .send()
24 .await?
25 .json::<Vec<IssueComment>>()
26 .await?;
27
28 Ok(response)
29 }
30 }
31}
32
33#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
34#[serde(deny_unknown_fields)]
35pub struct IssueDependenciesSummary {
36 pub blocked_by: i64,
37 pub blocking: i64,
38 pub total_blocked_by: i64,
39 pub total_blocking: i64,
40}
41
42#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
43#[serde(deny_unknown_fields)]
44pub struct IssueFieldValueSingleSelectOption {
45 pub id: i64,
46 pub name: String,
47 pub color: String,
48}
49
50#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
51#[serde(deny_unknown_fields)]
52pub struct IssueFieldValue {
53 pub issue_field_id: i64,
54 pub node_id: String,
55 pub data_type: String,
56 pub value: Option<serde_json::Value>,
57 pub single_select_option: Option<IssueFieldValueSingleSelectOption>,
58}
59
60#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
61#[serde(deny_unknown_fields)]
62pub struct IssueType {
63 pub id: i64,
64 pub node_id: String,
65 pub name: String,
66 pub description: Option<String>,
67 pub color: Option<String>,
68 pub created_at: String,
69 pub updated_at: String,
70 pub is_enabled: bool,
71}
72
73#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
74#[serde(deny_unknown_fields)]
75pub struct Commit {
76 pub id: String,
77 pub tree_id: String,
78 pub distinct: bool,
79 pub message: String,
80 pub timestamp: String,
81 pub url: String,
82 pub author: CommitUser,
83 pub committer: CommitUser,
84 pub added: Vec<String>,
85 pub removed: Vec<String>,
86 pub modified: Vec<String>,
87}
88
89#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
90#[serde(deny_unknown_fields)]
91pub struct CommitUser {
92 pub name: String,
93 pub email: String,
94 pub username: Option<String>,
95}
96
97#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
98#[serde(deny_unknown_fields)]
99pub struct Repository {
100 pub id: i64,
101 pub node_id: String,
102 pub name: String,
103 pub full_name: String,
104 pub private: bool,
105 pub owner: SimpleUser,
106 pub html_url: String,
107 pub description: Option<String>,
108 pub fork: bool,
109 pub url: String,
110 pub archive_url: String,
111 pub assignees_url: String,
112 pub blobs_url: String,
113 pub branches_url: String,
114 pub collaborators_url: String,
115 pub comments_url: String,
116 pub commits_url: String,
117 pub compare_url: String,
118 pub contents_url: String,
119 pub contributors_url: String,
120 pub deployments_url: String,
121 pub downloads_url: String,
122 pub events_url: String,
123 pub forks_url: String,
124 pub git_commits_url: String,
125 pub git_refs_url: String,
126 pub git_tags_url: String,
127 pub git_url: Option<String>,
128 pub issue_comment_url: String,
129 pub issue_events_url: String,
130 pub issues_url: String,
131 pub keys_url: String,
132 pub labels_url: String,
133 pub languages_url: String,
134 pub merges_url: String,
135 pub milestones_url: String,
136 pub notifications_url: String,
137 pub pulls_url: String,
138 pub releases_url: String,
139 pub ssh_url: Option<String>,
140 pub stargazers_url: String,
141 pub statuses_url: String,
142 pub subscribers_url: String,
143 pub subscription_url: String,
144 pub tags_url: String,
145 pub teams_url: String,
146 pub trees_url: String,
147 pub clone_url: Option<String>,
148 pub mirror_url: Option<String>,
149 pub hooks_url: String,
150 pub svn_url: Option<String>,
151 pub homepage: Option<String>,
152 pub language: Option<String>,
153 pub forks_count: Option<u64>,
154 pub stargazers_count: Option<u64>,
155 pub watchers_count: Option<u64>,
156 pub size: Option<u64>,
157 pub default_branch: Option<String>,
158 pub open_issues_count: Option<u64>,
159 pub is_template: Option<bool>,
160 pub topics: Option<Vec<String>>,
161 pub has_issues: Option<bool>,
162 pub has_projects: Option<bool>,
163 pub has_wiki: Option<bool>,
164 pub has_pages: Option<bool>,
165 pub has_downloads: Option<bool>,
166 pub has_discussions: Option<bool>,
167 pub archived: Option<bool>,
168 pub disabled: Option<bool>,
169 pub visibility: Option<String>,
170 pub pushed_at: Option<String>,
171 pub created_at: Option<String>,
172 pub updated_at: Option<String>,
173 pub permissions: Option<RepositoryPermissions>,
174 pub allow_rebase_merge: Option<bool>,
175 pub template_repository: Option<serde_json::Value>,
176 pub temp_clone_token: Option<String>,
177 pub allow_squash_merge: Option<bool>,
178 pub allow_auto_merge: Option<bool>,
179 pub delete_branch_on_merge: Option<bool>,
180 pub allow_merge_commit: Option<bool>,
181 pub allow_forking: Option<bool>,
182 pub web_commit_signoff_required: Option<bool>,
183 pub subscribers_count: Option<u64>,
184 pub network_count: Option<u64>,
185 pub license: Option<RepositoryLicense>,
186 pub forks: Option<u64>,
187 pub open_issues: Option<u64>,
188 pub watchers: Option<u64>,
189}
190
191#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
192#[serde(deny_unknown_fields)]
193pub struct RepositoryPermissions {
194 pub admin: bool,
195 pub maintain: Option<bool>,
196 pub push: bool,
197 pub pull: bool,
198 pub triage: Option<bool>,
199}
200
201#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
202#[serde(deny_unknown_fields)]
203pub struct RepositoryLicense {
204 pub key: String,
205 pub name: String,
206 pub spdx_id: Option<String>,
207 pub url: Option<String>,
208 pub node_id: String,
209}
210
211#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
212#[serde(deny_unknown_fields)]
213pub struct Milestone {
214 pub url: String,
215 pub html_url: String,
216 pub labels_url: String,
217 pub id: i64,
218 pub node_id: String,
219 pub number: u64,
220 pub title: String,
221 pub description: Option<String>,
222 pub creator: Option<SimpleUser>,
223 pub open_issues: u64,
224 pub closed_issues: u64,
225 pub state: String,
226 pub created_at: String,
227 pub updated_at: String,
228 pub due_on: Option<String>,
229 pub closed_at: Option<String>,
230}
231
232#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
233#[serde(deny_unknown_fields)]
234pub struct IssuePullRequest {
235 pub url: String,
236 pub html_url: String,
237 pub diff_url: String,
238 pub patch_url: String,
239 pub merged_at: Option<String>,
240}
241
242#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
243#[serde(deny_unknown_fields)]
244pub struct Integration {
245 pub id: i64,
246 pub slug: Option<String>,
247 pub node_id: String,
248 pub owner: SimpleUser,
249 pub name: String,
250 pub description: Option<String>,
251 pub external_url: String,
252 pub html_url: String,
253 pub created_at: String,
254 pub updated_at: String,
255 pub permissions: IntegrationPermissions,
256 pub events: Vec<String>,
257 pub installations_count: Option<u64>,
258 pub client_id: Option<String>,
259 pub client_secret: Option<String>,
260 pub webhook_secret: Option<String>,
261 pub pem: Option<String>,
262}
263
264#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
265#[serde(deny_unknown_fields)]
266pub struct IntegrationPermissions {
267 pub issues: Option<String>,
268 pub checks: Option<String>,
269 pub metadata: Option<String>,
270 pub contents: Option<String>,
271 pub deployments: Option<String>,
272}
273
274#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
275#[serde(deny_unknown_fields)]
276#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
277pub enum AuthorAssociation {
278 Owner,
279 Member,
280 Collaborator,
281 Contributor,
282 FirstTimeContributor,
283 FirstTimer,
284 Mannequin,
285 None,
286}
287
288#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
289#[serde(deny_unknown_fields)]
290pub struct ReactionRollup {
291 pub url: String,
292 pub total_count: u64,
293 #[serde(rename = "+1")]
294 pub plus_one: u64,
295 #[serde(rename = "-1")]
296 pub minus_one: u64,
297 pub laugh: u64,
298 pub hooray: u64,
299 pub confused: u64,
300 pub heart: u64,
301 pub rocket: u64,
302 pub eyes: u64,
303}
304
305#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
306#[serde(deny_unknown_fields)]
307pub struct SubIssuesSummary {
308 pub completed: u64,
309 pub percent_completed: u64,
310 pub total: u64,
311}
312
313#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
314#[serde(deny_unknown_fields)]
315pub struct Links {
316 #[serde(rename = "self")]
317 pub self_link: Link,
318 pub html: Link,
319 pub issue: Option<Link>,
320 pub comments: Option<Link>,
321 pub review_comments: Option<Link>,
322 pub review_comment: Option<Link>,
323 pub commits: Option<Link>,
324 pub statuses: Option<Link>,
325}
326
327#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
328#[serde(deny_unknown_fields)]
329pub struct Team {
330 pub id: i64,
331 pub node_id: String,
332 pub url: String,
333 pub html_url: String,
334 pub name: String,
335 pub slug: String,
336 pub description: Option<String>,
337 pub privacy: String,
338 pub permission: String,
339 pub members_url: String,
340 pub repositories_url: String,
341 pub parent: Option<Box<Team>>,
342}
343
344#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
345#[serde(deny_unknown_fields)]
346pub struct AutoMerge {
347 pub enabled_by: SimpleUser,
348 pub merge_method: String,
349 pub commit_title: Option<String>,
350 pub commit_message: Option<String>,
351}
352
353#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
354#[serde(deny_unknown_fields)]
355pub struct HeadCommit {
356 pub id: String,
357 pub tree_id: String,
358 pub message: String,
359 pub timestamp: String,
360 pub author: CommitUser,
361 pub committer: CommitUser,
362 pub added: Vec<String>,
363 pub removed: Vec<String>,
364 pub modified: Vec<String>,
365}
366
367#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
368#[serde(deny_unknown_fields)]
369pub struct SimpleUser {
370 pub name: Option<String>,
371 pub email: Option<String>,
372 pub login: String,
373 pub id: i64,
374 pub node_id: String,
375 pub avatar_url: String,
376 pub gravatar_id: Option<String>,
377 pub url: String,
378 pub html_url: String,
379 pub followers_url: String,
380 pub following_url: String,
381 pub gists_url: String,
382 pub starred_url: String,
383 pub subscriptions_url: String,
384 pub organizations_url: String,
385 pub repos_url: String,
386 pub events_url: String,
387 pub received_events_url: String,
388 pub r#type: String,
389 pub site_admin: bool,
390 pub starred_at: Option<String>,
391 pub user_view_type: Option<String>,
392}
393
394#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
395#[serde(deny_unknown_fields)]
396pub struct Actor {
397 pub id: i64,
398 pub login: String,
399 pub display_login: Option<String>,
400 pub gravatar_id: Option<String>,
401 pub url: String,
402 pub avatar_url: String,
403}
404
405#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
406#[serde(deny_unknown_fields)]
407pub struct RepoStub {
408 pub id: u64,
409 pub name: String,
410 pub url: String,
411}
412
413#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
414#[serde(deny_unknown_fields)]
415#[serde(tag = "type")]
416pub struct Event {
417 pub r#type: String,
418 pub id: String,
419 pub actor: Actor,
420 pub repo: RepoStub,
421 pub org: Option<Actor>,
422 pub payload: serde_json::Value,
423 pub public: bool,
424 pub created_at: Option<String>,
425}
426
427#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
428#[serde(deny_unknown_fields)]
429#[serde(untagged)]
430pub enum EventPayload {
431 IssueComment {
432 action: String,
433 issue: Box<Issue>,
434 comment: Box<IssueComment>,
435 },
436 Issue {
437 action: String,
438 issue: Box<Issue>,
439 },
440 Create {
441 description: String,
442 master_branch: String,
443 pusher_type: String,
444 r#ref: String,
445 ref_type: String,
446 },
447 PushEvent {
448 before: String,
449 commits: Vec<Commit>,
450 distinct_size: u64,
451 head: String,
452 push_id: u64,
453 r#ref: String,
454 repository_id: u64,
455 size: u64,
456 },
457 Delete {
458 pusher_type: String,
459 r#ref: String,
460 ref_type: String,
461 },
462 PullRequest {
463 action: String,
464 number: u64,
465 pull_request: Box<PullRequest>,
466 },
467 PullRequestReview {
468 action: String,
469 pull_request: Box<PullRequest>,
470 review: Box<PullRequestReview>,
471 },
472 PullRequestComment {
473 action: String,
474 pull_request: Box<PullRequest>,
475 comment: Box<PullRequestReviewComment>,
476 },
477 Forkee {
478 forkee: Box<Repository>,
479 },
480 Action {
481 action: String,
482 },
483}
484
485#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
486#[serde(deny_unknown_fields)]
487#[serde(untagged)]
488pub enum Label {
489 Detailed {
490 id: serde_json::Value,
491 node_id: Option<String>,
492 url: Option<String>,
493 name: String,
494 color: String,
495 default: bool,
496 description: Option<String>,
497 },
498 Simple(String),
499}
500
501impl Label {
502 pub fn name(&self) -> &str {
503 match self {
504 Self::Detailed { name, .. } => name.as_str(),
505 Self::Simple(name) => name.as_str(),
506 }
507 }
508}
509
510#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
511#[serde(deny_unknown_fields)]
512pub struct Link {
513 pub href: String,
514}
515
516#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
517#[serde(deny_unknown_fields)]
518pub struct Issue {
519 pub id: i64,
520 pub node_id: String,
521 pub url: String,
522 pub repository_url: String,
523 pub labels_url: String,
524 pub comments_url: String,
525 pub events_url: String,
526 pub html_url: String,
527 pub number: u64,
528 pub state: String,
529 pub state_reason: Option<String>,
530 pub title: String,
531 pub body: Option<String>,
532 pub user: Option<SimpleUser>,
533 pub labels: Vec<Label>,
534 pub assignee: Option<SimpleUser>,
535 pub assignees: Option<Vec<SimpleUser>>,
536 pub milestone: Option<Milestone>,
537 pub locked: bool,
538 pub active_lock_reason: Option<String>,
539 pub comments: u64,
540 pub pull_request: Option<IssuePullRequest>,
541 pub closed_at: Option<String>,
542 pub created_at: String,
543 pub updated_at: String,
544 pub draft: Option<bool>,
545 pub closed_by: Option<SimpleUser>,
546 pub body_html: Option<String>,
547 pub body_text: Option<String>,
548 pub timeline_url: Option<String>,
549 pub repository: Option<Repository>,
550 pub performed_via_github_app: Option<Integration>,
551 pub author_association: AuthorAssociation,
552 pub reactions: Option<ReactionRollup>,
553 pub sub_issues_summary: Option<SubIssuesSummary>,
554 pub issue_dependencies_summary: Option<IssueDependenciesSummary>,
555 pub issue_field_values: Option<Vec<IssueFieldValue>>,
556 pub parent_issue_url: Option<String>,
557 #[serde(rename = "type")]
558 pub r#type: Option<IssueType>,
559}
560
561impl Issue {
562 #[allow(clippy::too_many_arguments)]
563 pub async fn fetch_user_issues<Tz: TimeZone>(
564 filter: Option<String>,
565 state: Option<String>,
566 labels: Option<String>,
567 sort: Option<String>,
568 direction: Option<String>,
569 since: Option<DateTime<Tz>>,
570 per_page: Option<u64>,
571 page: Option<u64>,
572 ) -> Result<Vec<Issue>, Box<dyn std::error::Error>> {
573 let url = UrlBuilder::new("https://api.github.com/issues")
574 .param("filter", filter)
575 .param("state", state)
576 .param("labels", labels)
577 .param("sort", sort)
578 .param("direction", direction)
579 .param("since", since.map(|s| s.to_rfc3339()))
580 .param("per_page", per_page)
581 .param("page", page)
582 .build();
583
584 let client = GitHubClient::new()?;
585 let issues: Vec<Issue> = client.get(&url).send().await?.json().await?;
586
587 Ok(issues)
588 }
589}
590
591impl CommentFetcher for Issue {
592 fn comments_url(&self) -> &str {
593 &self.comments_url
594 }
595}
596
597#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
598#[serde(deny_unknown_fields)]
599pub struct IssueComment {
600 pub id: i64,
601 pub node_id: String,
602 pub url: String,
603 pub body: Option<String>,
604 pub body_text: Option<String>,
605 pub body_html: Option<String>,
606 pub html_url: String,
607 pub user: Option<SimpleUser>,
608 pub created_at: String,
609 pub updated_at: String,
610 pub issue_url: String,
611 pub author_association: AuthorAssociation,
612 pub performed_via_github_app: Option<Integration>,
613 pub reactions: Option<ReactionRollup>,
614}
615
616#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
617#[serde(deny_unknown_fields)]
618pub struct PullRequestReview {
619 pub _links: Option<Links>,
620 pub author_association: String,
621 pub body: Option<String>,
622 pub commit_id: String,
623 pub html_url: String,
624 pub id: i64,
625 pub node_id: String,
626 pub pull_request_url: String,
627 pub state: String,
628 pub submitted_at: String,
629 pub user: SimpleUser,
630}
631
632#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
633#[serde(deny_unknown_fields)]
634pub struct PullRequestReviewComment {
635 pub id: i64,
636 pub node_id: String,
637 pub url: String,
638 pub body: Option<String>,
639 pub body_text: Option<String>,
640 pub body_html: Option<String>,
641 pub html_url: String,
642 pub user: Option<SimpleUser>,
643 pub created_at: String,
644 pub updated_at: String,
645 pub issue_url: Option<String>,
646 pub author_association: AuthorAssociation,
647 pub performed_via_github_app: Option<Integration>,
648 pub reactions: Option<ReactionRollup>,
649 pub _links: Option<Links>,
650 pub commit_id: Option<String>,
651 pub diff_hunk: Option<String>,
652 pub line: Option<u64>,
653 pub original_commit_id: Option<String>,
654 pub original_line: Option<u64>,
655 pub original_position: Option<u64>,
656 pub original_start_line: Option<u64>,
657 pub path: Option<String>,
658 pub position: Option<u64>,
659 pub pull_request_review_id: Option<u64>,
660 pub pull_request_url: Option<String>,
661 pub side: Option<String>,
662 pub start_line: Option<u64>,
663 pub start_side: Option<String>,
664 pub subject_type: Option<String>,
665 pub in_reply_to_id: Option<serde_json::Value>,
666}
667
668#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
669#[serde(deny_unknown_fields)]
670pub struct PullRequestHead {
671 pub label: String,
672 #[serde(rename = "ref")]
673 pub r#ref: String,
674 pub sha: String,
675 pub user: SimpleUser,
676 pub repo: Repository,
677}
678
679#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
680#[serde(deny_unknown_fields)]
681pub struct PullRequest {
682 pub url: String,
683 pub id: i64,
684 pub node_id: String,
685 pub html_url: String,
686 pub diff_url: String,
687 pub patch_url: String,
688 pub issue_url: String,
689 pub commits_url: String,
690 pub review_comments_url: String,
691 pub review_comment_url: String,
692 pub comments_url: String,
693 pub statuses_url: String,
694 pub number: u64,
695 pub state: String,
696 pub locked: bool,
697 pub title: String,
698 pub user: SimpleUser,
699 pub body: Option<String>,
700 pub labels: Vec<Label>,
701 pub milestone: Option<Milestone>,
702 pub active_lock_reason: Option<String>,
703 pub created_at: String,
704 pub updated_at: String,
705 pub closed_at: Option<String>,
706 pub merged_at: Option<String>,
707 pub merge_commit_sha: Option<String>,
708 pub assignee: Option<SimpleUser>,
709 pub assignees: Option<Vec<SimpleUser>>,
710 pub requested_reviewers: Option<Vec<SimpleUser>>,
711 pub requested_teams: Option<Vec<Team>>,
712 pub head: PullRequestHead,
713 pub base: PullRequestHead,
714 pub _links: Links,
715 pub author_association: AuthorAssociation,
716 pub auto_merge: Option<AutoMerge>,
717 pub draft: Option<bool>,
718 pub merged: Option<bool>,
719 pub mergeable: Option<bool>,
720 pub rebaseable: Option<bool>,
721 pub mergeable_state: Option<String>,
722 pub merged_by: Option<SimpleUser>,
723 pub comments: Option<u64>,
724 pub review_comments: Option<u64>,
725 pub maintainer_can_modify: Option<bool>,
726 pub commits: Option<u64>,
727 pub additions: Option<u64>,
728 pub deletions: Option<u64>,
729 pub changed_files: Option<u64>,
730}
731
732impl CommentFetcher for PullRequest {
733 fn comments_url(&self) -> &str {
734 &self.comments_url
735 }
736}
737
738#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
739#[serde(deny_unknown_fields)]
740pub struct NotificationSubject {
741 pub title: String,
742 pub url: String,
743 pub latest_comment_url: Option<String>,
744 pub r#type: String,
745}
746
747#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
748#[serde(deny_unknown_fields)]
749pub struct Notification {
750 pub id: String,
751 pub unread: bool,
752 pub reason: String,
753 pub updated_at: String,
754 pub last_read_at: Option<String>,
755 pub subject: NotificationSubject,
756 pub repository: Repository,
757 pub url: String,
758 pub subscription_url: String,
759}
760
761impl Notification {
762 pub async fn fetch_all<Tz: chrono::TimeZone>(
763 all: bool,
764 participating: bool,
765 since: Option<DateTime<Tz>>,
766 before: Option<DateTime<Tz>>,
767 ) -> Result<Vec<Notification>, Box<dyn std::error::Error>> {
768 let url = UrlBuilder::new("https://api.github.com/notifications")
769 .param("all", if all { Some("true") } else { None })
770 .param(
771 "participating",
772 if participating { Some("true") } else { None },
773 )
774 .param("since", since.map(|s| s.to_rfc3339()))
775 .param("before", before.map(|s| s.to_rfc3339()))
776 .build();
777
778 let client = GitHubClient::new()?;
779 let mut notifications: Vec<Notification> = client.get(&url).send().await?.json().await?;
780
781 notifications.sort_by_key(|n| n.updated_at.clone());
782 Ok(notifications)
783 }
784
785 pub async fn fetch_pull_request(&self) -> Result<PullRequest, Box<dyn std::error::Error>> {
786 if self.subject.r#type != "PullRequest" {
787 return Err("not a pull request".into());
788 }
789
790 let client = GitHubClient::new()?;
791 let pull_request: PullRequest = client.get(&self.subject.url).send().await?.json().await?;
792
793 Ok(pull_request)
794 }
795
796 pub async fn fetch_issue(&self) -> Result<Issue, Box<dyn std::error::Error>> {
797 if self.subject.r#type != "Issue" {
798 return Err("not an issue".into());
799 }
800
801 let client = GitHubClient::new()?;
802 let issue: Issue = client.get(&self.subject.url).send().await?.json().await?;
803
804 Ok(issue)
805 }
806
807 pub async fn mark_as_read(&self) -> Result<(), Box<dyn std::error::Error>> {
808 let client = GitHubClient::new()?;
809 let url = format!("https://api.github.com/notifications/threads/{}", self.id);
810
811 client
812 .request(reqwest::Method::PATCH, &url)
813 .send()
814 .await?
815 .error_for_status()?;
816
817 Ok(())
818 }
819}
820
821#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
822#[serde(deny_unknown_fields)]
823pub struct ReferencedWorkflow {
824 path: String,
825 sha: String,
826 r#ref: Option<String>,
827}
828
829#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
830#[serde(deny_unknown_fields)]
831pub struct Action {
832 pub id: i64,
833 pub name: Option<String>,
834 pub node_id: String,
835 pub check_suite_id: Option<u64>,
836 pub check_suite_node_id: Option<String>,
837 pub head_branch: Option<String>,
838 pub head_sha: String,
839 pub path: String,
840 pub run_number: u64,
841 pub run_attempt: Option<u64>,
842 pub referenced_workflows: Vec<ReferencedWorkflow>,
843 pub event: String,
844 pub status: Option<String>,
845 pub conclusion: Option<String>,
846 pub workflow_id: u64,
847 pub url: String,
848 pub html_url: String,
849 pub pull_requests: Vec<IssuePullRequest>,
850 pub created_at: String,
851 pub updated_at: String,
852 pub actor: Option<SimpleUser>,
853 pub triggering_actor: Option<SimpleUser>,
854 pub run_started_at: Option<String>,
855 pub jobs_url: String,
856 pub logs_url: String,
857 pub check_suite_url: String,
858 pub artifacts_url: String,
859 pub cancel_url: String,
860 pub rerun_url: String,
861 pub previous_attempt_url: Option<String>,
862 pub workflow_url: String,
863 pub head_commit: HeadCommit,
864 pub repository: Repository,
865 pub head_repository: Repository,
866 pub head_repository_id: Option<u64>,
867 pub display_title: String,
868}
869
870#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
871#[serde(deny_unknown_fields)]
872pub struct Runs {
873 pub total_count: u64,
874 pub workflow_runs: Vec<Action>,
875}
876
877#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
878#[serde(deny_unknown_fields)]
879pub struct RunJobs {
880 pub total_count: u64,
881 pub jobs: Vec<Job>,
882}
883
884impl Action {
885 #[allow(clippy::too_many_arguments)]
886 pub async fn fetch_all(
887 owner: String,
888 repo: String,
889 actor: Option<String>,
890 workflow_run_branch: Option<String>,
891 event: Option<String>,
892 workflow_run_status: Option<String>,
893 per_page: Option<u64>,
894 page: Option<u64>,
895 ) -> Result<Vec<Action>, Box<dyn std::error::Error>> {
896 let url = UrlBuilder::new(format!(
897 "https://api.github.com/repos/{owner}/{repo}/actions/runs"
898 ))
899 .param("actor", actor)
900 .param("workflow_run_branch", workflow_run_branch)
901 .param("event", event)
902 .param("workflow_run_status", workflow_run_status)
903 .param("per_page", per_page)
904 .param("page", page)
905 .build();
906
907 let client = GitHubClient::new()?;
908 let mut runs: Runs = client.get(&url).send().await?.json().await?;
909
910 runs.workflow_runs.sort_by_key(|n| n.updated_at.clone());
911 Ok(runs.workflow_runs)
912 }
913
914 pub async fn fetch_jobs(&self) -> Result<Vec<Job>, Box<dyn std::error::Error>> {
915 let client = GitHubClient::new()?;
916 let run_jobs: RunJobs = client.get(&self.jobs_url).send().await?.json().await?;
917
918 Ok(run_jobs.jobs)
919 }
920}
921
922#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
923#[serde(deny_unknown_fields)]
924pub struct JobStep {
925 pub status: String,
926 pub conclusion: Option<String>,
927 pub name: String,
928 pub number: u64,
929 pub started_at: Option<String>,
930 pub completed_at: Option<String>,
931}
932
933#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
934#[serde(deny_unknown_fields)]
935pub struct Job {
936 pub id: i64,
937 pub run_id: u64,
938 pub run_url: String,
939 pub run_attempt: Option<u64>,
940 pub node_id: String,
941 pub head_sha: String,
942 pub url: String,
943 pub html_url: Option<String>,
944 pub status: String,
945 pub conclusion: Option<String>,
946 pub created_at: String,
947 pub started_at: String,
948 pub completed_at: Option<String>,
949 pub name: String,
950 pub steps: Vec<JobStep>,
951 pub check_run_url: String,
952 pub labels: Vec<String>,
953 pub runner_id: Option<u64>,
954 pub runner_name: Option<String>,
955 pub runner_group_id: Option<u64>,
956 pub runner_group_name: Option<String>,
957 pub workflow_name: Option<String>,
958 pub head_branch: Option<String>,
959}