use crate::{Time, FileType, Mode, Checksum, Device};
use pathstr::PathStr;
use std::fmt::{self, Display};
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};
#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "kebab-case"))]
pub struct Comparisons
{
pub inner: Vec<Comparison>,
pub aliens: Option<Vec<PathStr>>,
pub missing: Option<Vec<PathStr>>
}
#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "kebab-case"))]
pub struct Comparison
{
pub name: PathStr,
pub mtime: Option<[Time; 2]>,
pub btime: Option<[Time; 2]>,
pub ctime: Option<[Time; 2]>,
pub nlink: Option<[libc::nlink_t; 2]>,
pub inode: Option<[libc::ino_t; 2]>,
pub user_name: Option<[String; 2]>,
pub group_name: Option<[String; 2]>,
pub user_id: Option<[libc::uid_t; 2]>,
pub group_id: Option<[libc::gid_t; 2]>,
pub kind: Option<[FileType; 2]>,
pub size: Option<[u64; 2]>,
pub mode: Option<[Mode; 2]>,
#[cfg(feature = "magic")]
pub mime_type: Option<[String; 2]>,
#[cfg(feature = "magic")]
pub mime_encoding: Option<[String; 2]>,
pub device: Option<[Device; 2]>,
pub special_device: Option<[Device; 2]>,
pub link_target: Option<[String; 2]>,
pub link_real: Option<[String; 2]>,
pub checksums: Option<[Vec<Checksum>; 2]>
}
impl Comparison
{
pub fn is_empty(&self) -> bool
{
#[cfg(feature = "magic")]
let mgc = self.mime_type.is_none() && self.mime_encoding.is_none();
#[cfg(not(feature = "magic"))]
let mgc = true;
mgc
&& self.mtime.is_none()
&& self.btime.is_none()
&& self.ctime.is_none()
&& self.nlink.is_none()
&& self.inode.is_none()
&& self.user_name.is_none()
&& self.group_name.is_none()
&& self.user_id.is_none()
&& self.group_id.is_none()
&& self.kind.is_none()
&& self.size.is_none()
&& self.mode.is_none()
&& self.device.is_none()
&& self.special_device.is_none()
&& self.link_target.is_none()
&& self.link_real.is_none()
&& self.checksums.is_none()
}
}
impl Display for Comparison
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
if self.is_empty() { return Ok(()) }
writeln!(f, "PATH: \u{1B}[1m{}\u{1B}[m", self.name)?;
if let Some(x) = self.mtime
{
writeln!(f, "time.modify: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = self.btime
{
writeln!(f, "time.birth: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = self.ctime
{
writeln!(f, "time.change: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = self.nlink
{
writeln!(f, "nlink: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = self.inode
{
writeln!(f, "inode: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = &self.user_name
{
writeln!(f, "user.name: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = self.user_id
{
writeln!(f, "user.id: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = &self.group_name
{
writeln!(f, "group.name: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = self.group_id
{
writeln!(f, "group.id: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = self.kind
{
writeln!(f, "type: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = self.mode
{
writeln!(f, "mode: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = self.size
{
writeln!(f, "size: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = self.device
{
writeln!(f, "device: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = self.special_device
{
writeln!(f, "device.special: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = &self.link_target
{
writeln!(f, "link.target: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(x) = &self.link_real
{
writeln!(f, "link.real: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
#[cfg(feature = "magic")]
if let Some(x) = &self.mime_type
{
writeln!(f, "mime.type: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
#[cfg(feature = "magic")]
if let Some(x) = &self.mime_encoding
{
writeln!(f, "mime.encoding: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", x[0], x[1])?;
}
if let Some(cks) = &self.checksums
{
for n in 0..cks[0].len()
{
let a = &cks[0][n];
let b = &cks[1][n];
writeln!(f, "hash.{}: \u{1B}[32m{}\u{1B}[m != \u{1B}[31m{}\u{1B}[m", a.algo, a.hash, b.hash)?;
}
}
Ok(())
}
}
impl Display for Comparisons
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
if let Some(missing) = &self.missing
&& !missing.is_empty()
{
writeln!(f, "Files \u{1B}[1;3mexpected\u{1B}[m but not present:")?;
for miss in missing.iter()
{
writeln!(f, "- \u{1B}[31m{miss}\u{1B}[m")?;
}
writeln!(f, "")?;
}
if let Some(aliens) = &self.aliens
&& !aliens.is_empty()
{
writeln!(f, "Files \u{1B}[1;3mpresent\u{1B}[m but not expected:")?;
for alien in aliens.iter()
{
writeln!(f, "- \u{1B}[33m{alien}\u{1B}[m")?;
}
writeln!(f, "")?;
}
if !self.inner.is_empty()
{
self.inner.iter().try_for_each(|e| writeln!(f, "{e}"))?;
writeln!(f, "\u{1B}[33;1mwarn\u{1B}[m: {} differences found in expected files", self.inner.len())?;
}
else
{
writeln!(f, "\u{1B}[32;1mok\u{1B}[m: 0 differences found in expected files")?;
}
Ok(())
}
}