parallel_disk_usage/hardlink/hardlink_list/
reflection.rs1use super::{HardlinkList, Value};
2use crate::{hardlink::LinkPathListReflection, inode::InodeNumber};
3use dashmap::DashMap;
4use derive_more::{Display, Error, Into, IntoIterator};
5use into_sorted::IntoSortedUnstable;
6use pipe_trait::Pipe;
7
8#[cfg(feature = "json")]
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, PartialEq, Eq, Into, IntoIterator)]
24#[cfg_attr(feature = "json", derive(Deserialize, Serialize))]
25pub struct Reflection<Size>(Vec<ReflectionEntry<Size>>);
26
27impl<Size> Reflection<Size> {
28 #[inline]
30 pub fn len(&self) -> usize {
31 self.0.len()
32 }
33
34 #[inline]
36 pub fn is_empty(&self) -> bool {
37 self.0.is_empty()
38 }
39
40 #[inline]
42 pub fn iter(&self) -> impl Iterator<Item = &ReflectionEntry<Size>> + Clone {
43 self.0.iter()
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49#[cfg_attr(feature = "json", derive(Deserialize, Serialize))]
50pub struct ReflectionEntry<Size> {
51 pub ino: InodeNumber,
53 pub size: Size,
55 pub links: u64,
57 pub paths: LinkPathListReflection,
59}
60
61impl<Size> ReflectionEntry<Size> {
62 #[inline]
64 fn new(ino: InodeNumber, Value { size, links, paths }: Value<Size>) -> Self {
65 let paths = paths.into();
66 ReflectionEntry {
67 ino,
68 size,
69 links,
70 paths,
71 }
72 }
73
74 #[inline]
76 fn dissolve(self) -> (InodeNumber, Value<Size>) {
77 let ReflectionEntry {
78 ino,
79 size,
80 links,
81 paths,
82 } = self;
83 let paths = paths.into();
84 (ino, Value { size, links, paths })
85 }
86}
87
88impl<Size> From<Vec<ReflectionEntry<Size>>> for Reflection<Size> {
89 fn from(list: Vec<ReflectionEntry<Size>>) -> Self {
91 list.into_sorted_unstable_by_key(|entry| u64::from(entry.ino))
92 .pipe(Reflection)
93 }
94}
95
96impl<Size> From<HardlinkList<Size>> for Reflection<Size> {
97 fn from(HardlinkList(list): HardlinkList<Size>) -> Self {
98 list.into_iter()
99 .map(|(ino, value)| ReflectionEntry::new(ino, value))
100 .collect::<Vec<_>>()
101 .pipe(Reflection::from)
102 }
103}
104
105#[derive(Debug, Display, Error, Clone, Copy, PartialEq, Eq)]
108#[non_exhaustive]
109pub enum ConversionError {
110 #[display("Inode number {_0} is duplicated")]
112 DuplicatedInode(#[error(not(source))] InodeNumber),
113}
114
115impl<Size> TryFrom<Reflection<Size>> for HardlinkList<Size> {
116 type Error = ConversionError;
117 fn try_from(Reflection(entries): Reflection<Size>) -> Result<Self, Self::Error> {
118 let map = DashMap::with_capacity(entries.len());
119
120 for entry in entries {
121 let (ino, value) = entry.dissolve();
122 if map.insert(ino, value).is_some() {
123 return ino.pipe(ConversionError::DuplicatedInode).pipe(Err);
124 }
125 }
126
127 map.pipe(HardlinkList).pipe(Ok)
128 }
129}