hadris_common/types/
extent.rs1use core::fmt;
6
7#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
22#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
23#[repr(C)]
24pub struct Extent {
25 pub sector: u32,
27 _padding: u32,
29 pub length: u64,
31}
32
33impl Extent {
34 #[inline]
36 pub const fn new(sector: u32, length: u64) -> Self {
37 Self {
38 sector,
39 _padding: 0,
40 length,
41 }
42 }
43
44 #[inline]
46 pub const fn end_sector(&self, sector_size: u32) -> u32 {
47 let sectors = self.length.div_ceil(sector_size as u64);
48 self.sector + sectors as u32
49 }
50
51 #[inline]
53 pub const fn sector_count(&self, sector_size: u32) -> u32 {
54 self.length.div_ceil(sector_size as u64) as u32
55 }
56
57 #[inline]
59 pub const fn overlaps(&self, other: &Extent, sector_size: u32) -> bool {
60 let self_end = self.end_sector(sector_size);
61 let other_end = other.end_sector(sector_size);
62 self.sector < other_end && other.sector < self_end
63 }
64
65 #[inline]
67 pub const fn is_empty(&self) -> bool {
68 self.length == 0
69 }
70}
71
72impl fmt::Display for Extent {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 write!(f, "sector {} ({} bytes)", self.sector, self.length)
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
80#[repr(u8)]
81pub enum FileType {
82 #[default]
84 RegularFile = 0,
85 Directory = 1,
87 Symlink = 2,
89}
90
91impl FileType {
92 #[inline]
94 pub const fn is_directory(&self) -> bool {
95 matches!(self, FileType::Directory)
96 }
97
98 #[inline]
100 pub const fn is_file(&self) -> bool {
101 matches!(self, FileType::RegularFile)
102 }
103
104 #[inline]
106 pub const fn is_symlink(&self) -> bool {
107 matches!(self, FileType::Symlink)
108 }
109}
110
111impl fmt::Display for FileType {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 match self {
114 FileType::RegularFile => write!(f, "file"),
115 FileType::Directory => write!(f, "directory"),
116 FileType::Symlink => write!(f, "symlink"),
117 }
118 }
119}
120
121#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
126#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
127#[repr(C)]
128pub struct Timestamps {
129 pub created: u64,
131 pub modified: u64,
133 pub accessed: u64,
135}
136
137impl Timestamps {
138 #[inline]
140 pub const fn new(time: u64) -> Self {
141 Self {
142 created: time,
143 modified: time,
144 accessed: time,
145 }
146 }
147
148 #[inline]
150 pub const fn with_times(created: u64, modified: u64, accessed: u64) -> Self {
151 Self {
152 created,
153 modified,
154 accessed,
155 }
156 }
157
158 #[inline]
160 pub const fn most_recent(&self) -> u64 {
161 let max = if self.created > self.modified {
162 self.created
163 } else {
164 self.modified
165 };
166 if self.accessed > max {
167 self.accessed
168 } else {
169 max
170 }
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn test_extent_basics() {
180 let extent = Extent::new(100, 4096);
181 assert_eq!(extent.sector, 100);
182 assert_eq!(extent.length, 4096);
183 assert!(!extent.is_empty());
184
185 assert_eq!(extent.sector_count(2048), 2);
187 assert_eq!(extent.end_sector(2048), 102);
188 }
189
190 #[test]
191 fn test_extent_overlap() {
192 let a = Extent::new(100, 4096); let b = Extent::new(101, 2048); let c = Extent::new(102, 2048); assert!(a.overlaps(&b, 2048));
197 assert!(!a.overlaps(&c, 2048));
198 assert!(!b.overlaps(&c, 2048));
199 }
200
201 #[test]
202 fn test_file_type() {
203 assert!(FileType::Directory.is_directory());
204 assert!(FileType::RegularFile.is_file());
205 assert!(FileType::Symlink.is_symlink());
206 }
207
208 #[test]
209 fn test_timestamps() {
210 let ts = Timestamps::new(1000);
211 assert_eq!(ts.created, 1000);
212 assert_eq!(ts.modified, 1000);
213 assert_eq!(ts.accessed, 1000);
214
215 let ts2 = Timestamps::with_times(100, 200, 150);
216 assert_eq!(ts2.most_recent(), 200);
217 }
218}