use anyhow::{anyhow, Result};
use futures::stream::{FuturesUnordered, StreamExt};
use graphql_client::*;
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "src/client/schema.graphql",
query_path = "src/client/blame.graphql",
response_derives = "Debug"
)]
pub struct Blame;
pub async fn blame(
github_api_token: &str,
repo_name: &str,
repo_owner: &str,
files: &[String],
login: &str,
) -> bool {
let blame_checks: FuturesUnordered<_> = files
.iter()
.map(|file| async move {
eprint!(".");
let response_data: blame::ResponseData =
match girhub_blame(github_api_token, repo_name, repo_owner, file).await {
Ok(data) => data,
Err(error) => panic!("Can't get the authors for {file}: {}", error),
};
is_file_author(&response_data, login)
})
.collect();
blame_checks
.collect::<Vec<bool>>()
.await
.into_iter()
.any(|b| b)
}
fn is_file_author(response_data: &blame::ResponseData, login: &str) -> bool {
let v = match response_data {
blame::ResponseData {
repository:
Some(blame::BlameRepository {
id: _,
name: _,
default_branch_ref:
Some(blame::BlameRepositoryDefaultBranchRef {
target:
Some(blame::BlameRepositoryDefaultBranchRefTarget::Commit(
blame::BlameRepositoryDefaultBranchRefTargetOnCommit {
blame:
blame::BlameRepositoryDefaultBranchRefTargetOnCommitBlame {
ranges,
},
},
)),
}),
}),
} => Some(ranges),
_ => None,
};
let authors = match v {
Some(ranges) => ranges.iter().flat_map(|range|
match &range.commit.authors.nodes {
Some(nodes) => nodes.iter().flat_map(|node|
match node {
Some(blame::BlameRepositoryDefaultBranchRefTargetOnCommitBlameRangesCommitAuthorsNodes{
user: Some(blame::BlameRepositoryDefaultBranchRefTargetOnCommitBlameRangesCommitAuthorsNodesUser {login})
}) => Some(login),
_ => None
}
).collect(),
_ => vec!()
}
).collect(),
_ => vec!(),
};
authors.contains(&&login.to_string())
}
async fn girhub_blame(
github_api_token: &str,
repo_name: &str,
repo_owner: &str,
path: &str,
) -> Result<blame::ResponseData> {
let q = Blame::build_query(blame::Variables {
repo_name: repo_name.to_string(),
repo_owner: repo_owner.to_string(),
path: path.to_string(),
});
let res = super::call(github_api_token, &q).await?;
let response_body: Response<blame::ResponseData> = res.json().await?;
if let Some(errors) = response_body.errors {
Err(anyhow!(
"Errors fetching the authors of {} {}",
path,
errors
.iter()
.map(|error| format!("{:?}", error))
.collect::<String>()
))
} else {
match response_body.data {
Some(data) => Ok(data),
None => Err(anyhow!(
"Missing response data fetching the authors of {path}"
)),
}
}
}