1use std::path::Path;
2use std::path::PathBuf;
3
4use iddqd::IdOrdItem;
5use iddqd::id_upcast;
6use owo_colors::Style;
7
8use crate::DiffTag;
9use crate::DisplayDiffOpts;
10use crate::PathInfo;
11
12#[derive(Debug, Clone)]
14pub struct DiffEntry<'a> {
15 pub(crate) relative: PathBuf,
16 pub(crate) tag: DiffTag,
17 pub(crate) deleted: Option<PathInfo<'a>>,
18 pub(crate) inserted: Option<PathInfo<'a>>,
19}
20
21impl<'a> IdOrdItem for DiffEntry<'a> {
22 type Key<'b>
23 = &'b Path
24 where
25 Self: 'b;
26
27 fn key(&self) -> Self::Key<'_> {
28 self.relative.as_path()
29 }
30
31 id_upcast! {}
32}
33
34impl<'a> DiffEntry<'a> {
35 pub fn relative(&self) -> &Path {
37 &self.relative
38 }
39
40 pub fn tag(&self) -> DiffTag {
42 self.tag
43 }
44
45 pub fn deleted(&self) -> Option<&PathInfo<'a>> {
47 self.deleted.as_ref()
48 }
49
50 pub fn inserted(&self) -> Option<&PathInfo<'a>> {
52 self.inserted.as_ref()
53 }
54
55 pub(crate) fn is_dir(&self) -> bool {
56 self.inserted
57 .as_ref()
58 .or(self.deleted.as_ref())
59 .map(|info| info.metadata.is_dir())
60 .unwrap_or(false)
61 }
62
63 pub(crate) fn format_path(&self) -> String {
64 let mut ret = self.relative.display().to_string();
65 if self.is_dir() {
66 ret.push('/');
67 }
68 ret
69 }
70
71 fn styled(&self, style: Style) -> owo_colors::Styled<String> {
72 style.style(format!("{} {}", self.tag.marker(), self.format_path()))
73 }
74
75 pub(crate) fn fmt_with(
76 &self,
77 f: &mut std::fmt::Formatter<'_>,
78 opts: &DisplayDiffOpts,
79 ) -> std::fmt::Result {
80 let style = if opts.color {
81 self.tag.style()
82 } else {
83 Style::new()
84 };
85
86 match self.tag {
87 DiffTag::Equal => {}
88 DiffTag::Delete => {
89 writeln!(f, "{}", self.styled(style))?;
90 }
91 DiffTag::Replace => {
92 if !self.is_dir() {
95 writeln!(f, "{}", self.styled(style))?;
96 }
97 }
98 DiffTag::Insert => {
99 writeln!(f, "{}", self.styled(style))?;
100 }
101 }
102 Ok(())
103 }
104
105 #[cfg(test)]
106 pub(crate) fn as_pair(&self) -> (&Path, DiffTag) {
107 (self.relative(), self.tag())
108 }
109}