use std::fmt;
use readahead_iterator::IntoReadahead;
use crate::*;
use DiffKind::*;
use Kind::*;
use MergedEntryKind::*;
#[derive(Debug)]
pub struct DiffOptions {
pub exclude: Exclude,
pub include_unchanged: bool,
}
impl Default for DiffOptions {
fn default() -> Self {
DiffOptions {
exclude: Exclude::nothing(),
include_unchanged: false,
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum DiffKind {
Unchanged,
New,
Deleted,
Changed,
}
impl DiffKind {
pub fn as_sigil(self) -> char {
match self {
Unchanged => '.',
New => '+',
Deleted => '-',
Changed => '*',
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct DiffEntry {
pub apath: Apath,
pub kind: DiffKind,
}
impl fmt::Display for DiffEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}\t{}", self.kind.as_sigil(), self.apath)
}
}
pub fn diff(
st: &StoredTree,
lt: &LiveTree,
options: &DiffOptions,
) -> Result<impl Iterator<Item = DiffEntry>> {
let readahead = 1000;
let include_unchanged: bool = options.include_unchanged;
let ait = st
.iter_entries(Apath::root(), options.exclude.clone())?
.readahead(readahead);
let bit = lt
.iter_entries(Apath::root(), options.exclude.clone())?
.filter(|le| le.kind() != Unknown)
.readahead(readahead);
Ok(MergeTrees::new(ait, bit)
.map(diff_merged_entry)
.filter(move |de: &DiffEntry| include_unchanged || de.kind != DiffKind::Unchanged))
}
fn diff_merged_entry<AE, BE>(me: merge::MergedEntry<AE, BE>) -> DiffEntry
where
AE: Entry,
BE: Entry,
{
let apath = me.apath;
match me.kind {
Both(ae, be) => diff_common_entry(ae, be, apath),
LeftOnly(_) => DiffEntry {
kind: Deleted,
apath,
},
RightOnly(_) => DiffEntry { kind: New, apath },
}
}
fn diff_common_entry<AE, BE>(ae: AE, be: BE, apath: Apath) -> DiffEntry
where
AE: Entry,
BE: Entry,
{
let ak = ae.kind();
if ak != be.kind()
|| (ak == File && (ae.mtime() != be.mtime() || ae.size() != be.size()))
|| (ak == Symlink && (ae.symlink_target() != be.symlink_target()))
{
DiffEntry {
kind: Changed,
apath,
}
} else {
DiffEntry {
kind: Unchanged,
apath,
}
}
}