rust_filesearch/fs/
metadata.rs1use 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
10pub 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 } 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
43fn extract_mtime(metadata: &fs::Metadata) -> Result<DateTime<Utc>> {
45 let mtime = metadata.modified()?;
46 Ok(DateTime::from(mtime))
47}
48
49#[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#[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 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}