use super::{HardlinkList, Value};
use crate::{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 size: Size,
pub links: u64,
pub paths: LinkPathListReflection,
}
impl<Size> ReflectionEntry<Size> {
#[inline]
fn new(ino: InodeNumber, Value { size, links, paths }: Value<Size>) -> Self {
let paths = paths.into();
ReflectionEntry {
ino,
size,
links,
paths,
}
}
#[inline]
fn dissolve(self) -> (InodeNumber, Value<Size>) {
let ReflectionEntry {
ino,
size,
links,
paths,
} = self;
let paths = paths.into();
(ino, Value { size, links, paths })
}
}
impl<Size> From<Vec<ReflectionEntry<Size>>> for Reflection<Size> {
fn from(list: Vec<ReflectionEntry<Size>>) -> Self {
list.into_sorted_unstable_by_key(|entry| u64::from(entry.ino))
.pipe(Reflection)
}
}
impl<Size> From<HardlinkList<Size>> for Reflection<Size> {
fn from(HardlinkList(list): HardlinkList<Size>) -> Self {
list.into_iter()
.map(|(ino, value)| ReflectionEntry::new(ino, value))
.collect::<Vec<_>>()
.pipe(Reflection::from)
}
}
#[derive(Debug, Display, Error, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ConversionError {
#[display("Inode number {_0} is duplicated")]
DuplicatedInode(#[error(not(source))] InodeNumber),
}
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 (ino, value) = entry.dissolve();
if map.insert(ino, value).is_some() {
return ino.pipe(ConversionError::DuplicatedInode).pipe(Err);
}
}
map.pipe(HardlinkList).pipe(Ok)
}
}