use super::{HardlinkList, Reflection, iter::Item as IterItem, reflection::ReflectionEntry};
use crate::size;
use derive_more::{Add, AddAssign, Sum};
use derive_setters::Setters;
use std::{
cmp::Ordering,
fmt::{self, Display},
};
#[cfg(feature = "json")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Setters, Clone, Copy, PartialEq, Eq, Add, AddAssign, Sum)]
#[cfg_attr(feature = "json", derive(Deserialize, Serialize))]
#[setters(prefix = "with_")]
#[non_exhaustive]
pub struct Summary<Size> {
pub inodes: usize,
pub exclusive_inodes: usize,
pub all_links: u64,
pub detected_links: usize,
pub exclusive_links: usize,
pub shared_size: Size,
pub exclusive_shared_size: Size,
}
pub trait SummarizeHardlinks<Size>: Sized {
type Summary;
fn summarize_hardlinks(self) -> Self::Summary;
}
#[derive(Debug, Clone, Copy)]
pub struct SingleInodeSummary<Size> {
links: u64,
paths: usize,
size: Size,
}
impl<Size, Iter> SummarizeHardlinks<Size> for Iter
where
Size: size::Size,
Iter: IntoIterator,
Iter::Item: SummarizeHardlinks<Size>,
<Iter::Item as SummarizeHardlinks<Size>>::Summary: Into<SingleInodeSummary<Size>>,
{
type Summary = Summary<Size>;
fn summarize_hardlinks(self) -> Self::Summary {
let mut summary = Summary::default();
for item in self {
let SingleInodeSummary { links, paths, size } = item.summarize_hardlinks().into();
summary.inodes += 1;
summary.all_links += links;
summary.detected_links += paths;
summary.shared_size += size;
match links.cmp(&(paths as u64)) {
Ordering::Greater => {}
Ordering::Equal => {
summary.exclusive_inodes += 1;
summary.exclusive_links += paths; summary.exclusive_shared_size += size;
}
Ordering::Less => {
panic!(
"Impossible! Total of nlink ({links}) is less than detected paths ({paths}). Something must have gone wrong!"
);
}
}
}
summary
}
}
impl<Size: size::Size> HardlinkList<Size> {
pub fn summarize(&self) -> Summary<Size> {
self.iter().summarize_hardlinks()
}
}
impl<Size: size::Size> SummarizeHardlinks<Size> for &HardlinkList<Size> {
type Summary = Summary<Size>;
fn summarize_hardlinks(self) -> Self::Summary {
self.summarize()
}
}
impl<Size: size::Size> Reflection<Size> {
pub fn summarize(&self) -> Summary<Size> {
self.iter().summarize_hardlinks()
}
}
impl<Size: size::Size> SummarizeHardlinks<Size> for &Reflection<Size> {
type Summary = Summary<Size>;
fn summarize_hardlinks(self) -> Self::Summary {
self.summarize()
}
}
#[derive(Debug, Clone, Copy)]
pub struct SummaryDisplay<'a, Size: size::Size> {
format: Size::DisplayFormat,
summary: &'a Summary<Size>,
}
impl<Size: size::Size> Display for SummaryDisplay<'_, Size> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let SummaryDisplay { format, summary } = self;
let Summary {
inodes,
exclusive_inodes,
all_links,
detected_links,
exclusive_links,
shared_size,
exclusive_shared_size,
} = summary;
let shared_size = shared_size.display(*format);
let exclusive_shared_size = exclusive_shared_size.display(*format);
macro_rules! ln {
($($args:tt)*) => {
writeln!(f, $($args)*)
};
}
if inodes == &0 {
return ln!("There are no hardlinks.");
}
write!(f, "Hardlinks detected! ")?;
if exclusive_inodes == inodes {
ln!("No files have links outside this tree")?;
ln!("* Number of shared inodes: {inodes}")?;
ln!("* Total number of links: {all_links}")?;
ln!("* Total shared size: {shared_size}")?;
} else if exclusive_inodes == &0 {
ln!("All hardlinks within this tree have links without")?;
ln!("* Number of shared inodes: {inodes}")?;
ln!("* Total number of links: {all_links} total, {detected_links} detected")?;
ln!("* Total shared size: {shared_size}")?;
} else {
ln!("Some files have links outside this tree")?;
ln!("* Number of shared inodes: {inodes} total, {exclusive_inodes} exclusive")?;
ln!(
"* Total number of links: {all_links} total, {detected_links} detected, {exclusive_links} exclusive"
)?;
ln!("* Total shared size: {shared_size} total, {exclusive_shared_size} exclusive")?;
}
Ok(())
}
}
impl<Size: size::Size> Summary<Size> {
#[inline]
pub fn display(&self, format: Size::DisplayFormat) -> SummaryDisplay<'_, Size> {
SummaryDisplay {
format,
summary: self,
}
}
}
impl<Size: Copy> SummarizeHardlinks<Size> for ReflectionEntry<Size> {
type Summary = SingleInodeSummary<Size>;
fn summarize_hardlinks(self) -> Self::Summary {
(&self).summarize_hardlinks()
}
}
impl<Size: Copy> SummarizeHardlinks<Size> for &ReflectionEntry<Size> {
type Summary = SingleInodeSummary<Size>;
fn summarize_hardlinks(self) -> Self::Summary {
SingleInodeSummary {
links: self.links,
paths: self.paths.len(),
size: self.size,
}
}
}
impl<'a, Size: Copy> SummarizeHardlinks<Size> for IterItem<'a, Size> {
type Summary = SingleInodeSummary<Size>;
fn summarize_hardlinks(self) -> Self::Summary {
(&self).summarize_hardlinks()
}
}
impl<'a, Size: Copy> SummarizeHardlinks<Size> for &IterItem<'a, Size> {
type Summary = SingleInodeSummary<Size>;
fn summarize_hardlinks(self) -> Self::Summary {
SingleInodeSummary {
links: self.links(),
paths: self.paths().len(),
size: *self.size(),
}
}
}