rust_filesearch/fs/
metadata.rs

1use crate::errors::Result;
2use crate::models::{Entry, EntryKind};
3use chrono::{DateTime, Utc};
4use std::fs;
5use std::path::Path;
6
7#[cfg(unix)]
8use std::os::unix::fs::PermissionsExt;
9
10/// Extract entry metadata from a path
11pub fn extract_entry(path: &Path, depth: usize) -> Result<Entry> {
12    let metadata = fs::symlink_metadata(path)?;
13    let mtime = extract_mtime(&metadata)?;
14    let kind = EntryKind::from_metadata(&metadata);
15
16    let size = if kind == EntryKind::Dir {
17        0 // Directory size computed separately if needed
18    } else {
19        metadata.len()
20    };
21
22    let name = path
23        .file_name()
24        .and_then(|n| n.to_str())
25        .unwrap_or("")
26        .to_string();
27
28    let perms = extract_permissions(&metadata);
29    let owner = extract_owner(path);
30
31    Ok(Entry {
32        path: path.to_path_buf(),
33        name,
34        size,
35        kind,
36        mtime,
37        perms,
38        owner,
39        depth,
40    })
41}
42
43/// Extract modification time from metadata
44fn extract_mtime(metadata: &fs::Metadata) -> Result<DateTime<Utc>> {
45    let mtime = metadata.modified()?;
46    Ok(DateTime::from(mtime))
47}
48
49/// Extract permission string (Unix-style)
50#[cfg(unix)]
51fn extract_permissions(metadata: &fs::Metadata) -> Option<String> {
52    let mode = metadata.permissions().mode();
53    Some(format_permissions(mode))
54}
55
56#[cfg(not(unix))]
57fn extract_permissions(_metadata: &fs::Metadata) -> Option<String> {
58    None
59}
60
61#[cfg(unix)]
62fn format_permissions(mode: u32) -> String {
63    let user = triplet(mode, 6);
64    let group = triplet(mode, 3);
65    let other = triplet(mode, 0);
66    format!("{}{}{}", user, group, other)
67}
68
69#[cfg(unix)]
70fn triplet(mode: u32, shift: u32) -> String {
71    let r = if mode & (0o4 << shift) != 0 { 'r' } else { '-' };
72    let w = if mode & (0o2 << shift) != 0 { 'w' } else { '-' };
73    let x = if mode & (0o1 << shift) != 0 { 'x' } else { '-' };
74    format!("{}{}{}", r, w, x)
75}
76
77/// Extract owner information (best effort)
78#[cfg(unix)]
79fn extract_owner(path: &Path) -> Option<String> {
80    use std::os::unix::fs::MetadataExt;
81
82    if let Ok(metadata) = fs::metadata(path) {
83        let uid = metadata.uid();
84        // For simplicity, just return UID; could use libc to get username
85        Some(format!("{}", uid))
86    } else {
87        None
88    }
89}
90
91#[cfg(not(unix))]
92fn extract_owner(_path: &Path) -> Option<String> {
93    None
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99    use std::fs::File;
100    use tempfile::tempdir;
101
102    #[test]
103    fn test_extract_entry_file() {
104        let dir = tempdir().unwrap();
105        let file_path = dir.path().join("test.txt");
106        File::create(&file_path).unwrap();
107
108        let entry = extract_entry(&file_path, 0).unwrap();
109        assert_eq!(entry.name, "test.txt");
110        assert_eq!(entry.kind, EntryKind::File);
111        assert_eq!(entry.depth, 0);
112    }
113
114    #[test]
115    fn test_extract_entry_dir() {
116        let dir = tempdir().unwrap();
117        let entry = extract_entry(dir.path(), 0).unwrap();
118        assert_eq!(entry.kind, EntryKind::Dir);
119    }
120
121    #[cfg(unix)]
122    #[test]
123    fn test_format_permissions() {
124        assert_eq!(format_permissions(0o755), "rwxr-xr-x");
125        assert_eq!(format_permissions(0o644), "rw-r--r--");
126        assert_eq!(format_permissions(0o777), "rwxrwxrwx");
127    }
128}