Lines
83.75 %
Functions
24.44 %
use crate::git_signatures::{check_signatures, get_signatures};
use git2::Repository;
use sequoia_openpgp::Cert;
use serde::Serialize;
use std::collections::{btree_map::Entry, BTreeMap, BTreeSet};
type Result<T> = anyhow::Result<T>;
#[derive(Serialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct AuthorData {
names: BTreeSet<String>,
number_of_commits: usize,
}
type Email = String;
#[derive(Serialize, PartialEq)]
pub struct Commit {
time: i64,
sha1: String,
email: Option<Email>,
name: Option<String>,
pub struct RepositoryAnalysis {
#[serde(skip_serializing_if = "BTreeSet::is_empty")]
signatures: BTreeSet<SignatureAnalysis>,
missing_keys: BTreeSet<String>,
default_branch: Option<String>,
first_commit: Option<Commit>,
latest_commit: Option<Commit>,
authors: BTreeMap<Email, AuthorData>,
pub struct SignatureAnalysis {
handle: String,
email: BTreeSet<String>,
fn add_author(commit: &git2::Commit, authors: &mut BTreeMap<Email, AuthorData>) {
let author = commit.author();
if let (Some(name), Some(email)) = (author.name(), author.email()) {
let email = email.to_string();
let name = name.to_string();
match authors.entry(email) {
Entry::Occupied(mut e) => {
let e = e.get_mut();
e.number_of_commits += 1;
e.names.insert(name);
Entry::Vacant(e) => {
let mut names = BTreeSet::new();
names.insert(name);
e.insert(AuthorData {
number_of_commits: 1,
names,
});
fn get_commit(commit: &git2::Commit) -> Commit {
Commit {
time: commit.time().seconds(),
sha1: commit.id().to_string(),
email: author.email().map(|e| e.to_string()),
name: author.name().map(|e| e.to_string()),
pub fn analyze_repository(certs: &[Cert], repo: &Repository) -> Result<RepositoryAnalysis> {
let signatures = get_signatures(repo)?;
let r = check_signatures(certs, signatures)?;
let mut signature_analyses = BTreeSet::new();
let mut authors = BTreeMap::new();
for key in r.found_keys {
let mut analysis = SignatureAnalysis {
handle: format!("{}", key.0),
email: BTreeSet::new(),
};
for id in key.1.userids() {
if let Ok(Some(email)) = id.email_normalized() {
analysis.email.insert(email);
signature_analyses.insert(analysis);
// since the checkout is untouched, head is the default branch
let mut default_branch = None;
let mut first_commit = None;
let mut latest_commit = None;
let mut number_of_commits = 0;
if let Ok(head) = repo.head() {
default_branch = head
.name()
.and_then(|n| n.strip_prefix("refs/heads/"))
.map(|n| n.to_string());
if let Ok(mut commit) = head.peel_to_commit() {
number_of_commits = 1;
latest_commit = Some(get_commit(&commit));
add_author(&commit, &mut authors);
while let Some(p) = commit.parents().next() {
number_of_commits += 1;
commit = p;
first_commit = Some(get_commit(&commit));
//repo.head().
Ok(RepositoryAnalysis {
signatures: signature_analyses,
missing_keys: r.missing_keys.iter().map(|k| format!("{}", k)).collect(),
default_branch,
first_commit,
latest_commit,
number_of_commits,
authors,
})