11use super :: { iter:: Item as IterItem , reflection:: ReflectionEntry , HardlinkList , Reflection } ;
22use crate :: size;
33use derive_more:: { Add , AddAssign , Sum } ;
4- use std:: fmt:: { self , Display } ;
4+ use std:: {
5+ cmp:: Ordering ,
6+ fmt:: { self , Display } ,
7+ } ;
58
69#[ cfg( feature = "json" ) ]
710use serde:: { Deserialize , Serialize } ;
@@ -11,23 +14,34 @@ use serde::{Deserialize, Serialize};
1114#[ cfg_attr( feature = "json" , derive( Deserialize , Serialize ) ) ]
1215#[ non_exhaustive]
1316pub struct Summary < Size > {
14- /// Number of unique files , each with more than 1 links.
17+ /// Number of shared inodes , each with more than 1 links (i.e. `nlink > 1`) .
1518 pub inodes : usize ,
16- /// Total number of links of all the unique files as counted by [`Summary::inodes`].
17- pub links : usize ,
18- /// Total size of all the unique files.
19- pub size : Size ,
20- }
2119
22- impl < Size > Summary < Size > {
23- /// Create a new summary.
24- pub fn new ( inodes : usize , links : usize , size : Size ) -> Self {
25- Summary {
26- inodes,
27- links,
28- size,
29- }
30- }
20+ /// Number of [shared inodes](Self::inodes) that don't have links outside the measured tree.
21+ ///
22+ /// This number is expected to be less than or equal to [`Self::inodes`].
23+ pub owned_inodes : usize ,
24+
25+ /// Totality of the numbers of links of all [shared inodes](Self::inodes).
26+ pub all_links : u64 ,
27+
28+ /// Total number of links of [shared inodes](Self::inodes) that were detected within the measured tree.
29+ ///
30+ /// This number is expected to be less than or equal to [`Self::all_links`].
31+ pub detected_links : usize ,
32+
33+ /// Total number of links of [shared inodes](Self::inodes) that don't have links outside the measured tree.
34+ ///
35+ /// This number is expected to be less than or equal to [`Self::all_links`].
36+ pub owned_links : usize ,
37+
38+ /// Totality of the sizes of all [shared inodes](Self::inodes).
39+ pub shared_size : Size ,
40+
41+ /// Totality of the sizes of all [shared inodes](Self::inodes) that don't have links outside the measured tree.
42+ ///
43+ /// This number is expected to be less than or equal to [`Self::all_links`].
44+ pub owned_shared_size : Size ,
3145}
3246
3347/// Ability to summarize into a [`Summary`].
@@ -39,8 +53,10 @@ pub trait SummarizeHardlinks<Size>: Sized {
3953/// Summary of a single unique file.
4054#[ derive( Debug , Clone , Copy ) ]
4155pub struct SingleInodeSummary < Size > {
56+ /// Total number of all links to the file.
57+ links : u64 ,
4258 /// Number of detected links to the file.
43- links : usize ,
59+ paths : usize ,
4460 /// Size of the file.
4561 size : Size ,
4662}
@@ -54,13 +70,22 @@ where
5470 fn summarize_hardlinks ( self ) -> Summary < Size > {
5571 let mut summary = Summary :: default ( ) ;
5672 for item in self {
57- let SingleInodeSummary { links, size } = item. into ( ) ;
58- if links <= 1 {
59- continue ;
60- }
73+ let SingleInodeSummary { links, paths, size } = item. into ( ) ;
6174 summary. inodes += 1 ;
62- summary. links += links;
63- summary. size += size;
75+ summary. all_links += links;
76+ summary. detected_links += paths;
77+ summary. shared_size += size;
78+ match links. cmp ( & ( paths as u64 ) ) {
79+ Ordering :: Greater => { }
80+ Ordering :: Equal => {
81+ summary. owned_inodes += 1 ;
82+ summary. owned_links += paths; // `links` and `paths` are both fine, but `paths` doesn't require type cast
83+ summary. owned_shared_size += size;
84+ }
85+ Ordering :: Less => {
86+ panic ! ( "Impossible! Total of nlink ({links}) is less than detected paths ({paths}). Something must have went wrong!" ) ;
87+ }
88+ }
6489 }
6590 summary
6691 }
@@ -116,14 +141,42 @@ impl<Size: size::Size> Display for SummaryDisplay<'_, Size> {
116141 let SummaryDisplay { format, summary } = self ;
117142 let Summary {
118143 inodes,
119- links,
120- size,
144+ owned_inodes,
145+ all_links,
146+ detected_links,
147+ owned_links,
148+ shared_size,
149+ owned_shared_size,
121150 } = summary;
122- let size = size. display ( * format) ;
123- write ! (
124- f,
125- "Detected {links} hardlinks for {inodes} unique files (total: {size})"
126- )
151+
152+ let shared_size = shared_size. display ( * format) ;
153+ let owned_shared_size = owned_shared_size. display ( * format) ;
154+
155+ macro_rules! ln {
156+ ( $( $args: tt) * ) => {
157+ writeln!( f, $( $args) * )
158+ } ;
159+ }
160+
161+ write ! ( f, "Hardlinks detected! " ) ?;
162+ if owned_inodes == inodes {
163+ ln ! ( "No files have links outside this tree" ) ?;
164+ ln ! ( "* Number of shared inodes: {inodes}" ) ?;
165+ ln ! ( "* Total number of links: {all_links}" ) ?;
166+ ln ! ( "* Total shared size: {shared_size}" ) ?;
167+ } else if owned_inodes == & 0 {
168+ ln ! ( "All hardlinks within this tree have links without" ) ?;
169+ ln ! ( "* Number of shared inodes: {inodes}" ) ?;
170+ ln ! ( "* Total number of links: {all_links}, {detected_links} detected" ) ?;
171+ ln ! ( "* Total shared size: {shared_size}" ) ?;
172+ } else {
173+ ln ! ( "Some files have links outside this tree" ) ?;
174+ ln ! ( "* Number of shared inodes: {inodes} total, {owned_inodes} exclusive" ) ?;
175+ ln ! ( "* Total number of links: {all_links} total, {detected_links} detected, {owned_links} exclusive" ) ?;
176+ ln ! ( "* Total shared size: {shared_size} total, {owned_shared_size} exclusive" ) ?;
177+ }
178+
179+ Ok ( ( ) )
127180 }
128181}
129182
@@ -146,7 +199,8 @@ impl<Size: Copy> From<ReflectionEntry<Size>> for SingleInodeSummary<Size> {
146199impl < ' r , Size : Copy > From < & ' r ReflectionEntry < Size > > for SingleInodeSummary < Size > {
147200 fn from ( reflection : & ' r ReflectionEntry < Size > ) -> Self {
148201 SingleInodeSummary {
149- links : reflection. paths . len ( ) ,
202+ links : reflection. links ,
203+ paths : reflection. paths . len ( ) ,
150204 size : reflection. size ,
151205 }
152206 }
@@ -161,7 +215,8 @@ impl<'a, Size: Copy> From<IterItem<'a, Size>> for SingleInodeSummary<Size> {
161215impl < ' r , ' a , Size : Copy > From < & ' r IterItem < ' a , Size > > for SingleInodeSummary < Size > {
162216 fn from ( value : & ' r IterItem < ' a , Size > ) -> Self {
163217 SingleInodeSummary {
164- links : value. paths ( ) . len ( ) ,
218+ links : value. links ( ) ,
219+ paths : value. paths ( ) . len ( ) ,
165220 size : * value. size ( ) ,
166221 }
167222 }
0 commit comments