gitdataai/lib/service/git/compare.rs

139 lines
4.0 KiB
Rust

use git::rpc::{proto as p, proto::commit_service_client::CommitServiceClient};
use session::Session;
use crate::{AppService, error::AppError, git::rpc_err};
#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
pub struct CompareResponse {
pub base_commit: CompareCommit,
pub head_commit: CompareCommit,
pub ahead_by: i32,
pub behind_by: i32,
pub total_commits: i32,
pub commits: Vec<CompareCommit>,
pub files_changed: u64,
pub insertions: u64,
pub deletions: u64,
}
#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
pub struct CompareCommit {
pub sha: String,
pub message: String,
pub author_name: Option<String>,
pub author_email: Option<String>,
}
impl AppService {
pub async fn git_compare(
&self,
ctx: &Session,
wk_name: &str,
repo_name: &str,
base: &str,
head: &str,
) -> Result<CompareResponse, AppError> {
let repo = self.git_require_member(ctx, wk_name, repo_name).await?;
let mut client = CommitServiceClient::new(self.git.clone());
fn oid(s: &str) -> p::ObjectId {
p::ObjectId {
value: s.to_string(),
}
}
let base_info = client
.commit_info(tonic::Request::new(p::CommitInfoRequest {
repo_id: repo.id.to_string(),
oid: Some(oid(base)),
}))
.await
.map_err(rpc_err)?
.into_inner();
let head_info = client
.commit_info(tonic::Request::new(p::CommitInfoRequest {
repo_id: repo.id.to_string(),
oid: Some(oid(head)),
}))
.await
.map_err(rpc_err)?
.into_inner();
let history = client
.commit_history(tonic::Request::new(p::CommitHistoryRequest {
repo_id: repo.id.to_string(),
limit: 250,
skip: 0,
sort: 0,
branch: Some(format!("{base}..{head}")),
}))
.await
.map_err(rpc_err)?
.into_inner();
let commits: Vec<CompareCommit> = history
.commits
.into_iter()
.map(|c| {
let author_name = c.author.as_ref().map(|a| a.name.clone());
let author_email = c.author.as_ref().map(|a| a.email.clone());
CompareCommit {
sha: c.oid.map(|o| o.value).unwrap_or_default(),
message: c.summary,
author_name,
author_email,
}
})
.collect();
let diff = crate::AppService::git_diff_stats(
self,
ctx,
wk_name,
repo_name,
base.to_string(),
head.to_string(),
None,
)
.await?;
let stats = diff.result.and_then(|r| r.stats);
let files_changed =
stats.as_ref().map(|s| s.files_changed).unwrap_or(0);
let insertions = stats.as_ref().map(|s| s.insertions).unwrap_or(0);
let deletions = stats.as_ref().map(|s| s.deletions).unwrap_or(0);
Ok(CompareResponse {
base_commit: cmt(base_info.commit),
head_commit: cmt(head_info.commit),
ahead_by: commits.len() as i32,
behind_by: 0,
total_commits: commits.len() as i32,
commits,
files_changed,
insertions,
deletions,
})
}
}
fn cmt(c: Option<p::CommitMeta>) -> CompareCommit {
c.map(|c| {
let author_name = c.author.as_ref().map(|a| a.name.clone());
let author_email = c.author.as_ref().map(|a| a.email.clone());
CompareCommit {
sha: c.oid.map(|o| o.value).unwrap_or_default(),
message: c.message,
author_name,
author_email,
}
})
.unwrap_or_else(|| CompareCommit {
sha: String::new(),
message: String::new(),
author_name: None,
author_email: None,
})
}