use anyhow::Context; use futures::{ compat::{Future01CompatExt, Stream01CompatExt}, stream::{FuturesUnordered, StreamExt}, }; use once_cell::sync::OnceCell; use reqwest::header::{AUTHORIZATION, USER_AGENT}; use reqwest::{ r#async::{Client, RequestBuilder, Response}, StatusCode, }; use std::fmt; #[derive(Debug, PartialEq, Eq, serde::Deserialize)] pub struct User { pub login: String, } impl GithubClient { async fn _send_req(&self, req: RequestBuilder) -> Result<(Response, String), reqwest::Error> { log::debug!("_send_req with {:?}", req); let req = req.build()?; let req_dbg = format!("{:?}", req); let resp = self.client.execute(req).compat().await?; resp.error_for_status_ref()?; Ok((resp, req_dbg)) } async fn send_req(&self, req: RequestBuilder) -> anyhow::Result> { let (resp, req_dbg) = self._send_req(req).await?; let mut body = Vec::new(); let mut stream = resp.into_body().compat(); while let Some(chunk) = stream.next().await { let chunk = chunk .context("reading stream failed") .map_err(anyhow::Error::from) .context(req_dbg.clone())?; body.extend_from_slice(&chunk); } Ok(body) } async fn json(&self, req: RequestBuilder) -> anyhow::Result where T: serde::de::DeserializeOwned, { let (mut resp, req_dbg) = self._send_req(req).await?; Ok(resp.json().compat().await.context(req_dbg)?) } } impl User { pub async fn current(client: &GithubClient) -> anyhow::Result { client.json(client.get("https://api.github.com/user")).await } pub async fn is_team_member<'a>(&'a self, client: &'a GithubClient) -> anyhow::Result { let url = format!("{}/teams.json", rust_team_data::v1::BASE_URL); let permission: rust_team_data::v1::Teams = client .json(client.raw().get(&url)) .await .context("could not get team data")?; let map = permission.teams; let is_triager = map .get("wg-triage") .map_or(false, |w| w.members.iter().any(|g| g.github == self.login)); Ok(map["all"].members.iter().any(|g| g.github == self.login) || is_triager) } } pub async fn get_team( client: &GithubClient, team: &str, ) -> anyhow::Result> { let url = format!("{}/teams.json", rust_team_data::v1::BASE_URL); let permission: rust_team_data::v1::Teams = client .json(client.raw().get(&url)) .await .context("could not get team data")?; let mut map = permission.teams; Ok(map.swap_remove(team)) } #[derive(PartialEq, Eq, Debug, Clone, serde::Deserialize)] pub struct Label { pub name: String, } impl Label { async fn exists<'a>(&'a self, repo_api_prefix: &'a str, client: &'a GithubClient) -> bool { #[allow(clippy::redundant_pattern_matching)] let url = format!("{}/labels/{}", repo_api_prefix, self.name); match client.send_req(client.get(&url)).await { Ok(_) => true, // XXX: Error handling if the request failed for reasons beyond 'label didn't exist' Err(_) => false, } } } #[derive(Debug, serde::Deserialize)] pub struct PullRequestDetails { // none for now } #[derive(Debug, serde::Deserialize)] pub struct Issue { pub number: u64, pub body: String, title: String, html_url: String, user: User, labels: Vec