use serde_json::Value;
use super::super::helpers::optional_string;
use super::semantic::normalize_token;
pub(super) fn change_path(change: &Value) -> Option<String> {
optional_string(change, "path").or_else(|| optional_string(change, "filePath"))
}
pub(super) fn summarize_change_counts(change: &Value) -> (i64, i64) {
let kind = optional_string(change, "kind")
.or_else(|| optional_string(change, "type"))
.or_else(|| optional_string(change, "status"))
.unwrap_or_default();
let diff = optional_string(change, "diff")
.or_else(|| optional_string(change, "content"))
.unwrap_or_default();
if looks_like_patch_text(&diff) {
let (_, _, add_lines, remove_lines) = summarize_diff_text(&diff);
return (add_lines, remove_lines);
}
match normalize_token(&kind).as_str() {
"add" | "added" => (count_content_lines(&diff), 0),
"delete" | "deleted" => (0, count_content_lines(&diff)),
_ => (0, 0),
}
}
pub(super) fn looks_like_patch_text(text: &str) -> bool {
let trimmed = text.trim();
trimmed.contains("*** Begin Patch")
|| trimmed.contains("diff --git ")
|| trimmed.contains("@@")
|| trimmed.contains("+++ ")
|| trimmed.contains("--- ")
}
pub(super) fn summarize_diff_text(diff: &str) -> (Option<String>, i64, i64, i64) {
if diff.contains("*** Begin Patch") {
return summarize_apply_patch_text(diff);
}
summarize_unified_diff_text(diff)
}
fn summarize_apply_patch_text(diff: &str) -> (Option<String>, i64, i64, i64) {
let mut primary_path = None;
let mut file_count = 0_i64;
let mut add_lines = 0_i64;
let mut remove_lines = 0_i64;
for line in diff.lines() {
if let Some(path) = line
.strip_prefix("*** Update File: ")
.or_else(|| line.strip_prefix("*** Add File: "))
.or_else(|| line.strip_prefix("*** Delete File: "))
{
file_count += 1;
if primary_path.is_none() {
primary_path = Some(path.trim().to_string());
}
continue;
}
if line.starts_with("+++")
|| line.starts_with("---")
|| line.starts_with("***")
|| line.starts_with("@@")
{
continue;
}
if line.starts_with('+') {
add_lines += 1;
} else if line.starts_with('-') {
remove_lines += 1;
}
}
(primary_path, file_count.max(1), add_lines, remove_lines)
}
fn summarize_unified_diff_text(diff: &str) -> (Option<String>, i64, i64, i64) {
let mut primary_path = None;
let mut file_count = 0_i64;
let mut add_lines = 0_i64;
let mut remove_lines = 0_i64;
for line in diff.lines() {
if let Some(path) = line
.strip_prefix("+++ b/")
.or_else(|| line.strip_prefix("+++ "))
{
if path != "/dev/null" {
file_count += 1;
if primary_path.is_none() {
primary_path = Some(path.trim().to_string());
}
}
continue;
}
if line.starts_with("diff --git ") {
if primary_path.is_none() {
primary_path = line
.split_whitespace()
.nth(2)
.map(|path| path.trim_start_matches("a/").to_string());
}
continue;
}
if line.starts_with("+++") || line.starts_with("---") {
continue;
}
if line.starts_with('+') {
add_lines += 1;
} else if line.starts_with('-') {
remove_lines += 1;
}
}
(primary_path, file_count.max(1), add_lines, remove_lines)
}
fn count_content_lines(text: &str) -> i64 {
text.lines().count() as i64
}