file_name.rs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. use std::path::Path;
  2. use ansi_term::{ANSIString, Style};
  3. use fs::{File, FileTarget};
  4. use output::Colours;
  5. use output::escape;
  6. use output::cell::TextCellContents;
  7. pub struct FileName<'a, 'dir: 'a> {
  8. file: &'a File<'dir>,
  9. colours: &'a Colours,
  10. }
  11. impl<'a, 'dir> FileName<'a, 'dir> {
  12. pub fn new(file: &'a File<'dir>, colours: &'a Colours) -> FileName<'a, 'dir> {
  13. FileName {
  14. file: file,
  15. colours: colours,
  16. }
  17. }
  18. pub fn paint(&self, links: bool, classify: bool) -> TextCellContents {
  19. let mut bits = Vec::new();
  20. if self.file.dir.is_none() {
  21. if let Some(parent) = self.file.path.parent() {
  22. self.add_parent_bits(&mut bits, parent);
  23. }
  24. }
  25. if !self.file.name.is_empty() {
  26. for bit in self.coloured_file_name() {
  27. bits.push(bit);
  28. }
  29. }
  30. if links && self.file.is_link() {
  31. match self.file.link_target() {
  32. FileTarget::Ok(target) => {
  33. bits.push(Style::default().paint(" "));
  34. bits.push(self.colours.punctuation.paint("->"));
  35. bits.push(Style::default().paint(" "));
  36. if let Some(parent) = target.path.parent() {
  37. self.add_parent_bits(&mut bits, parent);
  38. }
  39. if !target.name.is_empty() {
  40. let target = FileName::new(&target, self.colours);
  41. for bit in target.coloured_file_name() {
  42. bits.push(bit);
  43. }
  44. }
  45. },
  46. FileTarget::Broken(broken_path) => {
  47. bits.push(Style::default().paint(" "));
  48. bits.push(self.colours.broken_arrow.paint("->"));
  49. bits.push(Style::default().paint(" "));
  50. escape(broken_path.display().to_string(), &mut bits, self.colours.broken_filename, self.colours.control_char.underline());
  51. },
  52. FileTarget::Err(_) => {
  53. // Do nothing -- the error gets displayed on the next line
  54. }
  55. }
  56. }
  57. else if classify {
  58. if let Some(class) = self.classify_char() {
  59. bits.push(Style::default().paint(class));
  60. }
  61. }
  62. bits.into()
  63. }
  64. /// Adds the bits of the parent path to the given bits vector.
  65. /// The path gets its characters escaped based on the colours.
  66. fn add_parent_bits(&self, bits: &mut Vec<ANSIString>, parent: &Path) {
  67. let coconut = parent.components().count();
  68. if coconut == 1 && parent.has_root() {
  69. bits.push(self.colours.symlink_path.paint("/"));
  70. }
  71. else if coconut >= 1 {
  72. escape(parent.to_string_lossy().to_string(), bits, self.colours.symlink_path, self.colours.control_char);
  73. bits.push(self.colours.symlink_path.paint("/"));
  74. }
  75. }
  76. /// The character to be displayed after a file when classifying is on, if
  77. /// the file’s type has one associated with it.
  78. fn classify_char(&self) -> Option<&'static str> {
  79. if self.file.is_executable_file() {
  80. Some("*")
  81. } else if self.file.is_directory() {
  82. Some("/")
  83. } else if self.file.is_pipe() {
  84. Some("|")
  85. } else if self.file.is_link() {
  86. Some("@")
  87. } else if self.file.is_socket() {
  88. Some("=")
  89. } else {
  90. None
  91. }
  92. }
  93. /// Returns at least one ANSI-highlighted string representing this file’s
  94. /// name using the given set of colours.
  95. ///
  96. /// Ordinarily, this will be just one string: the file’s complete name,
  97. /// coloured according to its file type. If the name contains control
  98. /// characters such as newlines or escapes, though, we can’t just print them
  99. /// to the screen directly, because then there’ll be newlines in weird places.
  100. ///
  101. /// So in that situation, those characters will be escaped and highlighted in
  102. /// a different colour.
  103. fn coloured_file_name<'unused>(&self) -> Vec<ANSIString<'unused>> {
  104. let file_style = self.style();
  105. let mut bits = Vec::new();
  106. escape(self.file.name.clone(), &mut bits, file_style, self.colours.control_char);
  107. bits
  108. }
  109. pub fn style(&self) -> Style {
  110. match self.file {
  111. f if f.is_directory() => self.colours.filetypes.directory,
  112. f if f.is_executable_file() => self.colours.filetypes.executable,
  113. f if f.is_link() => self.colours.filetypes.symlink,
  114. f if f.is_pipe() => self.colours.filetypes.pipe,
  115. f if f.is_char_device()
  116. | f.is_block_device() => self.colours.filetypes.device,
  117. f if f.is_socket() => self.colours.filetypes.socket,
  118. f if !f.is_file() => self.colours.filetypes.special,
  119. f if f.is_immediate() => self.colours.filetypes.immediate,
  120. f if f.is_image() => self.colours.filetypes.image,
  121. f if f.is_video() => self.colours.filetypes.video,
  122. f if f.is_music() => self.colours.filetypes.music,
  123. f if f.is_lossless() => self.colours.filetypes.lossless,
  124. f if f.is_crypto() => self.colours.filetypes.crypto,
  125. f if f.is_document() => self.colours.filetypes.document,
  126. f if f.is_compressed() => self.colours.filetypes.compressed,
  127. f if f.is_temp() => self.colours.filetypes.temp,
  128. f if f.is_compiled() => self.colours.filetypes.compiled,
  129. _ => self.colours.filetypes.normal,
  130. }
  131. }
  132. }