oxihuman_core/
file_metadata.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct FileMetadata {
10 pub path: String,
11 pub size_bytes: u64,
12 pub mtime_secs: i64,
13 pub is_readonly: bool,
14 pub permissions: u32,
15}
16
17impl FileMetadata {
18 pub fn new(path: &str, size_bytes: u64, mtime_secs: i64) -> Self {
19 FileMetadata {
20 path: path.to_string(),
21 size_bytes,
22 mtime_secs,
23 is_readonly: false,
24 permissions: 0o644,
25 }
26 }
27
28 pub fn is_newer_than(&self, other: &FileMetadata) -> bool {
29 self.mtime_secs > other.mtime_secs
30 }
31
32 pub fn size_kb(&self) -> f64 {
33 self.size_bytes as f64 / 1024.0
34 }
35}
36
37pub struct MetadataStore {
39 entries: Vec<FileMetadata>,
40}
41
42impl MetadataStore {
43 pub fn new() -> Self {
44 MetadataStore {
45 entries: Vec::new(),
46 }
47 }
48
49 pub fn insert(&mut self, meta: FileMetadata) {
50 self.entries.push(meta);
51 }
52
53 pub fn get(&self, path: &str) -> Option<&FileMetadata> {
54 self.entries.iter().find(|m| m.path == path)
55 }
56
57 pub fn remove(&mut self, path: &str) {
58 self.entries.retain(|m| m.path != path);
59 }
60
61 pub fn count(&self) -> usize {
62 self.entries.len()
63 }
64}
65
66impl Default for MetadataStore {
67 fn default() -> Self {
68 Self::new()
69 }
70}
71
72pub fn read_metadata_stub(path: &str) -> FileMetadata {
74 FileMetadata::new(path, 0, 0)
75}
76
77pub fn largest_file(metas: &[FileMetadata]) -> Option<&FileMetadata> {
79 metas.iter().max_by_key(|m| m.size_bytes)
80}
81
82pub fn newest_file(metas: &[FileMetadata]) -> Option<&FileMetadata> {
84 metas.iter().max_by_key(|m| m.mtime_secs)
85}
86
87pub fn filter_large(metas: &[FileMetadata], min_bytes: u64) -> Vec<&FileMetadata> {
89 metas.iter().filter(|m| m.size_bytes >= min_bytes).collect()
90}
91
92pub fn total_size(metas: &[FileMetadata]) -> u64 {
94 metas.iter().map(|m| m.size_bytes).sum()
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn test_new_metadata() {
103 let m = FileMetadata::new("/tmp/x", 1024, 1000);
104 assert_eq!(m.size_bytes, 1024);
105 assert_eq!(m.mtime_secs, 1000);
106 }
107
108 #[test]
109 fn test_is_newer_than() {
110 let a = FileMetadata::new("/a", 0, 200);
111 let b = FileMetadata::new("/b", 0, 100);
112 assert!(a.is_newer_than(&b));
113 assert!(!b.is_newer_than(&a));
114 }
115
116 #[test]
117 fn test_size_kb() {
118 let m = FileMetadata::new("/f", 2048, 0);
119 assert!((m.size_kb() - 2.0).abs() < 0.01);
120 }
121
122 #[test]
123 fn test_store_insert_get() {
124 let mut s = MetadataStore::new();
125 s.insert(FileMetadata::new("/a", 10, 0));
126 assert!(s.get("/a").is_some());
127 assert!(s.get("/b").is_none());
128 }
129
130 #[test]
131 fn test_store_remove() {
132 let mut s = MetadataStore::new();
133 s.insert(FileMetadata::new("/x", 5, 0));
134 s.remove("/x");
135 assert_eq!(s.count(), 0);
136 }
137
138 #[test]
139 fn test_largest_file() {
140 let metas = vec![
141 FileMetadata::new("/a", 100, 0),
142 FileMetadata::new("/b", 500, 0),
143 FileMetadata::new("/c", 200, 0),
144 ];
145 let l = largest_file(&metas).expect("should succeed");
146 assert_eq!(l.path, "/b");
147 }
148
149 #[test]
150 fn test_newest_file() {
151 let metas = vec![
152 FileMetadata::new("/a", 0, 1000),
153 FileMetadata::new("/b", 0, 9000),
154 ];
155 let n = newest_file(&metas).expect("should succeed");
156 assert_eq!(n.path, "/b");
157 }
158
159 #[test]
160 fn test_filter_large() {
161 let metas = vec![
162 FileMetadata::new("/small", 50, 0),
163 FileMetadata::new("/big", 5000, 0),
164 ];
165 let big = filter_large(&metas, 100);
166 assert_eq!(big.len(), 1);
167 }
168
169 #[test]
170 fn test_total_size() {
171 let metas = vec![
172 FileMetadata::new("/a", 100, 0),
173 FileMetadata::new("/b", 200, 0),
174 ];
175 assert_eq!(total_size(&metas), 300);
176 }
177
178 #[test]
179 fn test_read_metadata_stub() {
180 let m = read_metadata_stub("/any/path");
181 assert_eq!(m.size_bytes, 0);
182 }
183}