fork(1) download
  1. extern crate chrono;
  2.  
  3. use std::fs;
  4. use std::fs::{File};
  5. use std::fs::Metadata;
  6. use std::collections::HashMap;
  7. use std::io::Error;
  8. use std::os::unix::fs::PermissionsExt;
  9. use std::{mem, ptr};
  10. use std::fmt::{Display,Formatter};
  11. use chrono::offset::Utc;
  12. use chrono::DateTime;
  13. use std::os::unix::fs::MetadataExt;
  14. use users::{Users,Groups,UsersCache};
  15.  
  16. static PERMS : [char;5] = ['d', '-', 'r', 'w', 'x'];
  17.  
  18. struct FileStats {
  19. is_dir : bool,
  20. permissions : String,
  21. user : String,
  22. group : String,
  23. timestamp : String,
  24. size : u64,
  25. }
  26.  
  27. impl Display for FileStats {
  28. fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
  29. write!(f, "{}\t{}\t{}\t{}\t{}", &self.permissions, &self.user, &self.group, bytes_to_string(self.size), &self.timestamp)
  30. }
  31. }
  32.  
  33. impl FileStats {
  34. fn new(is_dir : bool, permissions : String, user : String, group: String, timestamp : String, size : u64) -> FileStats {
  35. FileStats {
  36. is_dir, permissions, user, group, timestamp, size
  37. }
  38. }
  39. }
  40.  
  41. fn bytes_to_string(size: u64) -> String {
  42. let mut size_str = String::new();
  43. if size >= 1099511627776 {
  44. size_str.push_str(&bytes_to_string_with_factor(size, 1099511627776));
  45. size_str.push_str(" TB");
  46. } else if size >= 1073741824 {
  47. size_str.push_str(&bytes_to_string_with_factor(size, 1073741824));
  48. size_str.push_str(" GB");
  49. } else if size >= 1048576 {
  50. size_str.push_str(&bytes_to_string_with_factor(size, 1048576));
  51. size_str.push_str(" MB");
  52. } else if size >= 1024 {
  53. size_str.push_str(&bytes_to_string_with_factor(size, 1024));
  54. size_str.push_str(" KB");
  55. } else {
  56. size_str.push_str(&format!("{}", size));
  57. }
  58. if size_str.len() <= 7 {
  59. size_str.push('\t');
  60. }
  61. size_str
  62. }
  63.  
  64. fn bytes_to_string_with_factor(size: u64, factor: u64) -> String {
  65. format!("{:.2}", (size as f64 / factor as f64))
  66. }
  67.  
  68. fn main() {
  69. let args : Vec<String> = std::env::args().collect();
  70. let default_file = String::from(".");
  71. let path = args.get(1).unwrap_or_else(|| {
  72. &default_file
  73. });
  74. let mut map : HashMap<String, FileStats> = HashMap::new();
  75. let cache = UsersCache::new();
  76. read_file(&mut map, path, &cache, false);
  77. print_results(&map);
  78. }
  79.  
  80. fn read_dir(map : &mut HashMap<String, FileStats>, path : &str, cache: &UsersCache) {
  81. for p in fs::read_dir(path).expect(&format!("Failed to read path '{}'", path)) {
  82. let sub_path = p.expect("Failed to read file").path().display().to_string();
  83. read_file(map, &sub_path, cache, true);
  84. }
  85. }
  86.  
  87. fn read_file(map : &mut HashMap<String, FileStats>, path : &str, cache : &UsersCache, no_recursion : bool) {
  88. let result = File::open(path);
  89. match result {
  90. Ok(file) => {
  91. let meta = file.metadata().expect(&format!("Failed to read '{}' metadata", path));
  92. let stats = create_file_stats(&meta, cache);
  93. let stats = stats.expect(&format!("Failed to create file stats for '{}'", path));
  94. let mut relative_path = String::new();
  95. if !path.starts_with(".") && !path.starts_with("/") {
  96. relative_path.push_str("./");
  97. };
  98. let is_dir = meta.is_dir();
  99. relative_path.push_str(path);
  100. if is_dir && !path.ends_with("/") {
  101. relative_path.push('/');
  102. }
  103. map.insert(relative_path, stats);
  104. if is_dir && !no_recursion {
  105. read_dir(map, path, cache);
  106. }
  107. },
  108. Err(_) => println!("Failed to stat '{}'", path),
  109. }
  110. }
  111.  
  112. fn print_results(map: &HashMap<String, FileStats>) {
  113. let mut keys : Vec<String> = Vec::new();
  114. for key in map.keys() {
  115. keys.push(key.to_string());
  116. }
  117. keys.sort_unstable();
  118. for path in keys.iter() {
  119. let file_stats = map.get(path).unwrap();
  120. if file_stats.is_dir {
  121. println!("{}\t\x1B[36m{}\x1B[0m", file_stats, path);
  122. } else {
  123. println!("{}\t{}", file_stats, path);
  124. }
  125. }
  126. println!("{} files/directories", keys.len());
  127. }
  128.  
  129. fn create_file_stats(meta : &Metadata, cache: &UsersCache) -> Result<FileStats, Error> {
  130. let is_dir = meta.is_dir();
  131. let size = meta.len();
  132. let created = meta.created().expect("Failed to reads files creation date");
  133. let created: DateTime<Utc> = created.into();
  134. let created = format!("{}", created.format("%d/%m/%Y %T"));
  135. let permissions = meta.permissions();
  136. let mode = format!("{:#o}", permissions.mode());
  137. let mode = &mode[mode.len()-3..];
  138. let permissions = build_permissions(is_dir, &mode);
  139. let user = cache.get_user_by_uid(meta.uid()).unwrap();
  140. let group = cache.get_group_by_gid(meta.gid()).unwrap();
  141. let user = user.name().to_os_string().into_string().unwrap();
  142. let group = group.name().to_os_string().into_string().unwrap();
  143.  
  144. Ok(FileStats::new(
  145. is_dir,
  146. permissions,
  147. user,
  148. group,
  149. created,
  150. size)
  151. )
  152. }
  153.  
  154. fn build_permissions(is_dir : bool, mode : &str) -> String {
  155. let groups : Vec<char> = mode.chars().collect();
  156. let mut permissions : [char; 10] = unsafe {
  157. let mut array : [char; 10] = mem::uninitialized();
  158. for (_i,e) in array.iter_mut().enumerate() {
  159. ptr::write(e, '-');
  160. }
  161. array
  162. };
  163. match is_dir {
  164. true => permissions[0] = PERMS[0],
  165. _ => permissions[0] = PERMS[1],
  166. };
  167. let mut i : usize = 1;
  168. let mut j : usize = 0;
  169. while i < 8 {
  170. build_permission_group(&mut permissions, groups.get(j).unwrap(), i);
  171. i = i+3;
  172. j = j+1;
  173. }
  174. let result : String = permissions.iter().collect();
  175. result
  176. }
  177.  
  178. fn build_permission_group(permissions : &mut [char], mode : &char, index : usize) {
  179. if index > 9 {
  180. return;
  181. }
  182. if *mode == '0' || *mode == '1' || *mode == '2' {
  183. permissions[index] = '-';
  184. } else {
  185. permissions[index] = 'r';
  186. }
  187. if *mode == '0' || *mode == '1' || *mode == '4' || *mode == '5' {
  188. permissions[index+1] = '-';
  189. } else {
  190. permissions[index+1] = 'w';
  191. }
  192. if *mode == '0' || *mode == '2' || *mode == '4' || *mode == '6' {
  193. permissions[index+2] = '-';
  194. } else {
  195. permissions[index+2] = 'x';
  196. }
  197. }
Compilation error #stdin compilation error #stdout 0s 0KB
stdin
Standard input is empty
compilation info
error[E0463]: can't find crate for `chrono`
 --> prog.rs:1:1
  |
1 | extern crate chrono;
  | ^^^^^^^^^^^^^^^^^^^^ can't find crate

error: aborting due to previous error

stdout
Standard output is empty