pub mod iter;
pub mod reflection;
pub mod summary;
pub use iter::Iter;
pub use reflection::Reflection;
pub use summary::Summary;
pub use Reflection as HardlinkListReflection;
pub use Summary as SharedLinkSummary;
use crate::{device::DeviceNumber, hardlink::LinkPathList, inode::InodeNumber, size};
use dashmap::DashMap;
use derive_more::{Display, Error};
use smart_default::SmartDefault;
use std::fmt::Debug;
#[cfg(any(unix, test))]
use pipe_trait::Pipe;
#[cfg(any(unix, test))]
use std::path::Path;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
struct InodeKey {
ino: InodeNumber,
dev: DeviceNumber,
}
#[derive(Debug, Clone)]
struct Value<Size> {
size: Size,
links: u64,
paths: LinkPathList,
}
#[derive(Debug, SmartDefault, Clone)]
pub struct HardlinkList<Size>(
) to its size, number of links, and detected paths.
DashMap<InodeKey, Value<Size>>,
);
impl<Size> HardlinkList<Size> {
pub fn new() -> Self {
HardlinkList::default()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn into_reflection(self) -> Reflection<Size> {
self.into()
}
}
#[derive(Debug, Display, Error)]
#[cfg_attr(test, derive(PartialEq, Eq))]
#[display(bound(Size: Debug))]
#[display("Size for inode {ino} on device {dev} changed from {recorded:?} to {detected:?}")]
pub struct SizeConflictError<Size> {
pub ino: InodeNumber,
pub dev: DeviceNumber,
pub recorded: Size,
pub detected: Size,
}
#[derive(Debug, Display, Error)]
#[cfg_attr(test, derive(PartialEq, Eq))]
#[display(
"Number of links of inode {ino} on device {dev} changed from {recorded:?} to {detected:?}"
)]
pub struct NumberOfLinksConflictError {
pub ino: InodeNumber,
pub dev: DeviceNumber,
pub recorded: u64,
pub detected: u64,
}
#[derive(Debug, Display, Error)]
#[cfg_attr(test, derive(PartialEq, Eq))]
#[display(bound(Size: Debug))]
#[non_exhaustive]
pub enum AddError<Size> {
SizeConflict(SizeConflictError<Size>),
NumberOfLinksConflict(NumberOfLinksConflictError),
}
impl<Size> HardlinkList<Size>
where
Size: size::Size,
{
#[cfg(any(unix, test))] pub(crate) fn add(
&self,
ino: InodeNumber,
dev: DeviceNumber,
size: Size,
links: u64,
path: &Path,
) -> Result<(), AddError<Size>> {
let key = InodeKey { ino, dev };
let mut assertions = Ok(());
self.0
.entry(key)
.and_modify(|recorded| {
if size != recorded.size {
assertions = Err(AddError::SizeConflict(SizeConflictError {
ino,
dev,
recorded: recorded.size,
detected: size,
}));
return;
}
if links != recorded.links {
assertions = Err(AddError::NumberOfLinksConflict(
NumberOfLinksConflictError {
ino,
dev,
recorded: recorded.links,
detected: links,
},
));
return;
}
recorded.paths.add(path.to_path_buf());
})
.or_insert_with(|| {
let paths = path.to_path_buf().pipe(LinkPathList::single);
Value { size, links, paths }
});
assertions
}
}
#[cfg(test)]
mod test;