-
-
Save dtolnay/aa9c34993dc051a4f344d1b10e4487e8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// [dependencies] | |
// anyhow = "1.0" | |
// reqwest = { version = "=0.10.0-alpha.2", features = ["blocking", "json"] } | |
// serde = { version = "1.0", features = ["derive"] } | |
// serde_json = "1.0" | |
use anyhow::Result; | |
use reqwest::blocking::Client; | |
use reqwest::header::AUTHORIZATION; | |
use serde::{Deserialize, Serialize}; | |
use std::collections::{BTreeMap as Map, BTreeSet as Set}; | |
#[derive(Deserialize, Debug)] | |
struct Response<T> { | |
data: Data<T>, | |
} | |
#[derive(Deserialize, Debug)] | |
struct Data<T> { | |
repository: T, | |
} | |
#[derive(Deserialize, Debug)] | |
#[serde(rename_all = "camelCase")] | |
struct Repository { | |
pull_requests: PullRequests, | |
} | |
#[derive(Deserialize, Debug)] | |
#[serde(rename_all = "camelCase")] | |
struct PullRequests { | |
page_info: PageInfo, | |
nodes: Vec<PullRequest>, | |
} | |
#[derive(Deserialize, Debug)] | |
struct PullRequest { | |
number: usize, | |
url: String, | |
files: Files, | |
} | |
#[derive(Deserialize, Debug)] | |
#[serde(rename_all = "camelCase")] | |
struct Files { | |
page_info: PageInfo, | |
nodes: Vec<File>, | |
} | |
#[derive(Deserialize, Debug)] | |
struct File { | |
path: String, | |
} | |
#[derive(Deserialize, Debug)] | |
#[serde(rename_all = "camelCase")] | |
struct PageInfo { | |
has_next_page: bool, | |
end_cursor: String, | |
} | |
const QUERY: &str = r#" | |
{ | |
repository(owner: "rust-lang", name: "rust") { | |
$content | |
} | |
} | |
"#; | |
const PAGE: &str = r#" | |
pullRequests(after: $cursor, first: 100, states: OPEN) { | |
pageInfo { | |
hasNextPage | |
endCursor | |
} | |
nodes { | |
number | |
url | |
files(first: 100) { | |
pageInfo { | |
hasNextPage | |
endCursor | |
} | |
nodes { | |
path | |
} | |
} | |
} | |
} | |
"#; | |
const PR: &str = r#" | |
pr$number: pullRequest(number: $number) { | |
number | |
url | |
files(after: $cursor, first: 100) { | |
pageInfo { | |
hasNextPage | |
endCursor | |
} | |
nodes { | |
path | |
} | |
} | |
} | |
"#; | |
#[derive(Serialize)] | |
struct Request { | |
query: String, | |
} | |
const ENDPOINT: &str = "https://api.github.com/graphql"; | |
const APITOKEN: &str = concat!("bearer ", include_str!("githubtoken")); | |
fn main() -> Result<()> { | |
let client = Client::new(); | |
let mut changed_files = Set::new(); | |
let mut cursor = "null".to_owned(); | |
let mut next_pages = Vec::new(); | |
loop { | |
let request = Request { | |
query: QUERY.replace("$content", PAGE).replace("$cursor", &cursor), | |
}; | |
let prs = client | |
.post(ENDPOINT) | |
.header(AUTHORIZATION, APITOKEN) | |
.json(&request) | |
.send()? | |
.json::<Response<Repository>>()? | |
.data | |
.repository | |
.pull_requests; | |
for pr in prs.nodes { | |
if pr.files.page_info.has_next_page { | |
next_pages.push((pr.number, pr.files.page_info.end_cursor)); | |
} | |
for file in pr.files.nodes { | |
changed_files.insert(file.path); | |
} | |
} | |
if !prs.page_info.has_next_page { | |
break; | |
} | |
cursor = serde_json::to_string(&prs.page_info.end_cursor).unwrap(); | |
} | |
while !next_pages.is_empty() { | |
let request = Request { | |
query: QUERY.replace( | |
"$content", | |
&next_pages | |
.into_iter() | |
.map(|(number, cursor)| { | |
PR.replace("$number", &number.to_string()) | |
.replace("$cursor", &serde_json::to_string(&cursor).unwrap()) | |
}) | |
.collect::<String>(), | |
), | |
}; | |
let prs = client | |
.post(ENDPOINT) | |
.header(AUTHORIZATION, APITOKEN) | |
.json(&request) | |
.send()? | |
.json::<Response<Map<String, PullRequest>>>()? | |
.data | |
.repository; | |
next_pages = Vec::new(); | |
for (_, pr) in prs { | |
if pr.files.page_info.has_next_page { | |
next_pages.push((pr.number, pr.files.page_info.end_cursor)); | |
} | |
for file in pr.files.nodes { | |
changed_files.insert(file.path); | |
} | |
} | |
} | |
for path in changed_files { | |
println!("{}", path); | |
} | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment