1use crate::error::MyResult;
2use crate::fs::flags::FileFlags;
3#[cfg(windows)]
4use once_cell::unsync::Lazy;
5#[cfg(windows)]
6use std::collections::HashSet;
7#[cfg(windows)]
8use std::env;
9#[cfg(windows)]
10use std::ffi::OsStr;
11use std::fs;
12use std::path::Path;
13use std::time::SystemTime;
14#[cfg(unix)]
15use uzers::{gid_t, uid_t};
16
17#[derive(Clone)]
18pub struct Metadata {
19 pub file_flags: FileFlags,
20 pub file_mode: u32,
21 #[cfg(unix)]
22 pub owner_uid: uid_t,
23 #[cfg(unix)]
24 pub owner_gid: gid_t,
25 pub file_size: u64,
26 pub file_time: SystemTime,
27}
28
29impl Metadata {
30 pub fn from_path(path: &Path) -> MyResult<Self> {
31 let metadata = fs::symlink_metadata(path).map_err(|e| (e, path))?;
32 Self::create_metadata(metadata, path)
33 }
34}
35
36#[cfg(windows)]
37impl Metadata {
38 fn create_metadata(metadata: fs::Metadata, path: &Path) -> MyResult<Self> {
39 use std::os::windows::fs::MetadataExt;
40 let file_flags = FileFlags::from_type(metadata.file_type(), false);
41 let file_mode = Self::convert_mode(metadata.file_attributes(), path);
42 let file_size = metadata.file_size();
43 let file_time = metadata.modified().map_err(|e| (e, path))?;
44 let metadata = Self {
45 file_flags,
46 file_mode,
47 file_size,
48 file_time,
49 };
50 Ok(metadata)
51 }
52
53 fn convert_mode(attributes: u32, path: &Path) -> u32 {
54 let extensions = Lazy::new(|| {
55 Self::parse_extensions()
56 });
57 let mut octal = 0x04; if attributes & 0x01 == 0 {
59 octal |= 0x02; }
61 if let Some(ext) = path.extension().and_then(OsStr::to_str) {
62 let ext = ext.to_uppercase();
63 if extensions.contains(&ext) {
64 octal |= 0x01; }
66 }
67 octal + (octal << 3) + (octal << 6)
68 }
69
70 fn parse_extensions() -> HashSet<String> {
71 let mut extensions = HashSet::new();
72 if let Ok(variable) = env::var("PATH_EXT") {
73 for ext in variable.split(";") {
74 if let Some(ext) = ext.strip_prefix(".") {
75 extensions.insert(ext.to_uppercase());
76 }
77 }
78 }
79 extensions
80 }
81}
82
83#[cfg(unix)]
84impl Metadata {
85 fn create_metadata(metadata: fs::Metadata, path: &Path) -> MyResult<Self> {
86 use std::os::unix::fs::MetadataExt;
87 let file_flags = FileFlags::from_type(metadata.file_type(), false);
88 let file_mode = metadata.mode();
89 let owner_uid = metadata.uid();
90 let owner_gid = metadata.gid();
91 let file_size = metadata.size();
92 let file_time = metadata.modified().map_err(|e| (e, path))?;
93 let metadata = Self {
94 file_flags,
95 file_mode,
96 owner_uid,
97 owner_gid,
98 file_size,
99 file_time,
100 };
101 Ok(metadata)
102 }
103}
104
105impl Default for Metadata {
106 fn default() -> Self {
107 Self {
108 file_flags: FileFlags::Dir,
109 file_mode: 0,
110 #[cfg(unix)]
111 owner_uid: 0,
112 #[cfg(unix)]
113 owner_gid: 0,
114 file_size: 0,
115 file_time: SystemTime::UNIX_EPOCH,
116 }
117 }
118}
119
120#[cfg(test)]
121pub mod tests {
122 use crate::fs::flags::FileFlags;
123 use crate::fs::metadata::Metadata;
124 use chrono::{DateTime, TimeZone, Utc};
125 use std::time::SystemTime;
126
127 impl Metadata {
128 #[allow(unused_variables)]
129 pub fn from_fields(
130 file_type: char,
131 file_mode: u32,
132 owner_uid: u32, owner_gid: u32, file_size: u64,
135 file_year: i32,
136 file_month: u32,
137 file_day: u32,
138 ) -> Self {
139 let file_flags = FileFlags::from_char(file_type);
140 let file_time = create_time(file_year, file_month, file_day);
141 let file_time = SystemTime::from(file_time);
142 Self {
143 file_flags,
144 file_mode,
145 #[cfg(unix)]
146 owner_uid,
147 #[cfg(unix)]
148 owner_gid,
149 file_size,
150 file_time,
151 }
152 }
153 }
154
155 fn create_time(year: i32, month: u32, day: u32) -> DateTime<Utc> {
156 Utc.with_ymd_and_hms(year, month, day, 0, 0, 0).unwrap()
157 }
158}