You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

161 lines
4.3 KiB

  1. use std::{
  2. fs::{metadata, Permissions},
  3. path::PathBuf,
  4. str::FromStr,
  5. };
  6. use chrono::{DateTime, Datelike, Local};
  7. use crate::error::ShellError;
  8. use super::{Builtin, BuiltinConfig};
  9. pub struct Ls;
  10. impl Builtin for Ls {
  11. fn execute(&mut self, _: &mut BuiltinConfig, _: Vec<String>) -> Result<(), ShellError> {
  12. let dir = std::env::current_dir().unwrap_or(PathBuf::from_str("/").unwrap());
  13. let entries = match std::fs::read_dir(dir) {
  14. Ok(e) => e,
  15. Err(e) => return Err(ShellError::ExecuteFailure(e.to_string())),
  16. };
  17. //for entry in entries.by_ref().into_iter() {}
  18. println!(
  19. "{} | dir | size | modified | accessed | created | readonly |",
  20. right_padding(" filename", 20)
  21. );
  22. for entry in entries {
  23. let Ok(entry) = entry else {
  24. eprintln!("Couldn't get directory entry");
  25. continue;
  26. };
  27. let metadata = match metadata(entry.path()) {
  28. Ok(m) => m,
  29. Err(_) => {
  30. continue;
  31. }
  32. };
  33. let file_name = entry.file_name().to_string_lossy().to_string();
  34. let modified: DateTime<Local> = match metadata.modified() {
  35. Ok(t) => DateTime::from(t),
  36. Err(_) => Local::now(),
  37. };
  38. let accessed: DateTime<Local> = match metadata.accessed() {
  39. Ok(t) => DateTime::from(t),
  40. Err(_) => Local::now(),
  41. };
  42. let created: DateTime<Local> = match metadata.created() {
  43. Ok(t) => DateTime::from(t),
  44. Err(_) => Local::now(),
  45. };
  46. let permissions = metadata.permissions();
  47. let mut file_type = "unknown";
  48. if metadata.file_type().is_dir() {
  49. file_type = "dir"
  50. } else if metadata.file_type().is_file() {
  51. file_type = "file"
  52. } else if metadata.file_type().is_symlink() {
  53. file_type = "link"
  54. }
  55. println!(
  56. "{}",
  57. format_line(
  58. 20,
  59. &file_name,
  60. file_type,
  61. metadata.len(),
  62. modified,
  63. accessed,
  64. created,
  65. permissions
  66. )
  67. );
  68. }
  69. Ok(())
  70. }
  71. }
  72. fn format_filesize(filesize: u64) -> String {
  73. if filesize > 9_999 && filesize <= 999_999 {
  74. format!("{}KB", filesize / 1_000)
  75. } else if filesize > 999_999 && filesize <= 999_999_999 {
  76. format!("{}MB", filesize / 1_000_000)
  77. } else if filesize > 999_999_999 {
  78. format!("{}GB", filesize / 1_000_000_000)
  79. } else {
  80. format!("{}B", filesize)
  81. }
  82. }
  83. fn format_date(date: DateTime<Local>) -> String {
  84. let now: DateTime<Local> = Local::now();
  85. if date.day() != now.day() || date.month() != now.month() || date.year() != now.year() {
  86. date.format("%F").to_string()
  87. } else {
  88. date.format("%T").to_string()
  89. }
  90. }
  91. fn format_line(
  92. max_name_len: usize,
  93. file_name: &str,
  94. file_type: &str,
  95. file_size: u64,
  96. modified: DateTime<Local>,
  97. accessed: DateTime<Local>,
  98. created: DateTime<Local>,
  99. permissions: Permissions,
  100. ) -> String {
  101. format!(
  102. "{} | {:4} | {:6} | {:<10} | {:<10} | {:<10} | {:<5} |",
  103. right_padding(file_name, max_name_len),
  104. file_type,
  105. format_filesize(file_size),
  106. format_date(modified),
  107. format_date(accessed),
  108. format_date(created),
  109. permissions.readonly().to_string(),
  110. )
  111. }
  112. fn right_padding(s: &str, max: usize) -> String {
  113. let mut tmp = String::from_str(s).unwrap();
  114. for _ in tmp.len()..max {
  115. tmp.push(' ');
  116. }
  117. tmp
  118. }
  119. #[cfg(test)]
  120. mod tests {
  121. use super::*;
  122. #[test]
  123. fn test_format_filesize_bytes() {
  124. assert_eq!(format_filesize(6969), "6969B");
  125. }
  126. #[test]
  127. fn test_format_filesize_kilobytes() {
  128. assert_eq!(format_filesize(69420), "69KB");
  129. }
  130. #[test]
  131. fn test_format_filesize_megabytes() {
  132. assert_eq!(format_filesize(69420420), "69MB");
  133. }
  134. #[test]
  135. fn test_format_filesize_gigabytes() {
  136. assert_eq!(format_filesize(69420420420), "69GB");
  137. }
  138. }