use super::{HardlinkList, InodeKey, Value};
use crate::{device::DeviceNumber, hardlink::LinkPathListReflection, inode::InodeNumber};
use dashmap::DashMap;
use derive_more::{Display, Error, Into, IntoIterator};
use into_sorted::IntoSortedUnstable;
use pipe_trait::Pipe;
#[cfg(feature = "json")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Into, IntoIterator)]
#[cfg_attr(feature = "json", derive(Deserialize, Serialize))]
pub struct Reflection<Size>(Vec<ReflectionEntry<Size>>);
impl<Size> Reflection<Size> {
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = &ReflectionEntry<Size>> + Clone {
self.0.iter()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "json", derive(Deserialize, Serialize))]
pub struct ReflectionEntry<Size> {
pub ino: InodeNumber,
pub dev: DeviceNumber,
pub size: Size,
pub links: u64,
pub paths: LinkPathListReflection,
}
impl<Size> ReflectionEntry<Size> {
#[inline]
fn new(InodeKey { ino, dev }: InodeKey, Value { size, links, paths }: Value<Size>) -> Self {
let paths = paths.into();
ReflectionEntry {
ino,
dev,
size,
links,
paths,
}
}
#[inline]
fn dissolve(self) -> (InodeKey, Value<Size>) {
let ReflectionEntry {
ino,
dev,
size,
links,
paths,
} = self;
let paths = paths.into();
(InodeKey { ino, dev }, Value { size, links, paths })
}
#[inline]
fn sorting_key(&self) -> (u64, u64) {
(u64::from(self.ino), u64::from(self.dev))
}
}
impl<Size> From<Vec<ReflectionEntry<Size>>> for Reflection<Size> {
fn from(list: Vec<ReflectionEntry<Size>>) -> Self {
list.into_sorted_unstable_by_key(ReflectionEntry::sorting_key)
.pipe(Reflection)
}
}
impl<Size> From<HardlinkList<Size>> for Reflection<Size> {
fn from(HardlinkList(list): HardlinkList<Size>) -> Self {
list.into_iter()
.map(|(key, value)| ReflectionEntry::new(key, value))
.collect::<Vec<_>>()
.pipe(Reflection::from)
}
}
#[derive(Debug, Display, Error, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ConversionError {
#[display("Inode {_0} on device {_1} is duplicated")]
DuplicatedInode(InodeNumber, DeviceNumber),
}
impl ConversionError {
fn duplicated_inode(InodeKey { ino, dev }: InodeKey) -> Self {
ConversionError::DuplicatedInode(ino, dev)
}
}
impl<Size> TryFrom<Reflection<Size>> for HardlinkList<Size> {
type Error = ConversionError;
fn try_from(Reflection(entries): Reflection<Size>) -> Result<Self, Self::Error> {
let map = DashMap::with_capacity(entries.len());
for entry in entries {
let (key, value) = entry.dissolve();
if map.insert(key, value).is_some() {
return key.pipe(ConversionError::duplicated_inode).pipe(Err);
}
}
map.pipe(HardlinkList).pipe(Ok)
}
}