file.rs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. use std::io::fs;
  2. use std::io;
  3. use colours::{Plain, Style, Black, Red, Green, Yellow, Blue, Purple, Cyan};
  4. use column::{Column, Permissions, FileName, FileSize, User, Group};
  5. use format::{formatBinaryBytes, formatDecimalBytes};
  6. use unix::{get_user_name, get_group_name};
  7. // Each file is definitely going to get `stat`ted at least once, if
  8. // only to determine what kind of file it is, so carry the `stat`
  9. // result around with the file for safe keeping.
  10. pub struct File<'a> {
  11. pub name: &'a str,
  12. pub path: &'a Path,
  13. pub stat: io::FileStat,
  14. }
  15. impl<'a> File<'a> {
  16. pub fn from_path(path: &'a Path) -> File<'a> {
  17. let filename: &str = path.filename_str().unwrap();
  18. // We have to use lstat here instad of file.stat(), as it
  19. // doesn't follow symbolic links. Otherwise, the stat() call
  20. // will fail if it encounters a link that's target is
  21. // non-existent.
  22. let stat: io::FileStat = match fs::lstat(path) {
  23. Ok(stat) => stat,
  24. Err(e) => fail!("Couldn't stat {}: {}", filename, e),
  25. };
  26. return File { path: path, stat: stat, name: filename };
  27. }
  28. pub fn ext(&self) -> Option<&'a str> {
  29. let re = regex!(r"\.(.+)$");
  30. re.captures(self.name).map(|caps| caps.at(1))
  31. }
  32. pub fn is_dotfile(&self) -> bool {
  33. self.name.starts_with(".")
  34. }
  35. pub fn display(&self, column: &Column) -> StrBuf {
  36. match *column {
  37. Permissions => self.permissions(),
  38. FileName => self.file_colour().paint(self.name.as_slice()),
  39. FileSize(si) => self.file_size(si),
  40. User => get_user_name(self.stat.unstable.uid as i32).unwrap_or(self.stat.unstable.uid.to_str()),
  41. Group => get_group_name(self.stat.unstable.gid as u32).unwrap_or(self.stat.unstable.gid.to_str()),
  42. }
  43. }
  44. fn file_size(&self, si: bool) -> StrBuf {
  45. let sizeStr = if si {
  46. formatBinaryBytes(self.stat.size)
  47. } else {
  48. formatDecimalBytes(self.stat.size)
  49. };
  50. return if self.stat.kind == io::TypeDirectory {
  51. Green.normal()
  52. } else {
  53. Green.bold()
  54. }.paint(sizeStr.as_slice());
  55. }
  56. fn type_char(&self) -> StrBuf {
  57. return match self.stat.kind {
  58. io::TypeFile => ".".to_strbuf(),
  59. io::TypeDirectory => Blue.paint("d"),
  60. io::TypeNamedPipe => Yellow.paint("|"),
  61. io::TypeBlockSpecial => Purple.paint("s"),
  62. io::TypeSymlink => Cyan.paint("l"),
  63. _ => "?".to_owned(),
  64. }
  65. }
  66. fn file_colour(&self) -> Style {
  67. if self.stat.kind == io::TypeDirectory {
  68. Blue.normal()
  69. } else if self.stat.perm.contains(io::UserExecute) {
  70. Green.normal()
  71. } else if self.name.ends_with("~") {
  72. Black.bold()
  73. } else {
  74. Plain
  75. }
  76. }
  77. fn permissions(&self) -> StrBuf {
  78. let bits = self.stat.perm;
  79. return format!("{}{}{}{}{}{}{}{}{}{}",
  80. self.type_char(),
  81. bit(bits, io::UserRead, "r", Yellow.bold()),
  82. bit(bits, io::UserWrite, "w", Red.bold()),
  83. bit(bits, io::UserExecute, "x", Green.bold().underline()),
  84. bit(bits, io::GroupRead, "r", Yellow.normal()),
  85. bit(bits, io::GroupWrite, "w", Red.normal()),
  86. bit(bits, io::GroupExecute, "x", Green.normal()),
  87. bit(bits, io::OtherRead, "r", Yellow.normal()),
  88. bit(bits, io::OtherWrite, "w", Red.normal()),
  89. bit(bits, io::OtherExecute, "x", Green.normal()),
  90. );
  91. }
  92. }
  93. fn bit(bits: io::FilePermission, bit: io::FilePermission, other: &'static str, style: Style) -> StrBuf {
  94. if bits.contains(bit) {
  95. style.paint(other.as_slice())
  96. } else {
  97. Black.bold().paint("-".as_slice())
  98. }
  99. }