use std::collections::HashSet;
use derive_builder::Builder;
use futures::TryFutureExt;
use reqwest::{header, Client, Response, Result};
use serde::Deserialize;
use tracing::{debug, info, instrument, warn, Level};
use url::Url;
#[derive(Debug, Clone, Builder)]
pub struct GitHub {
#[builder(
setter(name = "token", into),
field(
ty = "String",
build = r#"
let mut headers = header::HeaderMap::new();
headers.insert("User-Agent", header::HeaderValue::from_static("gfas"));
headers.insert("Authorization", format!("token {}", self.client).parse().unwrap());
Client::builder().default_headers(headers).build().unwrap()
"#
)
)]
client: Client,
#[builder]
endpoint: Url
}
impl GitHub {
pub fn builder() -> GitHubBuilder {
GitHubBuilder::create_empty()
}
#[instrument(skip(self), ret(level = Level::TRACE), err)]
pub async fn explore(&self, user: &str, role: &str) -> Result<HashSet<String>> {
let mut res = HashSet::new();
let url = self.endpoint.join(&format!("users/{user}/{role}")).unwrap();
#[derive(Deserialize)]
struct User {
login: String
}
const PER_PAGE: usize = 100;
for page in 1.. {
debug!("page {page}");
let users: Vec<_> = self
.client
.get(url.clone())
.query(&[("page", page), ("per_page", PER_PAGE)])
.send()
.and_then(|r| r.json::<Vec<User>>())
.await?
.into_iter()
.map(|u| u.login)
.collect();
let len = users.len();
res.extend(users);
info!("{}(+{len})", res.len());
if len < PER_PAGE {
break;
}
}
Ok(res)
}
#[instrument(skip(self), ret(level = Level::TRACE), err)]
pub async fn follow(&self, user: &str) -> Result<Response> {
warn!("");
let url = self.endpoint.join(&format!("/user/following/{user}")).unwrap();
self.client.put(url).send().await
}
#[instrument(skip(self), ret(level = Level::TRACE), err)]
pub async fn unfollow(&self, user: &str) -> Result<Response> {
warn!("");
let url = self.endpoint.join(&format!("/user/following/{user}")).unwrap();
self.client.delete(url).send().await
}
}