Skip to main content

oxihuman_core/
file_metadata.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! File metadata (size, mtime) reader stub.
6
7/// File metadata record.
8#[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
37/// Metadata store stub.
38pub 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
72/// Read metadata stub (returns synthetic entry).
73pub fn read_metadata_stub(path: &str) -> FileMetadata {
74    FileMetadata::new(path, 0, 0)
75}
76
77/// Find the largest file in a list.
78pub fn largest_file(metas: &[FileMetadata]) -> Option<&FileMetadata> {
79    metas.iter().max_by_key(|m| m.size_bytes)
80}
81
82/// Find the most recently modified file.
83pub fn newest_file(metas: &[FileMetadata]) -> Option<&FileMetadata> {
84    metas.iter().max_by_key(|m| m.mtime_secs)
85}
86
87/// Filter files larger than `min_bytes`.
88pub fn filter_large(metas: &[FileMetadata], min_bytes: u64) -> Vec<&FileMetadata> {
89    metas.iter().filter(|m| m.size_bytes >= min_bytes).collect()
90}
91
92/// Total size across all entries.
93pub 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}