1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use futures::future::join_all;
use serde::Deserialize;
use std::error::Error;
use crate::{api, Credentials};
#[derive(Deserialize, Debug, Clone)]
pub struct SearchItem {
url: String,
title: String,
}
#[derive(Deserialize, Debug, Clone)]
pub struct PullRequestRef {
label: String,
#[serde(rename = "ref")]
gitref: String,
sha: String,
}
#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum PullRequestStatus {
#[serde(rename = "open")]
Open,
#[serde(rename = "closed")]
Closed,
}
#[derive(Deserialize, Debug, Clone)]
pub struct PullRequest {
id: usize,
number: usize,
head: PullRequestRef,
base: PullRequestRef,
title: String,
url: String,
body: String,
state: PullRequestStatus,
}
impl PullRequest {
pub fn head(&self) -> &str {
&self.head.gitref
}
pub fn base(&self) -> &str {
&self.base.gitref
}
pub fn url(&self) -> &str {
&self.url
}
pub fn number(&self) -> usize {
self.number
}
pub fn title(&self) -> String {
match &self.state {
PullRequestStatus::Open => self.title.to_owned(),
PullRequestStatus::Closed => format!("~~{}~~", &self.title.trim()),
}
}
pub fn state(&self) -> &PullRequestStatus {
&self.state
}
pub fn body(&self) -> &str {
&self.body
}
pub fn note(&self) -> &str {
match &self.state {
PullRequestStatus::Open => "N/A",
PullRequestStatus::Closed => "Merged",
}
}
}
#[derive(Deserialize, Debug)]
struct SearchResponse {
items: Vec<SearchItem>,
}
pub async fn fetch_pull_requests_matching(
pattern: &str,
credentials: &Credentials,
) -> Result<Vec<PullRequest>, Box<dyn Error>> {
let client = reqwest::Client::new();
let request = api::base_request(
&client,
&credentials,
"https://api.github.com/search/issues",
)
.query(&[("q", format!("{} in:title", pattern))]);
let items = request.send().await?.json::<SearchResponse>().await?.items;
let item_futures = items.into_iter().map(|item| {
api::base_request(&client, &credentials, &item.url.replace("issues", "pulls")).send()
});
let items = join_all(item_futures)
.await
.into_iter()
.map(|item| item.unwrap());
let responses: Vec<_> = join_all(items.map(|item| item.json::<PullRequest>()))
.await
.into_iter()
.map(|item| item.unwrap())
.collect();
Ok(responses)
}