parallel_disk_usage/hardlink/hardlink_list/
reflection.rs1use super::{HardlinkList, InodeKey, Value};
2use crate::{device::DeviceNumber, 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 dev: DeviceNumber,
55 pub size: Size,
57 pub links: u64,
59 pub paths: LinkPathListReflection,
61}
62
63impl<Size> ReflectionEntry<Size> {
64 #[inline]
66 fn new(InodeKey { ino, dev }: InodeKey, Value { size, links, paths }: Value<Size>) -> Self {
67 let paths = paths.into();
68 ReflectionEntry {
69 ino,
70 dev,
71 size,
72 links,
73 paths,
74 }
75 }
76
77 #[inline]
79 fn dissolve(self) -> (InodeKey, Value<Size>) {
80 let ReflectionEntry {
81 ino,
82 dev,
83 size,
84 links,
85 paths,
86 } = self;
87 let paths = paths.into();
88 (InodeKey { ino, dev }, Value { size, links, paths })
89 }
90
91 #[inline]
98 fn sorting_key(&self) -> (u64, u64) {
99 (u64::from(self.ino), u64::from(self.dev))
100 }
101}
102
103impl<Size> From<Vec<ReflectionEntry<Size>>> for Reflection<Size> {
104 fn from(list: Vec<ReflectionEntry<Size>>) -> Self {
106 list.into_sorted_unstable_by_key(ReflectionEntry::sorting_key)
107 .pipe(Reflection)
108 }
109}
110
111impl<Size> From<HardlinkList<Size>> for Reflection<Size> {
112 fn from(HardlinkList(list): HardlinkList<Size>) -> Self {
113 list.into_iter()
114 .map(|(key, value)| ReflectionEntry::new(key, value))
115 .collect::<Vec<_>>()
116 .pipe(Reflection::from)
117 }
118}
119
120#[derive(Debug, Display, Error, Clone, Copy, PartialEq, Eq)]
123#[non_exhaustive]
124pub enum ConversionError {
125 #[display("Inode {_0} on device {_1} is duplicated")]
127 DuplicatedInode(InodeNumber, DeviceNumber),
128}
129
130impl ConversionError {
131 fn duplicated_inode(InodeKey { ino, dev }: InodeKey) -> Self {
136 ConversionError::DuplicatedInode(ino, dev)
137 }
138}
139
140impl<Size> TryFrom<Reflection<Size>> for HardlinkList<Size> {
141 type Error = ConversionError;
142 fn try_from(Reflection(entries): Reflection<Size>) -> Result<Self, Self::Error> {
143 let map = DashMap::with_capacity(entries.len());
144
145 for entry in entries {
146 let (key, value) = entry.dissolve();
147 if map.insert(key, value).is_some() {
148 return key.pipe(ConversionError::duplicated_inode).pipe(Err);
149 }
150 }
151
152 map.pipe(HardlinkList).pipe(Ok)
153 }
154}