use core::fmt;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(C)]
pub struct Extent {
pub sector: u32,
_padding: u32,
pub length: u64,
}
impl Extent {
#[inline]
pub const fn new(sector: u32, length: u64) -> Self {
Self {
sector,
_padding: 0,
length,
}
}
#[inline]
pub const fn end_sector(&self, sector_size: u32) -> u32 {
let sectors = self.length.div_ceil(sector_size as u64);
self.sector + sectors as u32
}
#[inline]
pub const fn sector_count(&self, sector_size: u32) -> u32 {
self.length.div_ceil(sector_size as u64) as u32
}
#[inline]
pub const fn overlaps(&self, other: &Extent, sector_size: u32) -> bool {
let self_end = self.end_sector(sector_size);
let other_end = other.end_sector(sector_size);
self.sector < other_end && other.sector < self_end
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.length == 0
}
}
impl fmt::Display for Extent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "sector {} ({} bytes)", self.sector, self.length)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[repr(u8)]
pub enum FileType {
#[default]
RegularFile = 0,
Directory = 1,
Symlink = 2,
}
impl FileType {
#[inline]
pub const fn is_directory(&self) -> bool {
matches!(self, FileType::Directory)
}
#[inline]
pub const fn is_file(&self) -> bool {
matches!(self, FileType::RegularFile)
}
#[inline]
pub const fn is_symlink(&self) -> bool {
matches!(self, FileType::Symlink)
}
}
impl fmt::Display for FileType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FileType::RegularFile => write!(f, "file"),
FileType::Directory => write!(f, "directory"),
FileType::Symlink => write!(f, "symlink"),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(C)]
pub struct Timestamps {
pub created: u64,
pub modified: u64,
pub accessed: u64,
}
impl Timestamps {
#[inline]
pub const fn new(time: u64) -> Self {
Self {
created: time,
modified: time,
accessed: time,
}
}
#[inline]
pub const fn with_times(created: u64, modified: u64, accessed: u64) -> Self {
Self {
created,
modified,
accessed,
}
}
#[inline]
pub const fn most_recent(&self) -> u64 {
let max = if self.created > self.modified {
self.created
} else {
self.modified
};
if self.accessed > max {
self.accessed
} else {
max
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_extent_basics() {
let extent = Extent::new(100, 4096);
assert_eq!(extent.sector, 100);
assert_eq!(extent.length, 4096);
assert!(!extent.is_empty());
assert_eq!(extent.sector_count(2048), 2);
assert_eq!(extent.end_sector(2048), 102);
}
#[test]
fn test_extent_overlap() {
let a = Extent::new(100, 4096); let b = Extent::new(101, 2048); let c = Extent::new(102, 2048);
assert!(a.overlaps(&b, 2048));
assert!(!a.overlaps(&c, 2048));
assert!(!b.overlaps(&c, 2048));
}
#[test]
fn test_file_type() {
assert!(FileType::Directory.is_directory());
assert!(FileType::RegularFile.is_file());
assert!(FileType::Symlink.is_symlink());
}
#[test]
fn test_timestamps() {
let ts = Timestamps::new(1000);
assert_eq!(ts.created, 1000);
assert_eq!(ts.modified, 1000);
assert_eq!(ts.accessed, 1000);
let ts2 = Timestamps::with_times(100, 200, 150);
assert_eq!(ts2.most_recent(), 200);
}
}