use crate::cli::output::Output;
use crate::core::manifest::Manifest;
use crate::core::repo::RepoInfo;
use crate::git::{get_current_branch, open_repo, path_exists};
use crate::platform::get_platform_adapter;
use std::path::Path;
pub async fn run_pr_diff(
workspace_root: &Path,
manifest: &Manifest,
stat_only: bool,
) -> anyhow::Result<()> {
if !stat_only {
Output::header("PR Diff");
println!();
}
let repos: Vec<RepoInfo> = manifest
.repos
.iter()
.filter_map(|(name, config)| {
RepoInfo::from_config(
name,
config,
workspace_root,
&manifest.settings,
manifest.remotes.as_ref(),
)
})
.collect();
let mut total_files = 0;
let mut total_additions = 0;
let mut total_deletions = 0;
for repo in &repos {
if !path_exists(&repo.absolute_path) {
continue;
}
let git_repo = match open_repo(&repo.absolute_path) {
Ok(r) => r,
Err(_) => continue,
};
let branch = match get_current_branch(&git_repo) {
Ok(b) => b,
Err(_) => continue,
};
if branch == repo.target_branch() {
continue;
}
let platform = get_platform_adapter(repo.platform_type, repo.platform_base_url.as_deref());
match platform
.find_pr_by_branch(&repo.owner, &repo.repo, &branch)
.await
{
Ok(Some(pr)) => {
match platform
.get_pull_request_diff(&repo.owner, &repo.repo, pr.number)
.await
{
Ok(diff) => {
if stat_only {
let stats = parse_diff_stats(&diff);
println!("{} #{}:", repo.name, pr.number);
println!(
" {} file(s), +{}, -{}",
stats.files, stats.additions, stats.deletions
);
total_files += stats.files;
total_additions += stats.additions;
total_deletions += stats.deletions;
} else {
println!("=== {} #{} ===", repo.name, pr.number);
println!("{}", diff);
println!();
}
}
Err(e) => {
Output::error(&format!("{}: {}", repo.name, e));
}
}
}
Ok(None) => {
Output::info(&format!("{}: no open PR for this branch", repo.name));
}
Err(e) => {
Output::error(&format!("{}: {}", repo.name, e));
}
}
}
if stat_only && (total_files > 0 || total_additions > 0 || total_deletions > 0) {
println!();
println!("Total:");
println!(
" {} file(s), +{}, -{}",
total_files, total_additions, total_deletions
);
}
Ok(())
}
struct DiffStats {
files: usize,
additions: usize,
deletions: usize,
}
fn parse_diff_stats(diff: &str) -> DiffStats {
let mut files = 0;
let mut additions = 0;
let mut deletions = 0;
for line in diff.lines() {
if line.starts_with("diff --git") || line.starts_with("Index:") {
files += 1;
} else if line.starts_with('+') && !line.starts_with("+++") {
additions += 1;
} else if line.starts_with('-') && !line.starts_with("---") {
deletions += 1;
}
}
DiffStats {
files,
additions,
deletions,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_diff_stats() {
let diff = r#"diff --git a/file1.rs b/file1.rs
--- a/file1.rs
+++ b/file1.rs
@@ -1,3 +1,4 @@
line1
-old line
+new line
+added line
diff --git a/file2.rs b/file2.rs
--- a/file2.rs
+++ b/file2.rs
@@ -1,2 +1,2 @@
-removed
+added
"#;
let stats = parse_diff_stats(diff);
assert_eq!(stats.files, 2);
assert_eq!(stats.additions, 3);
assert_eq!(stats.deletions, 2);
}
}