use crate::config::ConfigSet;
use crate::error::Result;
use crate::odb::Odb;
use crate::parallel::ThreadPolicy;
use super::detail::{DiffDetail, DiffTotals, FileDiff, LineStat};
use super::tree_content::{
tree_diff_stat_parallel, tree_diff_structured_parallel, TreeDiffStructured,
};
use super::{DiffEntry, DiffOptions, DiffStatus};
fn mode_display(mode: &str) -> Option<String> {
if mode == "000000" {
None
} else {
Some(mode.to_owned())
}
}
fn path_from_entry(entry: &DiffEntry) -> String {
entry
.new_path
.clone()
.or_else(|| entry.old_path.clone())
.unwrap_or_default()
}
fn old_path_from_entry(entry: &DiffEntry, path: &str) -> Option<String> {
entry
.old_path
.as_ref()
.filter(|op| op.as_str() != path)
.cloned()
}
fn file_diff_name_only(entry: &DiffEntry) -> FileDiff {
let path = path_from_entry(entry);
FileDiff {
path: path.clone(),
old_path: old_path_from_entry(entry, &path),
status: entry.status,
old_mode: mode_display(&entry.old_mode),
new_mode: mode_display(&entry.new_mode),
binary: false,
similarity: entry.rename_similarity(),
stat: None,
structured: None,
combined: None,
combined_status: None,
}
}
fn file_diff_from_structured(t: TreeDiffStructured) -> FileDiff {
let path = path_from_entry(&t.entry);
FileDiff {
path: path.clone(),
old_path: old_path_from_entry(&t.entry, &path),
status: t.entry.status,
old_mode: t.structured.old_mode.clone(),
new_mode: t.structured.new_mode.clone(),
binary: t.binary,
similarity: t.entry.rename_similarity(),
stat: None,
structured: Some(t.structured),
combined: None,
combined_status: None,
}
}
fn file_diff_from_stat(entry: DiffEntry, binary: bool, stat: Option<LineStat>) -> FileDiff {
let path = path_from_entry(&entry);
FileDiff {
path: path.clone(),
old_path: old_path_from_entry(&entry, &path),
status: entry.status,
old_mode: mode_display(&entry.old_mode),
new_mode: mode_display(&entry.new_mode),
binary,
similarity: entry.rename_similarity(),
stat,
structured: None,
combined: None,
combined_status: None,
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DiffFilesOutput {
pub files: Vec<FileDiff>,
pub totals: Option<DiffTotals>,
}
pub fn build_file_diffs(
odb: &Odb,
entries: Vec<DiffEntry>,
detail: DiffDetail,
diff_options: DiffOptions,
config: &ConfigSet,
thread_policy: &ThreadPolicy,
) -> Result<DiffFilesOutput> {
match detail {
DiffDetail::NameOnly | DiffDetail::NameStatus => {
let files: Vec<FileDiff> = entries.iter().map(file_diff_name_only).collect();
Ok(DiffFilesOutput {
files,
totals: None,
})
}
DiffDetail::Stat => {
let rows = tree_diff_stat_parallel(odb, entries, config, thread_policy, diff_options)?;
let files: Vec<FileDiff> = rows
.into_iter()
.map(|(entry, binary, stat)| file_diff_from_stat(entry, binary, stat))
.collect();
let totals = Some(DiffTotals::from_files(&files));
Ok(DiffFilesOutput { files, totals })
}
DiffDetail::Patch => {
let structured =
tree_diff_structured_parallel(odb, entries, config, thread_policy, diff_options)?;
let files: Vec<FileDiff> = structured
.into_iter()
.map(file_diff_from_structured)
.collect();
Ok(DiffFilesOutput {
files,
totals: None,
})
}
}
}
#[must_use]
pub fn name_status_line(entry: &DiffEntry) -> String {
let path = path_from_entry(entry);
match entry.status {
DiffStatus::Renamed => {
let score = entry.score.unwrap_or(100);
let old = entry.old_path.as_deref().unwrap_or("");
format!("R{score:03}\t{old}\t{path}")
}
DiffStatus::Copied => {
let score = entry.score.unwrap_or(100);
let old = entry.old_path.as_deref().unwrap_or("");
format!("C{score:03}\t{old}\t{path}")
}
DiffStatus::Added => format!("A\t{path}"),
DiffStatus::Deleted => format!("D\t{path}"),
DiffStatus::Modified => format!("M\t{path}"),
DiffStatus::TypeChanged => format!("T\t{path}"),
DiffStatus::Unmerged => format!("U\t{path}"),
}
}